From c2b9e539aef896eac943f696c47aeeb981d5329c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 21 Sep 2020 17:04:44 +0200 Subject: [PATCH] Update for #4351 --- lib/core/option.py | 91 ++++++++++++++++++++++++++++++++++++++---- lib/core/optiondict.py | 1 + lib/core/settings.py | 2 +- lib/parse/cmdline.py | 5 ++- lib/request/connect.py | 14 ++++++- sqlmap.conf | 5 ++- tamper/schemasplit.py | 2 +- 7 files changed, 107 insertions(+), 13 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 45ebd33d9..77ddf497f 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -825,7 +825,7 @@ def _setTamperingFunctions(): def _setPreprocessFunctions(): """ - Loads preprocess functions from given script(s) + Loads preprocess function(s) from given script(s) """ if conf.preprocess: @@ -870,7 +870,7 @@ def _setPreprocessFunctions(): raise SqlmapSyntaxException("cannot import preprocess module '%s' (%s)" % (getUnicode(filename[:-3]), getSafeExString(ex))) for name, function in inspect.getmembers(module, inspect.isfunction): - if name == "preprocess" and inspect.getargspec(function).args and all(_ in inspect.getargspec(function).args for _ in ("page", "headers", "code")): + if name == "preprocess" and inspect.getargspec(function).args and all(_ in inspect.getargspec(function).args for _ in ("req",)): found = True kb.preprocessFunctions.append(function) @@ -879,9 +879,84 @@ def _setPreprocessFunctions(): break if not found: - errMsg = "missing function 'preprocess(page, headers=None, code=None)' " + errMsg = "missing function 'preprocess(req)' " errMsg += "in preprocess script '%s'" % script raise SqlmapGenericException(errMsg) + else: + try: + function(_urllib.request.Request("http://localhost")) + except: + handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.PREPROCESS, suffix=".py") + os.close(handle) + + openFile(filename, "w+b").write("#!/usr/bin/env\n\ndef preprocess(req):\n pass\n") + openFile(os.path.join(os.path.dirname(filename), "__init__.py"), "w+b").write("pass") + + errMsg = "function 'preprocess(req)' " + errMsg += "in preprocess script '%s' " % script + errMsg += "appears to be invalid " + errMsg += "(Note: find template script at '%s')" % filename + raise SqlmapGenericException(errMsg) + +def _setPostprocessFunctions(): + """ + Loads postprocess function(s) from given script(s) + """ + + if conf.postprocess: + for script in re.split(PARAMETER_SPLITTING_REGEX, conf.postprocess): + found = False + function = None + + script = safeFilepathEncode(script.strip()) + + try: + if not script: + continue + + if not os.path.exists(script): + errMsg = "postprocess script '%s' does not exist" % script + raise SqlmapFilePathException(errMsg) + + elif not script.endswith(".py"): + errMsg = "postprocess script '%s' should have an extension '.py'" % script + raise SqlmapSyntaxException(errMsg) + except UnicodeDecodeError: + errMsg = "invalid character provided in option '--postprocess'" + raise SqlmapSyntaxException(errMsg) + + dirname, filename = os.path.split(script) + dirname = os.path.abspath(dirname) + + infoMsg = "loading postprocess module '%s'" % filename[:-3] + logger.info(infoMsg) + + if not os.path.exists(os.path.join(dirname, "__init__.py")): + errMsg = "make sure that there is an empty file '__init__.py' " + errMsg += "inside of postprocess scripts directory '%s'" % dirname + raise SqlmapGenericException(errMsg) + + if dirname not in sys.path: + sys.path.insert(0, dirname) + + try: + module = __import__(safeFilepathEncode(filename[:-3])) + except Exception as ex: + raise SqlmapSyntaxException("cannot import postprocess module '%s' (%s)" % (getUnicode(filename[:-3]), getSafeExString(ex))) + + for name, function in inspect.getmembers(module, inspect.isfunction): + if name == "postprocess" and inspect.getargspec(function).args and all(_ in inspect.getargspec(function).args for _ in ("page", "headers", "code")): + found = True + + kb.postprocessFunctions.append(function) + function.__name__ = module.__name__ + + break + + if not found: + errMsg = "missing function 'postprocess(page, headers=None, code=None)' " + errMsg += "in postprocess script '%s'" % script + raise SqlmapGenericException(errMsg) else: try: _, _, _ = function("", {}, None) @@ -889,11 +964,11 @@ def _setPreprocessFunctions(): handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.PREPROCESS, suffix=".py") os.close(handle) - open(filename, "w+b").write("#!/usr/bin/env\n\ndef preprocess(page, headers=None, code=None):\n return page, headers, code\n") - open(os.path.join(os.path.dirname(filename), "__init__.py"), "w+b").write("pass") + openFile(filename, "w+b").write("#!/usr/bin/env\n\ndef postprocess(page, headers=None, code=None):\n return page, headers, code\n") + openFile(os.path.join(os.path.dirname(filename), "__init__.py"), "w+b").write("pass") - errMsg = "function 'preprocess(page, headers=None, code=None)' " - errMsg += "in preprocess script '%s' " % script + errMsg = "function 'postprocess(page, headers=None, code=None)' " + errMsg += "in postprocess script '%s' " % script errMsg += "should return a tuple '(page, headers, code)' " errMsg += "(Note: find template script at '%s')" % filename raise SqlmapGenericException(errMsg) @@ -2038,6 +2113,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.keywords = set(getFileItems(paths.SQL_KEYWORDS)) kb.normalizeCrawlingChoice = None kb.passwordMgr = None + kb.postprocessFunctions = [] kb.preprocessFunctions = [] kb.skipVulnHost = None kb.storeCrawlingChoice = None @@ -2684,6 +2760,7 @@ def init(): _listTamperingFunctions() _setTamperingFunctions() _setPreprocessFunctions() + _setPostprocessFunctions() _setTrafficOutputFP() _setupHTTPCollector() _setHttpChunked() diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 57a28d3ef..c0cfe5d0d 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -222,6 +222,7 @@ optDict = { "hexConvert": "boolean", "outputDir": "string", "parseErrors": "boolean", + "postprocess": "string", "preprocess": "string", "repair": "boolean", "saveConfig": "string", diff --git a/lib/core/settings.py b/lib/core/settings.py index c1e89a116..67c62b13c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from lib.core.enums import OS from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.4.9.15" +VERSION = "1.4.9.16" 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/parse/cmdline.py b/lib/parse/cmdline.py index 090604523..e5b4c6c21 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -683,7 +683,10 @@ def cmdLineParser(argv=None): help="Parse and display DBMS error messages from responses") general.add_argument("--preprocess", dest="preprocess", - help="Use given script(s) for preprocessing of response data") + help="Use given script(s) for preprocessing (request data)") + + general.add_argument("--postprocess", dest="postprocess", + help="Use given script(s) for postprocessing (response data)") general.add_argument("--repair", dest="repair", action="store_true", help="Redump entries having unknown character marker (%s)" % INFERENCE_UNKNOWN_CHAR) diff --git a/lib/request/connect.py b/lib/request/connect.py index e70575e33..739d4dd13 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -501,6 +501,16 @@ class Connect(object): else: return None, None, None + for function in kb.preprocessFunctions: + try: + function(req) + except Exception as ex: + errMsg = "error occurred while running preprocess " + errMsg += "function '%s' ('%s')" % (function.__name__, getSafeExString(ex)) + raise SqlmapGenericException(errMsg) + else: + post, headers = req.data, req.headers + requestHeaders += "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if hasattr(key, "capitalize") else key), getUnicode(value)) for (key, value) in req.header_items()]) if not getRequestHeader(req, HTTP_HEADER.COOKIE) and conf.cj: @@ -815,11 +825,11 @@ class Connect(object): else: page = getUnicode(page) - for function in kb.preprocessFunctions: + for function in kb.postprocessFunctions: try: page, responseHeaders, code = function(page, responseHeaders, code) except Exception as ex: - errMsg = "error occurred while running preprocess " + errMsg = "error occurred while running postprocess " errMsg += "function '%s' ('%s')" % (function.__name__, getSafeExString(ex)) raise SqlmapGenericException(errMsg) diff --git a/sqlmap.conf b/sqlmap.conf index 3d46fb6ba..480cb27c3 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -769,9 +769,12 @@ outputDir = # Valid: True or False parseErrors = False -# Use given script(s) for preprocessing of response data. +# Use given script(s) for preprocessing of request. preprocess = +# Use given script(s) for postprocessing of response data. +postprocess = + # Redump entries having unknown character marker (?). # Valid: True or False repair = False diff --git a/tamper/schemasplit.py b/tamper/schemasplit.py index 243f14076..e7bb1ea58 100644 --- a/tamper/schemasplit.py +++ b/tamper/schemasplit.py @@ -16,7 +16,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces instances of UNION with e0UNION + Splits FROM schema identifiers (e.g. 'testdb.users') with whitespace (e.g. 'testdb 9.e.users') Requirement: * MySQL