From ec44e88db85214c98731ca6f97749fdd778d336f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 21 Jun 2012 10:09:10 +0000 Subject: [PATCH] lots of refactoring regarding removal of already obsolete session file mechanism --- lib/core/common.py | 7 -- lib/core/enums.py | 14 ++- lib/core/option.py | 2 - lib/core/session.py | 116 ++---------------- lib/core/target.py | 116 +++++++++--------- lib/core/testing.py | 24 +--- lib/request/inject.py | 9 +- lib/techniques/blind/inference.py | 57 +++++++++ lib/utils/resume.py | 188 ------------------------------ 9 files changed, 135 insertions(+), 398 deletions(-) delete mode 100644 lib/utils/resume.py diff --git a/lib/core/common.py b/lib/core/common.py index cf43095b6..78faeb779 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -738,13 +738,6 @@ def dataToStdout(data, forceOutput=False): logging._releaseLock() setFormatterPrependFlag(len(data) == 1 and data not in ('\n', '\r') or len(data) > 2 and data[0] == '\r' and data[-1] != '\n') -def dataToSessionFile(data): - if not conf.sessionFile or kb.suppressSession: - return - - conf.sessionFP.write(data) - conf.sessionFP.flush() - def dataToTrafficFile(data): if not conf.trafficFile: return diff --git a/lib/core/enums.py b/lib/core/enums.py index 1dfd6b0ab..bffa7836e 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -143,14 +143,16 @@ class EXPECTED: INT = "int" class HASHDB_KEYS: - KB_ABS_FILE_PATHS = "KB_ABS_FILE_PATHS" - KB_CHARS = "KB_CHARS" - KB_BRUTE_TABLES = "KB_BRUTE_TABLES" - KB_BRUTE_COLUMNS = "KB_BRUTE_COLUMNS" + DBMS = "DBMS" CONF_TMP_PATH = "CONF_TMP_PATH" - KB_XP_CMDSHELL_AVAILABLE = "KB_XP_CMDSHELL_AVAILABLE" - KB_INJECTIONS = "KB_INJECTIONS" + KB_ABS_FILE_PATHS = "KB_ABS_FILE_PATHS" + KB_BRUTE_COLUMNS = "KB_BRUTE_COLUMNS" + KB_BRUTE_TABLES = "KB_BRUTE_TABLES" + KB_CHARS = "KB_CHARS" KB_DYNAMIC_MARKINGS = "KB_DYNAMIC_MARKINGS" + KB_INJECTIONS = "KB_INJECTIONS" + KB_XP_CMDSHELL_AVAILABLE = "KB_XP_CMDSHELL_AVAILABLE" + OS = "OS" class REDIRECTION: YES = "Y" diff --git a/lib/core/option.py b/lib/core/option.py index b6863c787..6c4a24db0 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1497,13 +1497,11 @@ def __setKnowledgeBaseAttributes(flushAll=True): kb.reflectiveMechanism = True kb.reflectiveCounters = {REFLECTIVE_COUNTER.MISS:0, REFLECTIVE_COUNTER.HIT:0} kb.responseTimes = [] - kb.resumedQueries = {} kb.resumeValues = True kb.safeCharEncode = False kb.singleLogFlags = set() kb.skipOthersDbms = None kb.stickyFlag = False - kb.suppressSession = False kb.suppressResumeInfo = False kb.technique = None kb.testMode = False diff --git a/lib/core/session.py b/lib/core/session.py index 8f18ef6d9..af14691ee 100644 --- a/lib/core/session.py +++ b/lib/core/session.py @@ -11,7 +11,7 @@ import re from lib.core.common import Backend from lib.core.common import Format -from lib.core.common import dataToSessionFile +from lib.core.common import hashDBWrite from lib.core.common import intersect from lib.core.common import readInput from lib.core.common import singleTimeWarnMessage @@ -20,42 +20,25 @@ from lib.core.convert import base64unpickle from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger +from lib.core.enums import HASHDB_KEYS from lib.core.enums import OS from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import UNKNOWN_DBMS_VERSION -def safeFormatString(value): - retVal = value - if retVal: - retVal = retVal.replace("[", "__LEFT_SQUARE_BRACKET__").replace("]", "__RIGHT_SQUARE_BRACKET__") - return retVal - -def unSafeFormatString(value): - retVal = value - if retVal: - retVal = retVal.replace("__LEFT_SQUARE_BRACKET__", "[").replace("__RIGHT_SQUARE_BRACKET__", "]") - return retVal - def setDbms(dbms): """ @param dbms: database management system to be set into the knowledge base as fingerprint. @type dbms: C{str} """ - condition = ( - not kb.resumedQueries - or ( kb.resumedQueries.has_key(conf.url) and - not kb.resumedQueries[conf.url].has_key("DBMS") ) - ) - if condition: - dataToSessionFile("[%s][%s][%s][DBMS][%s]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), safeFormatString(dbms))) + hashDBWrite(HASHDB_KEYS.DBMS, dbms) - firstRegExp = "(%s)" % ("|".join([alias for alias in SUPPORTED_DBMS])) - dbmsRegExp = re.search("^%s" % firstRegExp, dbms, re.I) + _ = "(%s)" % ("|".join([alias for alias in SUPPORTED_DBMS])) + _ = re.search("^%s" % _, dbms, re.I) - if dbmsRegExp: - dbms = dbmsRegExp.group(1) + if _: + dbms = _.group(1) Backend.setDbms(dbms) @@ -76,11 +59,6 @@ def setOs(): """ infoMsg = "" - condition = ( - not kb.resumedQueries - or ( kb.resumedQueries.has_key(conf.url) and - not kb.resumedQueries[conf.url].has_key("OS") ) - ) if not kb.bannerFp: return @@ -105,82 +83,4 @@ def setOs(): if infoMsg: logger.info(infoMsg) - if condition: - dataToSessionFile("[%s][%s][%s][OS][%s]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), Backend.getOs())) - -def resumeConfKb(expression, url, value): - if expression == "Dynamic markings" and url == conf.url: - kb.dynamicMarkings = base64unpickle(value[:-1]) - infoMsg = "resuming dynamic markings from session file" - logger.info(infoMsg) - - elif expression == "DBMS" and url == conf.url: - dbms = unSafeFormatString(value[:-1]) - dbms = dbms.lower() - dbmsVersion = [UNKNOWN_DBMS_VERSION] - - firstRegExp = "(%s)" % ("|".join([alias for alias in SUPPORTED_DBMS])) - dbmsRegExp = re.search("%s ([\d\.]+)" % firstRegExp, dbms) - - if dbmsRegExp: - dbms = dbmsRegExp.group(1) - dbmsVersion = [ dbmsRegExp.group(2) ] - - if conf.dbms and conf.dbms.lower() != dbms: - message = "you provided '%s' as back-end DBMS, " % conf.dbms - message += "but from a past scan information on the target URL " - message += "sqlmap assumes the back-end DBMS is %s. " % dbms - message += "Do you really want to force the back-end " - message += "DBMS value? [y/N] " - test = readInput(message, default="N") - - if not test or test[0] in ("n", "N"): - conf.dbms = None - Backend.setDbms(dbms) - Backend.setVersionList(dbmsVersion) - else: - infoMsg = "resuming back-end DBMS '%s' " % dbms - infoMsg += "from session file" - logger.info(infoMsg) - - Backend.setDbms(dbms) - Backend.setVersionList(dbmsVersion) - - elif expression == "OS" and url == conf.url: - os = unSafeFormatString(value[:-1]) - - if os and os != 'None': - infoMsg = "resuming back-end DBMS operating system '%s' " % os - infoMsg += "from session file" - logger.info(infoMsg) - - if conf.os and conf.os.lower() != os.lower(): - message = "you provided '%s' as back-end DBMS operating " % conf.os - message += "system, but from a past scan information on the " - message += "target URL sqlmap assumes the back-end DBMS " - message += "operating system is %s. " % os - message += "Do you really want to force the back-end DBMS " - message += "OS value? [y/N] " - test = readInput(message, default="N") - - if not test or test[0] in ("n", "N"): - conf.os = os - else: - conf.os = os - - Backend.setOs(conf.os) - - elif expression == "Remote temp path" and url == conf.url and conf.tmpPath is None: - conf.tmpPath = unSafeFormatString(value[:-1]) - - infoMsg = "resuming remote absolute path of temporary " - infoMsg += "files directory '%s' from session file" % conf.tmpPath - logger.info(infoMsg) - - elif conf.freshQueries: - pass - - elif expression == "xp_cmdshell availability" and url == conf.url: - kb.xpCmdshellAvailable = True if unSafeFormatString(value[:-1]).lower() == "true" else False - infoMsg = "resuming xp_cmdshell availability" - logger.info(infoMsg) + hashDBWrite(HASHDB_KEYS.OS, Backend.getOs()) diff --git a/lib/core/target.py b/lib/core/target.py index 11561e8dc..61a52f8f3 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -14,7 +14,7 @@ import re import tempfile import time -from lib.core.common import dataToSessionFile +from lib.core.common import Backend from lib.core.common import hashDBRetrieve from lib.core.common import intersect from lib.core.common import paramToDict @@ -37,14 +37,15 @@ from lib.core.exception import sqlmapUserQuitException from lib.core.option import authHandler from lib.core.option import __setDBMS from lib.core.option import __setKnowledgeBaseAttributes -from lib.core.session import resumeConfKb from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import HOST_ALIASES from lib.core.settings import REFERER_ALIASES from lib.core.settings import RESULTS_FILE_FORMAT from lib.core.settings import SOAP_REGEX +from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import UNENCODED_ORIGINAL_VALUE from lib.core.settings import UNICODE_ENCODING +from lib.core.settings import UNKNOWN_DBMS_VERSION from lib.core.settings import URI_INJECTABLE_REGEX from lib.core.settings import USER_AGENT_ALIASES from lib.utils.hashdb import HashDB @@ -243,78 +244,79 @@ def __resumeHashDBValues(): if injection not in kb.injections: kb.injections.append(injection) -def __setOutputResume(): + __resumeDBMS() + __resumeOS() + +def __resumeDBMS(): """ - Check and set the output text file and the resume functionality. + Resume stored DBMS information from HashDB """ - if not conf.sessionFile: - conf.sessionFile = "%s%ssession" % (conf.outputPath, os.sep) + value = hashDBRetrieve(HASHDB_KEYS.DBMS) - logger.info("using '%s' as a session file" % conf.sessionFile) + if not value: + return - if os.path.exists(conf.sessionFile): - if not conf.flushSession: - try: - readSessionFP = codecs.open(conf.sessionFile, "r", UNICODE_ENCODING, 'replace') - __url_cache = set() - __expression_cache = {} + dbms = value.lower() + dbmsVersion = [UNKNOWN_DBMS_VERSION] + _ = "(%s)" % ("|".join([alias for alias in SUPPORTED_DBMS])) + _ = re.search("%s ([\d\.]+)" % _, dbms, re.I) - for line in readSessionFP.readlines(): # xreadlines doesn't return unicode strings when codec.open() is used - if line.count("][") == 4: - line = line.split("][") + if _: + dbms = _.group(1).lower() + dbmsVersion = [_.group(2)] - if len(line) != 5: - continue + if conf.dbms: + if conf.dbms.lower() != dbms: + message = "you provided '%s' as back-end DBMS, " % conf.dbms + message += "but from a past scan information on the target URL " + message += "sqlmap assumes the back-end DBMS is %s. " % dbms + message += "Do you really want to force the back-end " + message += "DBMS value? [y/N] " + test = readInput(message, default="N") - url, _, _, expression, value = line + if not test or test[0] in ("n", "N"): + conf.dbms = None + Backend.setDbms(dbms) + Backend.setVersionList(dbmsVersion) + else: + infoMsg = "resuming back-end DBMS '%s' " % dbms + logger.info(infoMsg) - if not value: - continue + Backend.setDbms(dbms) + Backend.setVersionList(dbmsVersion) - if url[0] == "[": - url = url[1:] +def __resumeOS(): + """ + Resume stored OS information from HashDB + """ - value = value.rstrip('\r\n') # Strips both chars independently + value = hashDBRetrieve(HASHDB_KEYS.OS) - if url not in ( conf.url, conf.hostname ): - continue + if not value: + return - if url not in __url_cache: - kb.resumedQueries[url] = {} - kb.resumedQueries[url][expression] = value - __url_cache.add(url) - __expression_cache[url] = set(expression) + os = value - resumeConfKb(expression, url, value) + if os and os != 'None': + infoMsg = "resuming back-end DBMS operating system '%s' " % os + logger.info(infoMsg) - if expression not in __expression_cache[url]: - kb.resumedQueries[url][expression] = value - __expression_cache[url].add(value) - elif len(value) >= len(kb.resumedQueries[url][expression]): - kb.resumedQueries[url][expression] = value + if conf.os and conf.os.lower() != os.lower(): + message = "you provided '%s' as back-end DBMS operating " % conf.os + message += "system, but from a past scan information on the " + message += "target URL sqlmap assumes the back-end DBMS " + message += "operating system is %s. " % os + message += "Do you really want to force the back-end DBMS " + message += "OS value? [y/N] " + test = readInput(message, default="N") - if kb.injection.place is not None and kb.injection.parameter is not None: - kb.injections.append(kb.injection) - except IOError, msg: - errMsg = "unable to properly open the session file (%s)" % msg - raise sqlmapFilePathException, errMsg - else: - readSessionFP.close() + if not test or test[0] in ("n", "N"): + conf.os = os else: - try: - os.remove(conf.sessionFile) - logger.info("flushing session file") - except OSError, msg: - errMsg = "unable to flush the session file (%s)" % msg - raise sqlmapFilePathException, errMsg + conf.os = os - try: - conf.sessionFP = codecs.open(conf.sessionFile, "a", UNICODE_ENCODING) - dataToSessionFile("\n[%s]\n" % time.strftime("%X %x")) - except IOError: - errMsg = "unable to write on the session file specified" - raise sqlmapFilePathException, errMsg + Backend.setOs(conf.os) def __setResultsFile(): """ @@ -435,7 +437,6 @@ def initTargetEnv(): conf.paramDict = {} conf.parameters = {} - conf.sessionFile = None conf.hashDBFile = None __setKnowledgeBaseAttributes(False) @@ -445,7 +446,6 @@ def initTargetEnv(): def setupTargetEnv(): __createTargetDirs() __setRequestParams() - __setOutputResume() __setHashDB() __resumeHashDBValues() __setResultsFile() diff --git a/lib/core/testing.py b/lib/core/testing.py index a6f2faf54..87e86ffa5 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -126,7 +126,6 @@ def liveTest(): name = None log = [] - session = [] switches = dict(global_) if case.hasAttribute("name"): @@ -143,14 +142,9 @@ def liveTest(): if item.hasAttribute("value"): log.append(replaceVars(item.getAttribute("value"), vars_)) - if case.getElementsByTagName("session"): - for item in case.getElementsByTagName("session")[0].getElementsByTagName("item"): - if item.hasAttribute("value"): - session.append(replaceVars(item.getAttribute("value"), vars_)) - msg = "running live test case '%s' (%d/%d)" % (name, count, length) logger.info(msg) - result = runCase(switches, log, session) + result = runCase(switches, log) if result: logger.info("test passed") else: @@ -178,7 +172,6 @@ def initCase(switches=None): if key in cmdLineOptions.__dict__: cmdLineOptions.__dict__[key] = value - conf.sessionFile = None init(cmdLineOptions, True) __setVerbosity() @@ -190,7 +183,7 @@ def cleanCase(): conf.verbose = 1 __setVerbosity() -def runCase(switches=None, log=None, session=None): +def runCase(switches=None, log=None): retVal = True initCase(switches) @@ -198,19 +191,6 @@ def runCase(switches=None, log=None, session=None): if result == False: #if None ignore retVal = False - if session and retVal: - ifile = open(conf.sessionFile, 'r') - content = ifile.read() - ifile.close() - for item in session: - if item.startswith("r'") and item.endswith("'"): - if not re.search(item[2:-1], content, re.DOTALL): - retVal = False - break - elif content.find(item) < 0: - retVal = False - break - if log and retVal: ifile = open(conf.dumper.getOutputFile(), 'r') content = ifile.read() diff --git a/lib/request/inject.py b/lib/request/inject.py index e1842896a..2c26f962b 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -50,12 +50,11 @@ from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request from lib.request.direct import direct from lib.techniques.blind.inference import bisection +from lib.techniques.blind.inference import queryOutputLength from lib.techniques.dns.test import dnsTest from lib.techniques.dns.use import dnsUse from lib.techniques.error.use import errorUse from lib.techniques.union.use import unionUse -from lib.utils.resume import queryOutputLength -from lib.utils.resume import resume def __goInference(payload, expression, charsetType=None, firstChar=None, lastChar=None, dump=False): start = time.time() @@ -488,8 +487,4 @@ def goStacked(expression, silent=False): Request.queryPage(payload, content=False, silent=silent, noteResponseTime=False, timeBasedCompare=True) def checkBooleanExpression(expression, expectingNone=True): - kb.suppressSession = True - value = getValue(unescaper.unescape(expression), expected=EXPECTED.BOOL, suppressOutput=True, expectingNone=expectingNone) - kb.suppressSession = False - - return value + return getValue(unescaper.unescape(expression), expected=EXPECTED.BOOL, suppressOutput=True, expectingNone=expectingNone) diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 399122703..941dec590 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -7,12 +7,14 @@ Copyright (c) 2006-2012 sqlmap developers (http://www.sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +import re import threading import time from extra.safe2bin.safe2bin import safecharencode from lib.core.agent import agent from lib.core.common import Backend +from lib.core.common import calculateDeltaSeconds from lib.core.common import dataToStdout from lib.core.common import decodeHexValue from lib.core.common import decodeIntToUnicode @@ -24,6 +26,7 @@ from lib.core.common import getPartRun from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite from lib.core.common import incrementCounter +from lib.core.common import randomStr from lib.core.common import safeStringFormat from lib.core.common import setFormatterPrependFlag from lib.core.common import singleTimeWarnMessage @@ -31,6 +34,7 @@ from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.data import queries +from lib.core.enums import CHARSET_TYPE from lib.core.enums import DBMS from lib.core.enums import PAYLOAD from lib.core.exception import sqlmapThreadException @@ -546,3 +550,56 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None _ = finalValue or partialValue return getCounter(kb.technique), safecharencode(_) if kb.safeCharEncode else _ + +def queryOutputLength(expression, payload): + """ + Returns the query output length. + """ + + lengthQuery = queries[Backend.getIdentifiedDbms()].length.query + select = re.search("\ASELECT\s+", expression, re.I) + selectTopExpr = re.search("\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM", expression, re.I) + selectDistinctExpr = re.search("\ASELECT\s+DISTINCT\((.+?)\)\s+FROM", expression, re.I) + selectFromExpr = re.search("\ASELECT\s+(.+?)\s+FROM", expression, re.I) + selectExpr = re.search("\ASELECT\s+(.+)$", expression, re.I) + miscExpr = re.search("\A(.+)", expression, re.I) + + if selectTopExpr or selectDistinctExpr or selectFromExpr or selectExpr: + if selectTopExpr: + regExpr = selectTopExpr.groups()[0] + elif selectDistinctExpr: + regExpr = selectDistinctExpr.groups()[0] + elif selectFromExpr: + regExpr = selectFromExpr.groups()[0] + elif selectExpr: + regExpr = selectExpr.groups()[0] + elif miscExpr: + regExpr = miscExpr.groups()[0] + + if ( select and re.search("\A(COUNT|LTRIM)\(", regExpr, re.I) ) or len(regExpr) <= 1: + return None, None, None + + if selectDistinctExpr: + lengthExpr = "SELECT %s FROM (%s)" % (lengthQuery % regExpr, expression) + + if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): + lengthExpr += " AS %s" % randomStr(lowercase=True) + elif select: + lengthExpr = expression.replace(regExpr, lengthQuery % regExpr, 1) + else: + lengthExpr = lengthQuery % expression + + infoMsg = "retrieving the length of query output" + logger.info(infoMsg) + + start = time.time() + lengthExprUnescaped = unescaper.unescape(lengthExpr) + count, length = bisection(payload, lengthExprUnescaped, charsetType=CHARSET_TYPE.DIGITS) + + debugMsg = "performed %d queries in %d seconds" % (count, calculateDeltaSeconds(start)) + logger.debug(debugMsg) + + if length == " ": + length = 0 + + return count, length, regExpr diff --git a/lib/utils/resume.py b/lib/utils/resume.py deleted file mode 100644 index 47573d352..000000000 --- a/lib/utils/resume.py +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/env python - -""" -$Id$ - -Copyright (c) 2006-2012 sqlmap developers (http://www.sqlmap.org/) -See the file 'doc/COPYING' for copying permission -""" - -import re -import time - -from lib.core.common import calculateDeltaSeconds -from lib.core.common import dataToSessionFile -from lib.core.common import dataToStdout -from lib.core.common import Backend -from lib.core.common import safeStringFormat -from lib.core.common import randomStr -from lib.core.common import replaceNewlineTabs -from lib.core.common import restoreDumpMarkedChars -from lib.core.data import conf -from lib.core.data import kb -from lib.core.data import logger -from lib.core.data import queries -from lib.core.enums import DBMS -from lib.core.enums import CHARSET_TYPE -from lib.core.unescaper import unescaper -from lib.techniques.blind.inference import bisection - -def queryOutputLength(expression, payload): - """ - Returns the query output length. - """ - - lengthQuery = queries[Backend.getIdentifiedDbms()].length.query - select = re.search("\ASELECT\s+", expression, re.I) - selectTopExpr = re.search("\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM", expression, re.I) - selectDistinctExpr = re.search("\ASELECT\s+DISTINCT\((.+?)\)\s+FROM", expression, re.I) - selectFromExpr = re.search("\ASELECT\s+(.+?)\s+FROM", expression, re.I) - selectExpr = re.search("\ASELECT\s+(.+)$", expression, re.I) - miscExpr = re.search("\A(.+)", expression, re.I) - - if selectTopExpr or selectDistinctExpr or selectFromExpr or selectExpr: - if selectTopExpr: - regExpr = selectTopExpr.groups()[0] - elif selectDistinctExpr: - regExpr = selectDistinctExpr.groups()[0] - elif selectFromExpr: - regExpr = selectFromExpr.groups()[0] - elif selectExpr: - regExpr = selectExpr.groups()[0] - elif miscExpr: - regExpr = miscExpr.groups()[0] - - if ( select and re.search("\A(COUNT|LTRIM)\(", regExpr, re.I) ) or len(regExpr) <= 1: - return None, None, None - - if selectDistinctExpr: - lengthExpr = "SELECT %s FROM (%s)" % (lengthQuery % regExpr, expression) - - if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): - lengthExpr += " AS %s" % randomStr(lowercase=True) - elif select: - lengthExpr = expression.replace(regExpr, lengthQuery % regExpr, 1) - else: - lengthExpr = lengthQuery % expression - - infoMsg = "retrieving the length of query output" - logger.info(infoMsg) - - start = time.time() - lengthExprUnescaped = unescaper.unescape(lengthExpr) - count, length = bisection(payload, lengthExprUnescaped, charsetType=CHARSET_TYPE.DIGITS) - - debugMsg = "performed %d queries in %d seconds" % (count, calculateDeltaSeconds(start)) - logger.debug(debugMsg) - - if length == " ": - length = 0 - - return count, length, regExpr - -def resume(expression, payload): - """ - This function can be called to resume part or entire output of a - SQL injection query output. - """ - - try: - if "sqlmapfile" in expression or "sqlmapoutput" in expression or conf.freshQueries: - return None - - condition = ( - kb.resumedQueries and conf.url in kb.resumedQueries - and expression in kb.resumedQueries[conf.url] - ) - - if not condition: - return None - - resumedValue = kb.resumedQueries[conf.url][expression] - - if not resumedValue: - return None - - resumedValue = restoreDumpMarkedChars(resumedValue, True) - - if resumedValue[-1] == "]": - resumedValue = resumedValue[:-1] - - infoMsg = "read from file '%s': " % conf.sessionFile - - if "\n" in resumedValue: - infoMsg += "%s..." % resumedValue.split("\n")[0] - else: - infoMsg += resumedValue - - if not kb.suppressResumeInfo: - dataToStdout("[%s] [INFO] %s\n" % (time.strftime("%X"), infoMsg)) - - return resumedValue - - # If we called this function without providing a payload it means - # that we have called it from lib/request/inject __goInband() or - # from __goError() function so we return to the calling function - # so that the query output will be retrieved taking advantage - # of either error-based or inband SQL injection vulnerability. - if not payload: - return None - - if not Backend.getIdentifiedDbms(): - return None - - substringQuery = queries[Backend.getIdentifiedDbms()].substring.query - select = re.search("\ASELECT ", expression, re.I) - - _, length, regExpr = queryOutputLength(expression, payload) - - if not length: - return None - - if len(resumedValue) == int(length): - infoMsg = "read from file '%s': " % conf.sessionFile - infoMsg += "%s" % resumedValue.split("\n")[0] - logger.info(infoMsg) - - dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injection.place, conf.parameters[kb.injection.place], expression, replaceNewlineTabs(resumedValue))) - - return resumedValue - elif len(resumedValue) < int(length): - infoMsg = "resumed from file '%s': " % conf.sessionFile - infoMsg += "%s..." % resumedValue.split("\n")[0] - logger.info(infoMsg) - - dataToSessionFile("[%s][%s][%s][%s][%s" % (conf.url, kb.injection.place, conf.parameters[kb.injection.place], expression, replaceNewlineTabs(resumedValue))) - - if select: - newExpr = expression.replace(regExpr, safeStringFormat(substringQuery, (regExpr, len(resumedValue) + 1, int(length))), 1) - else: - newExpr = safeStringFormat(substringQuery, (expression, len(resumedValue) + 1, int(length))) - - missingCharsLength = int(length) - len(resumedValue) - - infoMsg = "retrieving pending %d query " % missingCharsLength - infoMsg += "output characters" - logger.info(infoMsg) - - start = time.time() - count, finalValue = bisection(payload, newExpr, length=missingCharsLength) - - debugMsg = "performed %d queries in %d seconds" % (count, calculateDeltaSeconds(start)) - logger.debug(debugMsg) - - if len(finalValue) != ( int(length) - len(resumedValue) ): - warnMsg = "the total length of the query is not " - warnMsg += "right, sqlmap is going to retrieve the " - warnMsg += "query value from the beginning now" - logger.warn(warnMsg) - - return None - - return "%s%s" % (resumedValue, finalValue) - - return None - except ValueError: - errMsg = "invalid resume value for expression: '%s'" % expression - logger.error(errMsg) - return None