From 65a05452f73bd812c9ddb914d3feffe8bc98870b Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Fri, 7 May 2010 13:40:57 +0000 Subject: [PATCH] Added option --search to work in conjunction with -D (done), -T (soon) or -C (replaces --dump -C) - See #190: * --search -D foobar: searches all database names like the ones provided * --search -T foobar: searches all databases' table names like the ones provided (soon) * --search -C foobar: replaces --dump -C --- lib/controller/action.py | 3 + lib/core/common.py | 4 +- lib/core/optiondict.py | 1 + lib/parse/cmdline.py | 3 + lib/parse/queriesfile.py | 28 +- plugins/dbms/access/enumeration.py | 8 +- plugins/dbms/firebird/enumeration.py | 6 + plugins/dbms/oracle/enumeration.py | 6 + plugins/dbms/sqlite/enumeration.py | 14 +- plugins/generic/enumeration.py | 695 +++++++++++++++------------ plugins/generic/misc.py | 18 + sqlmap.conf | 5 + xml/queries.xml | 63 ++- 13 files changed, 516 insertions(+), 338 deletions(-) diff --git a/lib/controller/action.py b/lib/controller/action.py index d1f7e737f..f858edec6 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -120,6 +120,9 @@ def action(): if conf.dumpAll: conf.dbmsHandler.dumpAll() + if conf.search: + conf.dbmsHandler.search() + if conf.query: dumper.string(conf.query, conf.dbmsHandler.sqlQuery(conf.query)) diff --git a/lib/core/common.py b/lib/core/common.py index e8b03a23b..94ba94276 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1006,7 +1006,7 @@ def normalizePath(path): return retVal def safeStringFormat(formatStr, params): - retVal = formatStr.replace('%d', '%s') + retVal = formatStr.replace("%d", "%s") if isinstance(params, str): retVal = retVal.replace("%s", params) @@ -1015,7 +1015,7 @@ def safeStringFormat(formatStr, params): index = 0 while index != -1: - index = retVal.find('%s') + index = retVal.find("%s") if index != -1: if count < len(params): diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 1adc221a0..c5a444023 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -96,6 +96,7 @@ optDict = { "getColumns": "boolean", "dumpTable": "boolean", "dumpAll": "boolean", + "search": "boolean", "user": "string", "db": "string", "tbl": "string", diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 7c3eb15b0..7db4834a3 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -275,6 +275,9 @@ def cmdLineParser(): enumeration.add_option("--dump-all", dest="dumpAll", action="store_true", help="Dump all DBMS databases tables entries") + enumeration.add_option("--search", dest="search", action="store_true", + help="Search column(s), table(s) and/or database name(s)") + enumeration.add_option("-D", dest="db", help="DBMS database to enumerate") diff --git a/lib/parse/queriesfile.py b/lib/parse/queriesfile.py index c1b2906b0..0f8cdc6fd 100644 --- a/lib/parse/queriesfile.py +++ b/lib/parse/queriesfile.py @@ -207,13 +207,6 @@ class queriesHandler(ContentHandler): self.__queries.columns = self.__columns - elif name == "dump_column": - self.__dumpColumn = {} - self.__dumpColumn["inband"] = { "query": self.__inband, "query2": self.__inband2, "condition": self.__conditionInband, "condition2": self.__conditionInband2 } - self.__dumpColumn["blind"] = { "query": self.__blind, "query2": self.__blind2, "count": self.__count, "count2": self.__count2, "condition": self.__conditionBlind, "condition2": self.__conditionBlind2 } - - self.__queries.dumpColumn = self.__dumpColumn - elif name == "dump_table": self.__dumpTable = {} self.__dumpTable["inband"] = { "query": self.__inband } @@ -221,6 +214,27 @@ class queriesHandler(ContentHandler): self.__queries.dumpTable = self.__dumpTable + elif name == "search_db": + self.__searchDb = {} + self.__searchDb["inband"] = { "query": self.__inband, "query2": self.__inband2, "condition": self.__conditionInband, "condition2": self.__conditionInband2 } + self.__searchDb["blind"] = { "query": self.__blind, "query2": self.__blind2, "count": self.__count, "count2": self.__count2, "condition": self.__conditionBlind, "condition2": self.__conditionBlind2 } + + self.__queries.searchDb = self.__searchDb + + elif name == "search_table": + self.__searchTable = {} + self.__searchTable["inband"] = { "query": self.__inband, "query2": self.__inband2, "condition": self.__conditionInband, "condition2": self.__conditionInband2 } + self.__searchTable["blind"] = { "query": self.__blind, "query2": self.__blind2, "count": self.__count, "count2": self.__count2, "condition": self.__conditionBlind, "condition2": self.__conditionBlind2 } + + self.__queries.searchTable = self.__searchTable + + elif name == "search_column": + self.__searchColumn = {} + self.__searchColumn["inband"] = { "query": self.__inband, "query2": self.__inband2, "condition": self.__conditionInband, "condition2": self.__conditionInband2 } + self.__searchColumn["blind"] = { "query": self.__blind, "query2": self.__blind2, "count": self.__count, "count2": self.__count2, "condition": self.__conditionBlind, "condition2": self.__conditionBlind2 } + + self.__queries.searchColumn = self.__searchColumn + def queriesParser(): """ This function calls a class to parse the default DBMS queries diff --git a/plugins/dbms/access/enumeration.py b/plugins/dbms/access/enumeration.py index c4670dded..1fa10e4f0 100644 --- a/plugins/dbms/access/enumeration.py +++ b/plugins/dbms/access/enumeration.py @@ -29,7 +29,7 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): def __init__(self): GenericEnumeration.__init__(self, "Microsoft Access") - + def getDbs(self): warnMsg = "on Microsoft Access it is not possible to enumerate databases" logger.warn(warnMsg) @@ -47,3 +47,9 @@ class Enumeration(GenericEnumeration): logger.warn(warnMsg) return {} + + def searchDb(self): + warnMsg = "on Microsoft Access it is not possible to search databases" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/firebird/enumeration.py b/plugins/dbms/firebird/enumeration.py index 4462f44cb..76254bb6c 100644 --- a/plugins/dbms/firebird/enumeration.py +++ b/plugins/dbms/firebird/enumeration.py @@ -41,3 +41,9 @@ class Enumeration(GenericEnumeration): logger.warn(warnMsg) return {} + + def searchDb(self): + warnMsg = "on Firebird it is not possible to search databases" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/oracle/enumeration.py b/plugins/dbms/oracle/enumeration.py index f6ee4ff26..2edd43578 100644 --- a/plugins/dbms/oracle/enumeration.py +++ b/plugins/dbms/oracle/enumeration.py @@ -180,3 +180,9 @@ class Enumeration(GenericEnumeration): logger.warn(warnMsg) return [] + + def searchDb(self): + warnMsg = "on Oracle it is not possible to search databases" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/sqlite/enumeration.py b/plugins/dbms/sqlite/enumeration.py index e9af5cbb3..0daf4c6a1 100644 --- a/plugins/dbms/sqlite/enumeration.py +++ b/plugins/dbms/sqlite/enumeration.py @@ -78,10 +78,16 @@ class Enumeration(GenericEnumeration): logger.warn(errMsg) - def dumpColumn(self): - errMsg = "on SQLite you must specify the table and columns to dump" - raise sqlmapUnsupportedFeatureException, errMsg - def dumpAll(self): errMsg = "on SQLite you must specify the table and columns to dump" raise sqlmapUnsupportedFeatureException, errMsg + + def searchDb(self): + warnMsg = "on SQLite it is not possible to search databases" + logger.warn(warnMsg) + + return [] + + def searchColumn(self): + errMsg = "on SQLite you must specify the table and columns to dump" + raise sqlmapUnsupportedFeatureException, errMsg diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py index 68caff95a..cca9123b2 100644 --- a/plugins/generic/enumeration.py +++ b/plugins/generic/enumeration.py @@ -28,6 +28,7 @@ from lib.core.agent import agent from lib.core.common import getRange from lib.core.common import parsePasswordHash from lib.core.common import readInput +from lib.core.common import safeStringFormat from lib.core.convert import urlencode from lib.core.data import conf from lib.core.data import kb @@ -971,313 +972,18 @@ class Enumeration: return kb.data.cachedColumns - def dumpColumn(self): - if kb.dbms == "MySQL" and not kb.data.has_information_schema: - errMsg = "information_schema not available, " - errMsg += "back-end DBMS is MySQL < 5.0" - raise sqlmapUnsupportedFeatureException, errMsg - - if not conf.col: - errMsg = "missing column parameter" - raise sqlmapMissingMandatoryOptionException, errMsg - - rootQuery = queries[kb.dbms].dumpColumn - foundCols = {} - dbs = {} - colList = conf.col.split(",") - colCond = rootQuery["inband"]["condition"] - dbCond = rootQuery["inband"]["condition2"] - - message = "do you want sqlmap to consider provided column(s):\n" - message += "[1] as LIKE column names (default)\n" - message += "[2] as exact column names" - colConsider = readInput(message, default="1") - - if not colConsider or colConsider.isdigit() and colConsider == "1": - colConsider = "1" - colCondParam = " LIKE '%%%s%%'" - elif colConsider.isdigit() and colConsider == "2": - colCondParam = "='%s'" - else: - errMsg = "invalid value" - raise sqlmapNoneDataException, errMsg - - for column in colList: - if kb.dbms == "Oracle": - column = column.upper() - conf.db = "USERS" - elif kb.dbms == "Microsoft SQL Server": - if not conf.db: - if not len(kb.data.cachedDbs): - enumDbs = self.getDbs() - else: - enumDbs = kb.data.cachedDbs - - conf.db = ",".join(db for db in enumDbs) - - foundCols[column] = {} - - if conf.db: - for db in conf.db.split(","): - dbs[db] = {} - foundCols[column][db] = [] - - continue - - infoMsg = "fetching databases with tables containing column" - if colConsider == "1": - infoMsg += "s like" - infoMsg += " '%s'" % column - logger.info(infoMsg) - - if conf.excludeSysDbs and kb.dbms != "Oracle": - dbsQuery = "".join(" AND '%s' != %s" % (db, dbCond) for db in self.excludeDbsList) - infoMsg = "skipping system databases '%s'" % ", ".join(db for db in self.excludeDbsList) - logger.info(infoMsg) - else: - dbsQuery = "" - - colQuery = "%s%s" % (colCond, colCondParam) - colQuery = colQuery % column - - if kb.unionPosition or conf.direct: - query = rootQuery["inband"]["query"] - query += colQuery - query += dbsQuery - values = inject.getValue(query, blind=False) - - if values: - if isinstance(values, str): - values = [ values ] - - for value in values: - dbs[value] = {} - foundCols[column][value] = [] - else: - infoMsg = "fetching number of databases with tables containing column" - if colConsider == "1": - infoMsg += "s like" - infoMsg += " '%s'" % column - logger.info(infoMsg) - - query = rootQuery["blind"]["count"] - query += colQuery - query += dbsQuery - count = inject.getValue(query, inband=False, expected="int", charsetType=2) - - if not count.isdigit() or not len(count) or count == "0": - warnMsg = "no databases have tables containing column" - if colConsider == "1": - warnMsg += "s like" - warnMsg += " '%s'" % column - logger.warn(warnMsg) - - continue - - indexRange = getRange(count) - - for index in indexRange: - query = rootQuery["blind"]["query"] - query += colQuery - query += dbsQuery - query = agent.limitQuery(index, query) - db = inject.getValue(query, inband=False) - dbs[db] = {} - foundCols[column][db] = [] - - for column, dbData in foundCols.items(): - colQuery = "%s%s" % (colCond, colCondParam) - colQuery = colQuery % column - - for db in dbData: - infoMsg = "fetching tables containing column" - if colConsider == "1": - infoMsg += "s like" - infoMsg += " '%s' in database '%s'" % (column, db) - logger.info(infoMsg) - - if kb.unionPosition or conf.direct: - query = rootQuery["inband"]["query2"] - - if kb.dbms in ( "MySQL", "PostgreSQL" ): - query = query % db - query += " AND %s" % colQuery - elif kb.dbms == "Oracle": - query += " WHERE %s" % colQuery - elif kb.dbms == "Microsoft SQL Server": - query = query % (db, db, db, db, db) - query += " AND %s" % colQuery.replace("[DB]", db) - - values = inject.getValue(query, blind=False) - - if values: - if isinstance(values, str): - values = [ values ] - - for value in values: - if value not in dbs[db]: - dbs[db][value] = {} - - dbs[db][value][column] = None - foundCols[column][db].append(value) - else: - infoMsg = "fetching number of tables containing column" - if colConsider == "1": - infoMsg += "s like" - infoMsg += " '%s' in database '%s'" % (column, db) - logger.info(infoMsg) - - query = rootQuery["blind"]["count2"] - - if kb.dbms in ( "MySQL", "PostgreSQL" ): - query = query % db - query += " AND %s" % colQuery - elif kb.dbms == "Oracle": - query += " WHERE %s" % colQuery - elif kb.dbms == "Microsoft SQL Server": - query = query % (db, db, db, db, db) - query += " AND %s" % colQuery.replace("[DB]", db) - - count = inject.getValue(query, inband=False, expected="int", charsetType=2) - - if not count.isdigit() or not len(count) or count == "0": - warnMsg = "no tables contain column" - if colConsider == "1": - warnMsg += "s like" - warnMsg += " '%s' " % column - warnMsg += "in database '%s'" % db - logger.warn(warnMsg) - - continue - - indexRange = getRange(count) - - for index in indexRange: - query = rootQuery["blind"]["query2"] - - if kb.dbms in ( "MySQL", "PostgreSQL" ): - query = query % db - query += " AND %s" % colQuery - field = None - elif kb.dbms == "Oracle": - query += " WHERE %s" % colQuery - field = None - elif kb.dbms == "Microsoft SQL Server": - query = query % (db, db, db, db, db) - query += " AND %s" % colQuery.replace("[DB]", db) - field = colCond.replace("[DB]", db) - - query = agent.limitQuery(index, query, field) - tbl = inject.getValue(query, inband=False) - - if tbl not in dbs[db]: - dbs[db][tbl] = {} - - dbs[db][tbl][column] = None - foundCols[column][db].append(tbl) - - if colConsider == "1": - okDbs = {} - - for db, tableData in dbs.items(): - conf.db = db - okDbs[db] = {} - - for tbl, columns in tableData.items(): - conf.tbl = tbl - - for column in columns: - conf.col = column - - self.getColumns(onlyColNames=True) - - if tbl in okDbs[db]: - okDbs[db][tbl].update(kb.data.cachedColumns[db][tbl]) - else: - okDbs[db][tbl] = kb.data.cachedColumns[db][tbl] - - kb.data.cachedColumns = {} - - dbs = okDbs - - if not dbs: - warnMsg = "no databases have tables containing any of the " - warnMsg += "provided columns" - logger.warn(warnMsg) - return - - dumper.dbColumns(foundCols, colConsider, dbs) - - message = "do you want to dump entries? [Y/n] " - output = readInput(message, default="Y") - - if output and output[0] not in ("y", "Y"): - return - - dumpFromDbs = [] - message = "which database(s)?\n[a]ll (default)\n" - - for db, tblData in dbs.items(): - if tblData: - message += "[%s]\n" % db - - message += "[q]uit" - test = readInput(message, default="a") - - if not test or test in ("a", "A"): - dumpFromDbs = dbs.keys() - elif test in ("q", "Q"): - return - else: - dumpFromDbs = test.replace(" ", "").split(",") - - for db, tblData in dbs.items(): - if db not in dumpFromDbs or not tblData: - continue - - conf.db = db - dumpFromTbls = [] - message = "which table(s) of database '%s'?\n" % db - message += "[a]ll (default)\n" - - for tbl in tblData: - message += "[%s]\n" % tbl - - message += "[s]kip\n" - message += "[q]uit" - test = readInput(message, default="a") - - if not test or test in ("a", "A"): - dumpFromTbls = tblData - elif test in ("s", "S"): - continue - elif test in ("q", "Q"): - return - else: - dumpFromTbls = test.replace(" ", "").split(",") - - for table, columns in tblData.items(): - if table not in dumpFromTbls: - continue - - conf.tbl = table - conf.col = ",".join(column for column in columns) - kb.data.cachedColumns = {} - kb.data.dumpedTable = {} - - data = self.dumpTable() - - if data: - dumper.dbTableValues(data) - def dumpTable(self): if not conf.tbl and not conf.col: - errMsg = "missing both table and column parameters, please " - errMsg += "provide at least one of them" + errMsg = "missing table parameter" raise sqlmapMissingMandatoryOptionException, errMsg if conf.col and not conf.tbl: - self.dumpColumn() + warnMsg = "missing table parameter. You only provided " + warnMsg += "column(s). sqlmap will search for all databases' " + warnMsg += "tables containing the provided column(s)" + logger.warn(warnMsg) + + self.searchColumn() return if "." in conf.tbl: @@ -1479,6 +1185,391 @@ class Enumeration: if data: dumper.dbTableValues(data) + def searchDb(self): + foundDbs = [] + rootQuery = queries[kb.dbms].searchDb + dbList = conf.db.split(",") + + if kb.dbms == "MySQL" and not kb.data.has_information_schema: + dbCond = rootQuery["inband"]["condition2"] + else: + dbCond = rootQuery["inband"]["condition"] + + dbConsider, dbCondParam = self.likeOrExact("database") + + for db in dbList: + infoMsg = "searching database" + if dbConsider == "1": + infoMsg += "s like" + infoMsg += " '%s'" % db + logger.info(infoMsg) + + if conf.excludeSysDbs: + exclDbsQuery = "".join(" AND '%s' != %s" % (db, dbCond) for db in self.excludeDbsList) + infoMsg = "skipping system databases '%s'" % ", ".join(db for db in self.excludeDbsList) + logger.info(infoMsg) + else: + exclDbsQuery = "" + + dbQuery = "%s%s" % (dbCond, dbCondParam) + dbQuery = dbQuery % db + + if kb.unionPosition or conf.direct: + if kb.dbms == "MySQL" and not kb.data.has_information_schema: + query = rootQuery["inband"]["query2"] + else: + query = rootQuery["inband"]["query"] + query += dbQuery + query += exclDbsQuery + values = inject.getValue(query, blind=False) + + if values: + if isinstance(values, str): + values = [ values ] + + for value in values: + foundDbs.append(value) + else: + infoMsg = "fetching number of databases" + if dbConsider == "1": + infoMsg += "s like" + infoMsg += " '%s'" % db + logger.info(infoMsg) + + if kb.dbms == "MySQL" and not kb.data.has_information_schema: + query = rootQuery["blind"]["count2"] + else: + query = rootQuery["blind"]["count"] + query += dbQuery + query += exclDbsQuery + count = inject.getValue(query, inband=False, expected="int", charsetType=2) + + if not count.isdigit() or not len(count) or count == "0": + warnMsg = "no database" + if dbConsider == "1": + warnMsg += "s like" + warnMsg += " '%s' found" % db + logger.warn(warnMsg) + + continue + + indexRange = getRange(count) + + for index in indexRange: + if kb.dbms == "MySQL" and not kb.data.has_information_schema: + query = rootQuery["blind"]["query2"] + else: + query = rootQuery["blind"]["query"] + query += dbQuery + query += exclDbsQuery + query = agent.limitQuery(index, query, dbCond) + + foundDbs.append(inject.getValue(query, inband=False)) + + return foundDbs + + def searchTable(self): + errMsg = "search for table names is not supported yet" + raise sqlmapUnsupportedFeatureException, errMsg + + def searchColumn(self): + if kb.dbms == "MySQL" and not kb.data.has_information_schema: + errMsg = "information_schema not available, " + errMsg += "back-end DBMS is MySQL < 5.0" + raise sqlmapUnsupportedFeatureException, errMsg + + rootQuery = queries[kb.dbms].searchColumn + foundCols = {} + dbs = {} + colList = conf.col.split(",") + colCond = rootQuery["inband"]["condition"] + dbCond = rootQuery["inband"]["condition2"] + + colConsider, colCondParam = self.likeOrExact("column") + + for column in colList: + if kb.dbms == "Oracle": + column = column.upper() + conf.db = "USERS" + elif kb.dbms == "Microsoft SQL Server": + if not conf.db: + if not len(kb.data.cachedDbs): + enumDbs = self.getDbs() + else: + enumDbs = kb.data.cachedDbs + + conf.db = ",".join(db for db in enumDbs) + + foundCols[column] = {} + + if conf.db: + for db in conf.db.split(","): + dbs[db] = {} + foundCols[column][db] = [] + + continue + + infoMsg = "fetching databases with tables containing column" + if colConsider == "1": + infoMsg += "s like" + infoMsg += " '%s'" % column + logger.info(infoMsg) + + if conf.excludeSysDbs and kb.dbms != "Oracle": + exclDbsQuery = "".join(" AND '%s' != %s" % (db, dbCond) for db in self.excludeDbsList) + infoMsg = "skipping system databases '%s'" % ", ".join(db for db in self.excludeDbsList) + logger.info(infoMsg) + else: + exclDbsQuery = "" + + colQuery = "%s%s" % (colCond, colCondParam) + colQuery = colQuery % column + + if kb.unionPosition or conf.direct: + query = rootQuery["inband"]["query"] + query += colQuery + query += exclDbsQuery + values = inject.getValue(query, blind=False) + + if values: + if isinstance(values, str): + values = [ values ] + + for value in values: + dbs[value] = {} + foundCols[column][value] = [] + else: + infoMsg = "fetching number of databases with tables containing column" + if colConsider == "1": + infoMsg += "s like" + infoMsg += " '%s'" % column + logger.info(infoMsg) + + query = rootQuery["blind"]["count"] + query += colQuery + query += exclDbsQuery + count = inject.getValue(query, inband=False, expected="int", charsetType=2) + + if not count.isdigit() or not len(count) or count == "0": + warnMsg = "no databases have tables containing column" + if colConsider == "1": + warnMsg += "s like" + warnMsg += " '%s'" % column + logger.warn(warnMsg) + + continue + + indexRange = getRange(count) + + for index in indexRange: + query = rootQuery["blind"]["query"] + query += colQuery + query += exclDbsQuery + query = agent.limitQuery(index, query) + db = inject.getValue(query, inband=False) + dbs[db] = {} + foundCols[column][db] = [] + + for column, dbData in foundCols.items(): + colQuery = "%s%s" % (colCond, colCondParam) + colQuery = colQuery % column + + for db in dbData: + infoMsg = "fetching tables containing column" + if colConsider == "1": + infoMsg += "s like" + infoMsg += " '%s' in database '%s'" % (column, db) + logger.info(infoMsg) + + if kb.unionPosition or conf.direct: + query = rootQuery["inband"]["query2"] + + if kb.dbms in ( "MySQL", "PostgreSQL" ): + query = query % db + query += " AND %s" % colQuery + elif kb.dbms == "Oracle": + query += " WHERE %s" % colQuery + elif kb.dbms == "Microsoft SQL Server": + query = query % (db, db, db, db, db) + query += " AND %s" % colQuery.replace("[DB]", db) + + values = inject.getValue(query, blind=False) + + if values: + if isinstance(values, str): + values = [ values ] + + for value in values: + if value not in dbs[db]: + dbs[db][value] = {} + + dbs[db][value][column] = None + foundCols[column][db].append(value) + else: + infoMsg = "fetching number of tables containing column" + if colConsider == "1": + infoMsg += "s like" + infoMsg += " '%s' in database '%s'" % (column, db) + logger.info(infoMsg) + + query = rootQuery["blind"]["count2"] + + if kb.dbms in ( "MySQL", "PostgreSQL" ): + query = query % db + query += " AND %s" % colQuery + elif kb.dbms == "Oracle": + query += " WHERE %s" % colQuery + elif kb.dbms == "Microsoft SQL Server": + query = query % (db, db, db, db, db) + query += " AND %s" % colQuery.replace("[DB]", db) + + count = inject.getValue(query, inband=False, expected="int", charsetType=2) + + if not count.isdigit() or not len(count) or count == "0": + warnMsg = "no tables contain column" + if colConsider == "1": + warnMsg += "s like" + warnMsg += " '%s' " % column + warnMsg += "in database '%s'" % db + logger.warn(warnMsg) + + continue + + indexRange = getRange(count) + + for index in indexRange: + query = rootQuery["blind"]["query2"] + + if kb.dbms in ( "MySQL", "PostgreSQL" ): + query = query % db + query += " AND %s" % colQuery + field = None + elif kb.dbms == "Oracle": + query += " WHERE %s" % colQuery + field = None + elif kb.dbms == "Microsoft SQL Server": + query = query % (db, db, db, db, db) + query += " AND %s" % colQuery.replace("[DB]", db) + field = colCond.replace("[DB]", db) + + query = agent.limitQuery(index, query, field) + tbl = inject.getValue(query, inband=False) + + if tbl not in dbs[db]: + dbs[db][tbl] = {} + + dbs[db][tbl][column] = None + foundCols[column][db].append(tbl) + + if colConsider == "1": + okDbs = {} + + for db, tableData in dbs.items(): + conf.db = db + okDbs[db] = {} + + for tbl, columns in tableData.items(): + conf.tbl = tbl + + for column in columns: + conf.col = column + + self.getColumns(onlyColNames=True) + + if tbl in okDbs[db]: + okDbs[db][tbl].update(kb.data.cachedColumns[db][tbl]) + else: + okDbs[db][tbl] = kb.data.cachedColumns[db][tbl] + + kb.data.cachedColumns = {} + + dbs = okDbs + + if not dbs: + warnMsg = "no databases have tables containing any of the " + warnMsg += "provided columns" + logger.warn(warnMsg) + return + + dumper.dbColumns(foundCols, colConsider, dbs) + + message = "do you want to dump entries? [Y/n] " + output = readInput(message, default="Y") + + if output and output[0] not in ("y", "Y"): + return + + dumpFromDbs = [] + message = "which database(s)?\n[a]ll (default)\n" + + for db, tblData in dbs.items(): + if tblData: + message += "[%s]\n" % db + + message += "[q]uit" + test = readInput(message, default="a") + + if not test or test in ("a", "A"): + dumpFromDbs = dbs.keys() + elif test in ("q", "Q"): + return + else: + dumpFromDbs = test.replace(" ", "").split(",") + + for db, tblData in dbs.items(): + if db not in dumpFromDbs or not tblData: + continue + + conf.db = db + dumpFromTbls = [] + message = "which table(s) of database '%s'?\n" % db + message += "[a]ll (default)\n" + + for tbl in tblData: + message += "[%s]\n" % tbl + + message += "[s]kip\n" + message += "[q]uit" + test = readInput(message, default="a") + + if not test or test in ("a", "A"): + dumpFromTbls = tblData + elif test in ("s", "S"): + continue + elif test in ("q", "Q"): + return + else: + dumpFromTbls = test.replace(" ", "").split(",") + + for table, columns in tblData.items(): + if table not in dumpFromTbls: + continue + + conf.tbl = table + conf.col = ",".join(column for column in columns) + kb.data.cachedColumns = {} + kb.data.dumpedTable = {} + + data = self.dumpTable() + + if data: + dumper.dbTableValues(data) + + def search(self): + if conf.db: + dumper.lister("found databases", self.searchDb()) + + if conf.tbl: + dumper.dbTables(self.searchTable()) + + if conf.col: + self.searchColumn() + + if not conf.db and not conf.tbl and not conf.col: + errMsg = "missing parameter, provide -D, -T or -C together " + errMsg += "with --search" + raise sqlmapMissingMandatoryOptionException, errMsg + def sqlQuery(self, query): output = None sqlType = None diff --git a/plugins/generic/misc.py b/plugins/generic/misc.py index 5dd95ad84..4972ab43e 100644 --- a/plugins/generic/misc.py +++ b/plugins/generic/misc.py @@ -184,3 +184,21 @@ class Miscellaneous: warnMsg += "saved on the file system can only be deleted " warnMsg += "manually" logger.warn(warnMsg) + + def likeOrExact(self, what): + message = "do you want sqlmap to consider provided %s(s):\n" % what + message += "[1] as LIKE column names (default)\n" + message += "[2] as exact column names" + + choice = readInput(message, default="1") + + if not choice or choice == "1": + choice = "1" + condParam = " LIKE '%%%s%%'" + elif choice.isdigit() and choice == "2": + condParam = "='%s'" + else: + errMsg = "invalid value" + raise sqlmapNoneDataException, errMsg + + return choice, condParam diff --git a/sqlmap.conf b/sqlmap.conf index 7ee15e2ba..2dfc98272 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -290,6 +290,11 @@ dumpTable = False # Valid: True or False dumpAll = False +# Search column(s), table(s) and/or database name(s). +# Requires: db, tbl or col +# Valid: True or False +search = False + # Back-end database management system database to enumerate. db = diff --git a/xml/queries.xml b/xml/queries.xml index 0be5618fc..496401f17 100644 --- a/xml/queries.xml +++ b/xml/queries.xml @@ -55,14 +55,19 @@ - - - - + + + + + + + + + @@ -126,14 +131,16 @@ - - - - + + + + + + @@ -190,14 +197,19 @@ - - - - + + + + + + + + + @@ -245,14 +257,19 @@ - - - - + + + + + + + + + @@ -291,11 +308,13 @@ - + + + @@ -355,10 +374,6 @@ - - - - @@ -369,7 +384,11 @@ - + + + + +