Implementation for #2552 (sorry @mg98)

This commit is contained in:
Miroslav Stampar 2018-12-10 14:53:11 +01:00
parent e47c1aa61b
commit 2c95b65eac
7 changed files with 61 additions and 42 deletions

View File

@ -505,7 +505,7 @@ def start():
infoMsg = "skipping %s parameter '%s'" % (paramType, parameter) infoMsg = "skipping %s parameter '%s'" % (paramType, parameter)
logger.info(infoMsg) logger.info(infoMsg)
elif parameter == conf.csrfToken: elif re.search(conf.csrfToken, parameter, re.I):
testSqlInj = False testSqlInj = False
infoMsg = "skipping anti-CSRF token parameter '%s'" % parameter infoMsg = "skipping anti-CSRF token parameter '%s'" % parameter

View File

@ -600,7 +600,7 @@ def paramToDict(place, parameters=None):
if condition: if condition:
testableParameters[parameter] = "=".join(parts[1:]) 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) _ = 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): 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 " warnMsg = "it appears that you have provided tainted parameter values "

View File

@ -1560,6 +1560,23 @@ def _cleanupOptions():
except re.error: except re.error:
conf.testFilter = re.escape(conf.testFilter) 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: if conf.testSkip:
conf.testSkip = conf.testSkip.strip('*+') conf.testSkip = conf.testSkip.strip('*+')
conf.testSkip = re.sub(r"([^.])([*+])", r"\g<1>.\g<2>", conf.testSkip) conf.testSkip = re.sub(r"([^.])([*+])", r"\g<1>.\g<2>", conf.testSkip)

View File

@ -19,7 +19,7 @@ from lib.core.enums import DBMS_DIRECTORY_NAME
from lib.core.enums import OS from lib.core.enums import OS
# sqlmap version (<major>.<minor>.<month>.<monthly commit>) # sqlmap version (<major>.<minor>.<month>.<monthly commit>)
VERSION = "1.2.12.10" VERSION = "1.2.12.11"
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} 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) VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)

View File

@ -393,8 +393,8 @@ def _setRequestParams():
raise SqlmapGenericException(errMsg) raise SqlmapGenericException(errMsg)
if conf.csrfToken: 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, {}): 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 errMsg = "anti-CSRF token parameter '%s' not " % conf.csrfToken._original
errMsg += "found in provided GET, POST, Cookie or header values" errMsg += "found in provided GET, POST, Cookie or header values"
raise SqlmapGenericException(errMsg) raise SqlmapGenericException(errMsg)
else: else:

View File

@ -64,6 +64,7 @@ from lib.core.common import urlencode
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.datatype import AttribDict
from lib.core.decorators import stackedmethod from lib.core.decorators import stackedmethod
from lib.core.dicts import POST_HINT_CONTENT_TYPES from lib.core.dicts import POST_HINT_CONTENT_TYPES
from lib.core.enums import ADJUST_TIME_DELAY from lib.core.enums import ADJUST_TIME_DELAY
@ -960,75 +961,76 @@ class Connect(object):
if conf.csrfToken: if conf.csrfToken:
def _adjustParameter(paramString, parameter, newValue): def _adjustParameter(paramString, parameter, newValue):
retVal = paramString retVal = paramString
match = re.search(r"%s=[^&]*" % re.escape(parameter), paramString) match = re.search(r"%s=[^&]*" % re.escape(parameter), paramString, re.I)
if match: 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: else:
match = re.search(r"(%s[\"']:[\"'])([^\"']+)" % re.escape(parameter), paramString) match = re.search(r"(%s[\"']:[\"'])([^\"']+)" % re.escape(parameter), paramString, re.I)
if match: 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 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)) 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)<input[^>]+\bname=[\"']?%s\b[^>]*\bvalue=[\"']?(?P<result>[^>'\"]*)" % re.escape(conf.csrfToken), page or "") match = re.search(r"(?i)<input[^>]+\bname=[\"']?(?P<name>%s)\b[^>]*\bvalue=[\"']?(?P<value>[^>'\"]*)" % conf.csrfToken, page or "", re.I)
if not token: if not match:
token = extractRegexResult(r"(?i)<input[^>]+\bvalue=[\"']?(?P<result>[^>'\"]*)[\"']?[^>]*\bname=[\"']?%s\b" % re.escape(conf.csrfToken), page or "") match = re.search(r"(?i)<input[^>]+\bvalue=[\"']?(?P<value>[^>'\"]*)[\"']?[^>]*\bname=[\"']?(?P<name>%s)\b" % conf.csrfToken, page or "", re.I)
if not token: if not match:
match = re.search(r"%s[\"']:[\"']([^\"']+)" % re.escape(conf.csrfToken), page or "") match = re.search(r"(?P<name>%s)[\"']:[\"'](?P<value>[^\"']+)" % conf.csrfToken, page or "", re.I)
token = match.group(1) if match else None
if not token: if not match:
token = extractRegexResult(r"\b%s\s*[:=]\s*(?P<result>\w+)" % re.escape(conf.csrfToken), str(headers)) match = re.search(r"\b(?P<name>%s)\s*[:=]\s*(?P<value>\w+)" % conf.csrfToken, str(headers), re.I)
if not token: if not match:
token = extractRegexResult(r"\b%s\s*=\s*['\"]?(?P<result>[^;'\"]+)" % re.escape(conf.csrfToken), page or "") match = re.search(r"\b(?P<name>%s)\s*=\s*['\"]?(?P<value>[^;'\"]+)" % conf.csrfToken, page or "", re.I)
if token: if match:
match = re.search(r"String\.fromCharCode\(([\d+, ]+)\)", token) token.name, token.value = match.group("name"), match.group("value")
if match: match = re.search(r"String\.fromCharCode\(([\d+, ]+)\)", token.value)
token = "".join(chr(int(_)) for _ in match.group(1).replace(' ', "").split(',')) if match:
token.value = "".join(chr(int(_)) for _ in match.group(1).replace(' ', "").split(','))
if not token: if not token:
if conf.csrfUrl != conf.url and code == httplib.OK: if conf.csrfUrl != conf.url and code == httplib.OK:
if headers and "text/plain" in headers.get(HTTP_HEADER.CONTENT_TYPE, ""): if headers and "text/plain" in headers.get(HTTP_HEADER.CONTENT_TYPE, ""):
token = page 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: for _ in conf.cj:
if _.name == conf.csrfToken: if re.search(conf.csrfToken, _.name, re.I):
token = _.value token.name, token.value = _.name, _.value
if not any(conf.csrfToken in _ for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))): if not any(re.search(conf.csrfToken, ' '.join(_), re.I) for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))):
if 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: 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: else:
get = "%s=%s" % (conf.csrfToken, token) get = "%s=%s" % (token.name, token.value)
break break
if not token: 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: if not conf.csrfUrl:
errMsg += ". You can try to rerun by providing " errMsg += ". You can try to rerun by providing "
errMsg += "a valid value for option '--csrf-url'" errMsg += "a valid value for option '--csrf-url'"
raise SqlmapTokenException(errMsg) raise SqlmapTokenException(errMsg)
if token: if token:
token = token.strip("'\"") token.value = token.value.strip("'\"")
for place in (PLACE.GET, PLACE.POST): for place in (PLACE.GET, PLACE.POST):
if place in conf.parameters: if place in conf.parameters:
if place == PLACE.GET and get: 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: 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)): for i in xrange(len(conf.httpHeaders)):
if conf.httpHeaders[i][0].lower() == conf.csrfToken.lower(): if conf.httpHeaders[i][0].lower() == token.name.lower():
conf.httpHeaders[i] = (conf.httpHeaders[i][0], token) conf.httpHeaders[i] = (conf.httpHeaders[i][0], token.value)
if conf.rParam: if conf.rParam:
def _randomizeParameter(paramString, randomParameter): def _randomizeParameter(paramString, randomParameter):

View File

