diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 9cdc3534d..bc071cbdb 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -505,7 +505,7 @@ def start(): infoMsg = "skipping %s parameter '%s'" % (paramType, parameter) logger.info(infoMsg) - elif parameter == conf.csrfToken: + elif re.search(conf.csrfToken, parameter, re.I): testSqlInj = False infoMsg = "skipping anti-CSRF token parameter '%s'" % parameter diff --git a/lib/core/common.py b/lib/core/common.py index 820a881f9..8c103814f 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -600,7 +600,7 @@ def paramToDict(place, parameters=None): if condition: testableParameters[parameter] = "=".join(parts[1:]) - if not conf.multipleTargets and not (conf.csrfToken and parameter == conf.csrfToken): + if not conf.multipleTargets and not (conf.csrfToken and re.search(conf.csrfToken, parameter, re.I)): _ = urldecode(testableParameters[parameter], convall=True) if (_.endswith("'") and _.count("'") == 1 or re.search(r'\A9{3,}', _) or re.search(r'\A-\d+\Z', _) or re.search(DUMMY_USER_INJECTION, _)) and not parameter.upper().startswith(GOOGLE_ANALYTICS_COOKIE_PREFIX): warnMsg = "it appears that you have provided tainted parameter values " diff --git a/lib/core/option.py b/lib/core/option.py index 499cefb0b..85acb8a32 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1560,6 +1560,23 @@ def _cleanupOptions(): except re.error: conf.testFilter = re.escape(conf.testFilter) + if conf.csrfToken: + original = conf.csrfToken + try: + re.compile(conf.csrfToken) + + if re.escape(conf.csrfToken) != conf.csrfToken: + message = "provided value for option '--csrf-token' is a regular expression? [Y/n] " + if not readInput(message, default='Y', boolean=True): + conf.csrfToken = re.escape(conf.csrfToken) + except re.error: + conf.csrfToken = re.escape(conf.csrfToken) + finally: + class _(unicode): + pass + conf.csrfToken = _(conf.csrfToken) + conf.csrfToken._original = original + if conf.testSkip: conf.testSkip = conf.testSkip.strip('*+') conf.testSkip = re.sub(r"([^.])([*+])", r"\g<1>.\g<2>", conf.testSkip) diff --git a/lib/core/settings.py b/lib/core/settings.py index ce145fec9..9edfaa5cc 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import DBMS_DIRECTORY_NAME from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.2.12.10" +VERSION = "1.2.12.11" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE) diff --git a/lib/core/target.py b/lib/core/target.py index a89c0c891..1b8232a32 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -393,8 +393,8 @@ def _setRequestParams(): raise SqlmapGenericException(errMsg) if conf.csrfToken: - if not any(conf.csrfToken in _ for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))) and not re.search(r"\b%s\b" % re.escape(conf.csrfToken), conf.data or "") and conf.csrfToken not in set(_[0].lower() for _ in conf.httpHeaders) and conf.csrfToken not in conf.paramDict.get(PLACE.COOKIE, {}): - errMsg = "anti-CSRF token parameter '%s' not " % conf.csrfToken + if not any(re.search(conf.csrfToken, ' '.join(_), re.I) for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))) and not re.search(r"\b%s\b" % re.escape(conf.csrfToken), conf.data or "") and conf.csrfToken not in set(_[0].lower() for _ in conf.httpHeaders) and conf.csrfToken not in conf.paramDict.get(PLACE.COOKIE, {}): + errMsg = "anti-CSRF token parameter '%s' not " % conf.csrfToken._original errMsg += "found in provided GET, POST, Cookie or header values" raise SqlmapGenericException(errMsg) else: diff --git a/lib/request/connect.py b/lib/request/connect.py index 8d46dbc7f..37ec0fc1e 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -64,6 +64,7 @@ from lib.core.common import urlencode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger +from lib.core.datatype import AttribDict from lib.core.decorators import stackedmethod from lib.core.dicts import POST_HINT_CONTENT_TYPES from lib.core.enums import ADJUST_TIME_DELAY @@ -960,75 +961,76 @@ class Connect(object): if conf.csrfToken: def _adjustParameter(paramString, parameter, newValue): retVal = paramString - match = re.search(r"%s=[^&]*" % re.escape(parameter), paramString) + match = re.search(r"%s=[^&]*" % re.escape(parameter), paramString, re.I) if match: - retVal = re.sub(re.escape(match.group(0)), ("%s=%s" % (parameter, newValue)).replace('\\', r'\\'), paramString) + retVal = re.sub(re.escape(match.group(0)), ("%s=%s" % (parameter, newValue)).replace('\\', r'\\'), paramString, flags=re.I) else: - match = re.search(r"(%s[\"']:[\"'])([^\"']+)" % re.escape(parameter), paramString) + match = re.search(r"(%s[\"']:[\"'])([^\"']+)" % re.escape(parameter), paramString, re.I) if match: - retVal = re.sub(re.escape(match.group(0)), "%s%s" % (match.group(1), newValue), paramString) + retVal = re.sub(re.escape(match.group(0)), "%s%s" % (match.group(1), newValue), paramString, flags=re.I) return retVal + token = AttribDict() page, headers, code = Connect.getPage(url=conf.csrfUrl or conf.url, data=conf.data if conf.csrfUrl == conf.url else None, method=conf.method if conf.csrfUrl == conf.url else None, cookie=conf.parameters.get(PLACE.COOKIE), direct=True, silent=True, ua=conf.parameters.get(PLACE.USER_AGENT), referer=conf.parameters.get(PLACE.REFERER), host=conf.parameters.get(PLACE.HOST)) - token = extractRegexResult(r"(?i)]+\bname=[\"']?%s\b[^>]*\bvalue=[\"']?(?P[^>'\"]*)" % re.escape(conf.csrfToken), page or "") + match = re.search(r"(?i)]+\bname=[\"']?(?P%s)\b[^>]*\bvalue=[\"']?(?P[^>'\"]*)" % conf.csrfToken, page or "", re.I) - if not token: - token = extractRegexResult(r"(?i)]+\bvalue=[\"']?(?P[^>'\"]*)[\"']?[^>]*\bname=[\"']?%s\b" % re.escape(conf.csrfToken), page or "") + if not match: + match = re.search(r"(?i)]+\bvalue=[\"']?(?P[^>'\"]*)[\"']?[^>]*\bname=[\"']?(?P%s)\b" % conf.csrfToken, page or "", re.I) - if not token: - match = re.search(r"%s[\"']:[\"']([^\"']+)" % re.escape(conf.csrfToken), page or "") - token = match.group(1) if match else None + if not match: + match = re.search(r"(?P%s)[\"']:[\"'](?P[^\"']+)" % conf.csrfToken, page or "", re.I) - if not token: - token = extractRegexResult(r"\b%s\s*[:=]\s*(?P\w+)" % re.escape(conf.csrfToken), str(headers)) + if not match: + match = re.search(r"\b(?P%s)\s*[:=]\s*(?P\w+)" % conf.csrfToken, str(headers), re.I) - if not token: - token = extractRegexResult(r"\b%s\s*=\s*['\"]?(?P[^;'\"]+)" % re.escape(conf.csrfToken), page or "") + if not match: + match = re.search(r"\b(?P%s)\s*=\s*['\"]?(?P[^;'\"]+)" % conf.csrfToken, page or "", re.I) - if token: - match = re.search(r"String\.fromCharCode\(([\d+, ]+)\)", token) + if match: + token.name, token.value = match.group("name"), match.group("value") - if match: - token = "".join(chr(int(_)) for _ in match.group(1).replace(' ', "").split(',')) + match = re.search(r"String\.fromCharCode\(([\d+, ]+)\)", token.value) + if match: + token.value = "".join(chr(int(_)) for _ in match.group(1).replace(' ', "").split(',')) if not token: if conf.csrfUrl != conf.url and code == httplib.OK: if headers and "text/plain" in headers.get(HTTP_HEADER.CONTENT_TYPE, ""): token = page - if not token and conf.cj and any(_.name == conf.csrfToken for _ in conf.cj): + if not token and conf.cj and any(re.search(conf.csrfToken, _.name, re.I) for _ in conf.cj): for _ in conf.cj: - if _.name == conf.csrfToken: - token = _.value - if not any(conf.csrfToken in _ for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))): + if re.search(conf.csrfToken, _.name, re.I): + token.name, token.value = _.name, _.value + if not any(re.search(conf.csrfToken, ' '.join(_), re.I) for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))): if post: - post = "%s%s%s=%s" % (post, conf.paramDel or DEFAULT_GET_POST_DELIMITER, conf.csrfToken, token) + post = "%s%s%s=%s" % (post, conf.paramDel or DEFAULT_GET_POST_DELIMITER, token.name, token.value) elif get: - get = "%s%s%s=%s" % (get, conf.paramDel or DEFAULT_GET_POST_DELIMITER, conf.csrfToken, token) + get = "%s%s%s=%s" % (get, conf.paramDel or DEFAULT_GET_POST_DELIMITER, token.name, token.value) else: - get = "%s=%s" % (conf.csrfToken, token) + get = "%s=%s" % (token.name, token.value) break if not token: - errMsg = "anti-CSRF token '%s' can't be found at '%s'" % (conf.csrfToken, conf.csrfUrl or conf.url) + errMsg = "anti-CSRF token '%s' can't be found at '%s'" % (conf.csrfToken._original, conf.csrfUrl or conf.url) if not conf.csrfUrl: errMsg += ". You can try to rerun by providing " errMsg += "a valid value for option '--csrf-url'" raise SqlmapTokenException(errMsg) if token: - token = token.strip("'\"") + token.value = token.value.strip("'\"") for place in (PLACE.GET, PLACE.POST): if place in conf.parameters: if place == PLACE.GET and get: - get = _adjustParameter(get, conf.csrfToken, token) + get = _adjustParameter(get, token.name, token.value) elif place == PLACE.POST and post: - post = _adjustParameter(post, conf.csrfToken, token) + post = _adjustParameter(post, token.name, token.value) for i in xrange(len(conf.httpHeaders)): - if conf.httpHeaders[i][0].lower() == conf.csrfToken.lower(): - conf.httpHeaders[i] = (conf.httpHeaders[i][0], token) + if conf.httpHeaders[i][0].lower() == token.name.lower(): + conf.httpHeaders[i] = (conf.httpHeaders[i][0], token.value) if conf.rParam: def _randomizeParameter(paramString, randomParameter): diff --git a/txt/checksum.md5 b/txt/checksum.md5 index a105fb39a..598fa2831 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -24,12 +24,12 @@ b3e60ea4e18a65c48515d04aab28ff68 extra/sqlharvest/sqlharvest.py c1bccc94522d3425a372dcd57f78418e extra/wafdetectify/wafdetectify.py 3459c562a6abb9b4bdcc36925f751f3e lib/controller/action.py 0f0feede9750be810d2b8a7ab159b7b0 lib/controller/checks.py -95cde6dc7efe2581a5936f0d4635cb3b lib/controller/controller.py +93f7eabf92f3da3d96cbd8266e30414d lib/controller/controller.py 988b548f6578adf9cec17afdeee8291c lib/controller/handler.py 1e5532ede194ac9c083891c2f02bca93 lib/controller/__init__.py e62309b22a59e60b270e62586f169441 lib/core/agent.py c347f085bd561adfa26d3a9512e5f3b9 lib/core/bigarray.py -9d040f1771efaab4fde8d09821a09f51 lib/core/common.py +cf84ff84891b7f51620a457b0bff28c5 lib/core/common.py 0d082da16c388b3445e656e0760fb582 lib/core/convert.py 9f87391b6a3395f7f50830b391264f27 lib/core/data.py 72016ea5c994a711a262fd64572a0fcd lib/core/datatype.py @@ -42,17 +42,17 @@ cada93357a7321655927fc9625b3bfec lib/core/exception.py 1e5532ede194ac9c083891c2f02bca93 lib/core/__init__.py 458a194764805cd8312c14ecd4be4d1e lib/core/log.py 7d6edc552e08c30f4f4d49fa93b746f1 lib/core/optiondict.py -9bf3349158df05775eb41742d6402ad8 lib/core/option.py +ecf9879967182e6402f3cab6840f5b75 lib/core/option.py c8c386d644d57c659d74542f5f57f632 lib/core/patch.py 6783160150b4711d02c56ee2beadffdb lib/core/profiling.py 6f654e1715571eff68a0f8af3d62dcf8 lib/core/readlineng.py 0c3eef46bdbf87e29a3f95f90240d192 lib/core/replication.py a7db43859b61569b601b97f187dd31c5 lib/core/revision.py fcb74fcc9577523524659ec49e2e964b lib/core/session.py -7535ff33c85d9b886f9e631dc0158cb9 lib/core/settings.py +e0b7d1e6e148bc962df6b59d35910b92 lib/core/settings.py a971ce157d04de96ba6e710d3d38a9a8 lib/core/shell.py a7edc9250d13af36ac0108f259859c19 lib/core/subprocessng.py -52642badbbca4c31a2fcdd754d67a983 lib/core/target.py +d6da5998fea61068a80d9c671db1095f lib/core/target.py 72d499ca8d792e90a1ebfb2ad2341a51 lib/core/testing.py cd0067d1798e45f422ce44b98baf57db lib/core/threads.py c40758411bb0bd68764d78e0bb72bd0f lib/core/unescaper.py @@ -71,7 +71,7 @@ f6b5957bf2103c3999891e4f45180bce lib/parse/payloads.py 30eed3a92a04ed2c29770e1b10d39dc0 lib/request/basicauthhandler.py 2b81435f5a7519298c15c724e3194a0d lib/request/basic.py 859b6ad583e0ffba154f17ee179b5b89 lib/request/comparison.py -77b24c30b1a2163add76652998e74127 lib/request/connect.py +9eb0cc48e7e4779e44f1641aa7d39a4d lib/request/connect.py dd4598675027fae99f2e2475b05986da lib/request/direct.py 2044fce3f4ffa268fcfaaf63241b1e64 lib/request/dns.py 98535d0efca5551e712fcc4b34a3f772 lib/request/httpshandler.py