diff --git a/lib/utils/pivotdumptable.py b/lib/utils/pivotdumptable.py new file mode 100644 index 000000000..79d8d4723 --- /dev/null +++ b/lib/utils/pivotdumptable.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2012 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +from extra.safe2bin.safe2bin import safechardecode +from lib.core.bigarray import BigArray +from lib.core.common import Backend +from lib.core.common import decodeIntToUnicode +from lib.core.common import isNoneValue +from lib.core.common import isNumPosStrValue +from lib.core.common import singleTimeWarnMessage +from lib.core.common import unArrayizeValue +from lib.core.common import unsafeSQLIdentificatorNaming +from lib.core.data import conf +from lib.core.data import logger +from lib.core.data import queries +from lib.core.enums import CHARSET_TYPE +from lib.core.enums import EXPECTED +from lib.core.exception import sqlmapConnectionException +from lib.core.exception import sqlmapNoneDataException +from lib.core.settings import MAX_INT +from lib.request import inject + +def pivotDumpTable(table, colList, count=None, blind=True): + lengths = {} + entries = {} + + dumpNode = queries[Backend.getIdentifiedDbms()].dump_table.blind + + validColumnList = False + validPivotValue = False + + if count is None: + query = dumpNode.count % table + count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if blind else inject.getValue(query, blind=False, expected=EXPECTED.INT) + + if isinstance(count, basestring) and count.isdigit(): + count = int(count) + + if count == 0: + infoMsg = "table '%s' appears to be empty" % unsafeSQLIdentificatorNaming(table) + logger.info(infoMsg) + + for column in colList: + lengths[column] = len(column) + entries[column] = [] + + return entries, lengths + + elif not isNumPosStrValue(count): + return None + + for column in colList: + lengths[column] = 0 + entries[column] = BigArray() + + colList = filter(None, sorted(colList, key=lambda x: len(x) if x else MAX_INT)) + + for column in colList: + infoMsg = "fetching number of distinct " + infoMsg += "values for column '%s'" % column + logger.info(infoMsg) + + query = dumpNode.count2 % (column, table) + value = inject.getValue(query, blind=blind, inband=not blind, error=not blind, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) + + if isNumPosStrValue(value): + validColumnList = True + + if value == count: + infoMsg = "using column '%s' as a pivot " % column + infoMsg += "for retrieving row data" + logger.info(infoMsg) + + validPivotValue = True + + colList.remove(column) + colList.insert(0, column) + break + + if not validColumnList: + errMsg = "all column name(s) provided are non-existent" + raise sqlmapNoneDataException, errMsg + + if not validPivotValue: + warnMsg = "no proper pivot column provided (with unique values)." + warnMsg += " It won't be possible to retrieve all rows" + logger.warn(warnMsg) + + pivotValue = " " + breakRetrieval = False + + try: + for i in xrange(count): + if breakRetrieval: + break + + for column in colList: + # Correction for pivotValues with unrecognized/problematic chars + for char in ('\'', '?'): + if pivotValue and char in pivotValue and pivotValue[0] != char: + pivotValue = pivotValue.split(char)[0] + pivotValue = pivotValue[:-1] + decodeIntToUnicode(ord(pivotValue[-1]) + 1) + break + if column == colList[0]: + query = dumpNode.query % (column, table, column, pivotValue) + else: + query = dumpNode.query2 % (column, table, colList[0], pivotValue) + + value = inject.getValue(query, blind=blind, inband=not blind, error=not blind) + + if column == colList[0]: + if isNoneValue(value): + breakRetrieval = True + break + else: + pivotValue = safechardecode(value) + + if conf.limitStart or conf.limitStop: + if conf.limitStart and (i + 1) < conf.limitStart: + warnMsg = "skipping first %d pivot " % conf.limitStart + warnMsg += "point values" + singleTimeWarnMessage(warnMsg) + break + elif conf.limitStop and (i + 1) > conf.limitStop: + breakRetrieval = True + break + + value = "" if isNoneValue(value) else unArrayizeValue(value) + + lengths[column] = max(lengths[column], len(value) if value else 0) + entries[column].append(value) + + except KeyboardInterrupt: + warnMsg = "user aborted during enumeration. sqlmap " + warnMsg += "will display partial output" + logger.warn(warnMsg) + + except sqlmapConnectionException, e: + errMsg = "connection exception detected. sqlmap " + errMsg += "will display partial output" + errMsg += "'%s'" % e + logger.critical(errMsg) + + return entries, lengths