@ -24,12 +24,12 @@ b3e60ea4e18a65c48515d04aab28ff68 extra/sqlharvest/sqlharvest.py
c1bccc94522d3425a372dcd57f78418e extra/wafdetectify/wafdetectify.py c1bccc94522d3425a372dcd57f78418e extra/wafdetectify/wafdetectify.py
3459c562a6abb9b4bdcc36925f751f3e lib/controller/action.py 3459c562a6abb9b4bdcc36925f751f3e lib/controller/action.py
0f0feede9750be810d2b8a7ab159b7b0 lib/controller/checks.py 0f0feede9750be810d2b8a7ab159b7b0 lib/controller/checks.py
95cde6dc7efe2581a5936f0d4635cb3b lib/controller/controller.py 93f7eabf92f3da3d96cbd8266e30414d lib/controller/controller.py
988b548f6578adf9cec17afdeee8291c lib/controller/handler.py 988b548f6578adf9cec17afdeee8291c lib/controller/handler.py
1e5532ede194ac9c083891c2f02bca93 lib/controller/__init__.py 1e5532ede194ac9c083891c2f02bca93 lib/controller/__init__.py
e62309b22a59e60b270e62586f169441 lib/core/agent.py e62309b22a59e60b270e62586f169441 lib/core/agent.py
c347f085bd561adfa26d3a9512e5f3b9 lib/core/bigarray.py c347f085bd561adfa26d3a9512e5f3b9 lib/core/bigarray.py
9d040f1771efaab4fde8d09821a09f51 lib/core/common.py cf84ff84891b7f51620a457b0bff28c5 lib/core/common.py
0d082da16c388b3445e656e0760fb582 lib/core/convert.py 0d082da16c388b3445e656e0760fb582 lib/core/convert.py
9f87391b6a3395f7f50830b391264f27 lib/core/data.py 9f87391b6a3395f7f50830b391264f27 lib/core/data.py
72016ea5c994a711a262fd64572a0fcd lib/core/datatype.py 72016ea5c994a711a262fd64572a0fcd lib/core/datatype.py
@ -42,17 +42,17 @@ cada93357a7321655927fc9625b3bfec lib/core/exception.py
1e5532ede194ac9c083891c2f02bca93 lib/core/__init__.py 1e5532ede194ac9c083891c2f02bca93 lib/core/__init__.py
458a194764805cd8312c14ecd4be4d1e lib/core/log.py 458a194764805cd8312c14ecd4be4d1e lib/core/log.py
7d6edc552e08c30f4f4d49fa93b746f1 lib/core/optiondict.py 7d6edc552e08c30f4f4d49fa93b746f1 lib/core/optiondict.py
9bf3349158df05775eb41742d6402ad8 lib/core/option.py ecf9879967182e6402f3cab6840f5b75 lib/core/option.py
c8c386d644d57c659d74542f5f57f632 lib/core/patch.py c8c386d644d57c659d74542f5f57f632 lib/core/patch.py
6783160150b4711d02c56ee2beadffdb lib/core/profiling.py 6783160150b4711d02c56ee2beadffdb lib/core/profiling.py
6f654e1715571eff68a0f8af3d62dcf8 lib/core/readlineng.py 6f654e1715571eff68a0f8af3d62dcf8 lib/core/readlineng.py
0c3eef46bdbf87e29a3f95f90240d192 lib/core/replication.py 0c3eef46bdbf87e29a3f95f90240d192 lib/core/replication.py
a7db43859b61569b601b97f187dd31c5 lib/core/revision.py a7db43859b61569b601b97f187dd31c5 lib/core/revision.py
fcb74fcc9577523524659ec49e2e964b lib/core/session.py fcb74fcc9577523524659ec49e2e964b lib/core/session.py
7535ff33c85d9b886f9e631dc0158cb9 lib/core/settings.py e0b7d1e6e148bc962df6b59d35910b92 lib/core/settings.py
a971ce157d04de96ba6e710d3d38a9a8 lib/core/shell.py a971ce157d04de96ba6e710d3d38a9a8 lib/core/shell.py
a7edc9250d13af36ac0108f259859c19 lib/core/subprocessng.py a7edc9250d13af36ac0108f259859c19 lib/core/subprocessng.py
52642badbbca4c31a2fcdd754d67a983 lib/core/target.py d6da5998fea61068a80d9c671db1095f lib/core/target.py
72d499ca8d792e90a1ebfb2ad2341a51 lib/core/testing.py 72d499ca8d792e90a1ebfb2ad2341a51 lib/core/testing.py
cd0067d1798e45f422ce44b98baf57db lib/core/threads.py cd0067d1798e45f422ce44b98baf57db lib/core/threads.py
c40758411bb0bd68764d78e0bb72bd0f lib/core/unescaper.py c40758411bb0bd68764d78e0bb72bd0f lib/core/unescaper.py
@ -71,7 +71,7 @@ f6b5957bf2103c3999891e4f45180bce lib/parse/payloads.py
30eed3a92a04ed2c29770e1b10d39dc0 lib/request/basicauthhandler.py 30eed3a92a04ed2c29770e1b10d39dc0 lib/request/basicauthhandler.py
2b81435f5a7519298c15c724e3194a0d lib/request/basic.py 2b81435f5a7519298c15c724e3194a0d lib/request/basic.py
859b6ad583e0ffba154f17ee179b5b89 lib/request/comparison.py 859b6ad583e0ffba154f17ee179b5b89 lib/request/comparison.py
77b24c30b1a2163add76652998e74127 lib/request/connect.py 9eb0cc48e7e4779e44f1641aa7d39a4d lib/request/connect.py
dd4598675027fae99f2e2475b05986da lib/request/direct.py dd4598675027fae99f2e2475b05986da lib/request/direct.py
2044fce3f4ffa268fcfaaf63241b1e64 lib/request/dns.py 2044fce3f4ffa268fcfaaf63241b1e64 lib/request/dns.py
98535d0efca5551e712fcc4b34a3f772 lib/request/httpshandler.py 98535d0efca5551e712fcc4b34a3f772 lib/request/httpshandler.py