diff --git a/lib/core/common.py b/lib/core/common.py index 5b1929a39..6de20fc14 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -104,6 +104,7 @@ from lib.core.log import LOGGER_HANDLER from lib.core.optiondict import optDict from lib.core.settings import BANNER from lib.core.settings import BOLD_PATTERNS +from lib.core.settings import BOUNDARY_BACKSLASH_MARKER from lib.core.settings import BOUNDED_INJECTION_MARKER from lib.core.settings import BRUTE_DOC_ROOT_PREFIXES from lib.core.settings import BRUTE_DOC_ROOT_SUFFIXES @@ -1384,6 +1385,38 @@ def banner(): dataToStdout(result, forceOutput=True) +def parseJson(content): + """ + This function parses POST_HINT.JSON and POST_HINT.JSON_LIKE content + + >>> parseJson("{'id':1}")["id"] == 1 + True + >>> parseJson('{"id":1}')["id"] == 1 + True + """ + + quote = None + retVal = None + + for regex in (r"'[^']+'\s*:", r'"[^"]+"\s*:'): + match = re.search(regex, content) + if match: + quote = match.group(0)[0] + + try: + if quote == '"': + retVal = json.loads(content) + elif quote == "'": + content = content.replace('"', '\\"') + content = content.replace("\\'", BOUNDARY_BACKSLASH_MARKER) + content = content.replace("'", '"') + content = content.replace(BOUNDARY_BACKSLASH_MARKER, "'") + retVal = json.loads(content) + except: + pass + + return retVal + def parsePasswordHash(password): """ In case of Microsoft SQL Server password hash value is expanded to its components diff --git a/lib/core/settings.py b/lib/core/settings.py index 488076d86..a7ac99f87 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -20,7 +20,7 @@ from thirdparty import six from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.6.3.6" +VERSION = "1.6.3.7" 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/request/connect.py b/lib/request/connect.py index 9f2a07f2f..d3b29ff5a 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -46,6 +46,7 @@ from lib.core.common import getSafeExString from lib.core.common import logHTTPTraffic from lib.core.common import openFile from lib.core.common import popValue +from lib.core.common import parseJson from lib.core.common import pushValue from lib.core.common import randomizeParameterValue from lib.core.common import randomInt @@ -1291,6 +1292,13 @@ class Connect(object): value = urldecode(value, convall=True, spaceplus=(item == post and kb.postSpaceToPlus)) variables[name] = value + if post and kb.postHint in (POST_HINT.JSON, POST_HINT.JSON_LIKE): + for name, value in (parseJson(post) or {}).items(): + if safeVariableNaming(name) != name: + conf.evalCode = re.sub(r"\b%s\b" % re.escape(name), safeVariableNaming(name), conf.evalCode) + name = safeVariableNaming(name) + variables[name] = value + if cookie: for part in cookie.split(conf.cookieDel or DEFAULT_COOKIE_DELIMITER): if '=' in part: @@ -1393,7 +1401,13 @@ class Connect(object): if not found: if post is not None: - post += "%s%s=%s" % (delimiter, name, value) + if kb.postHint in (POST_HINT.JSON, POST_HINT.JSON_LIKE): + match = re.search(r"['\"]", post) + if match: + quote = match.group(0) + post = re.sub(r"\}\Z", "%s%s}" % (',' if re.search(r"\w", post) else "", "%s%s%s:%s" % (quote, name, quote, value if value.isdigit() else "%s%s%s" % (quote, value, quote))), post) + else: + post += "%s%s=%s" % (delimiter, name, value) elif get is not None: get += "%s%s=%s" % (delimiter, name, value) elif cookie is not None: