Path: blob/master/plugins/dbms/mssqlserver/enumeration.py
2992 views
#!/usr/bin/env python12"""3Copyright (c) 2006-2025 sqlmap developers (https://sqlmap.org)4See the file 'LICENSE' for copying permission5"""67import re89from lib.core.agent import agent10from lib.core.common import arrayizeValue11from lib.core.common import getLimitRange12from lib.core.common import isInferenceAvailable13from lib.core.common import isNoneValue14from lib.core.common import isNumPosStrValue15from lib.core.common import isTechniqueAvailable16from lib.core.common import safeSQLIdentificatorNaming17from lib.core.common import safeStringFormat18from lib.core.common import singleTimeLogMessage19from lib.core.common import unArrayizeValue20from lib.core.common import unsafeSQLIdentificatorNaming21from lib.core.compat import xrange22from lib.core.data import conf23from lib.core.data import kb24from lib.core.data import logger25from lib.core.data import queries26from lib.core.enums import CHARSET_TYPE27from lib.core.enums import DBMS28from lib.core.enums import EXPECTED29from lib.core.enums import PAYLOAD30from lib.core.exception import SqlmapNoneDataException31from lib.core.settings import CURRENT_DB32from lib.request import inject33from plugins.generic.enumeration import Enumeration as GenericEnumeration34from thirdparty import six3536class Enumeration(GenericEnumeration):37def getPrivileges(self, *args, **kwargs):38warnMsg = "on Microsoft SQL Server it is not possible to fetch "39warnMsg += "database users privileges, sqlmap will check whether "40warnMsg += "or not the database users are database administrators"41logger.warning(warnMsg)4243users = []44areAdmins = set()4546if conf.user:47users = [conf.user]48elif not len(kb.data.cachedUsers):49users = self.getUsers()50else:51users = kb.data.cachedUsers5253for user in users:54user = unArrayizeValue(user)5556if user is None:57continue5859isDba = self.isDba(user)6061if isDba is True:62areAdmins.add(user)6364kb.data.cachedUsersPrivileges[user] = None6566return (kb.data.cachedUsersPrivileges, areAdmins)6768def getTables(self):69if len(kb.data.cachedTables) > 0:70return kb.data.cachedTables7172self.forceDbmsEnum()7374if conf.db == CURRENT_DB:75conf.db = self.getCurrentDb()7677if conf.db:78dbs = conf.db.split(',')79else:80dbs = self.getDbs()8182for db in dbs:83dbs[dbs.index(db)] = safeSQLIdentificatorNaming(db)8485dbs = [_ for _ in dbs if _]8687infoMsg = "fetching tables for database"88infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db if isinstance(db, six.string_types) else db[0] for db in sorted(dbs)))89logger.info(infoMsg)9091rootQuery = queries[DBMS.MSSQL].tables9293if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:94for db in dbs:95if conf.excludeSysDbs and db in self.excludeDbsList:96infoMsg = "skipping system database '%s'" % db97singleTimeLogMessage(infoMsg)98continue99100if conf.exclude and re.search(conf.exclude, db, re.I) is not None:101infoMsg = "skipping database '%s'" % db102singleTimeLogMessage(infoMsg)103continue104105for query in (rootQuery.inband.query, rootQuery.inband.query2, rootQuery.inband.query3):106query = query.replace("%s", db)107value = inject.getValue(query, blind=False, time=False)108if not isNoneValue(value):109break110111if not isNoneValue(value):112value = [_ for _ in arrayizeValue(value) if _]113value = [safeSQLIdentificatorNaming(unArrayizeValue(_), True) for _ in value]114kb.data.cachedTables[db] = value115116if not kb.data.cachedTables and isInferenceAvailable() and not conf.direct:117for db in dbs:118if conf.excludeSysDbs and db in self.excludeDbsList:119infoMsg = "skipping system database '%s'" % db120singleTimeLogMessage(infoMsg)121continue122123if conf.exclude and re.search(conf.exclude, db, re.I) is not None:124infoMsg = "skipping database '%s'" % db125singleTimeLogMessage(infoMsg)126continue127128infoMsg = "fetching number of tables for "129infoMsg += "database '%s'" % db130logger.info(infoMsg)131132for query in (rootQuery.blind.count, rootQuery.blind.count2, rootQuery.blind.count3):133_ = query.replace("%s", db)134count = inject.getValue(_, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)135if not isNoneValue(count):136break137138if not isNumPosStrValue(count):139if count != 0:140warnMsg = "unable to retrieve the number of "141warnMsg += "tables for database '%s'" % db142logger.warning(warnMsg)143continue144145tables = []146147for index in xrange(int(count)):148_ = safeStringFormat((rootQuery.blind.query if query == rootQuery.blind.count else rootQuery.blind.query2 if query == rootQuery.blind.count2 else rootQuery.blind.query3).replace("%s", db), index)149150table = inject.getValue(_, union=False, error=False)151if not isNoneValue(table):152kb.hintValue = table153table = safeSQLIdentificatorNaming(table, True)154tables.append(table)155156if tables:157kb.data.cachedTables[db] = tables158else:159warnMsg = "unable to retrieve the tables "160warnMsg += "for database '%s'" % db161logger.warning(warnMsg)162163if not kb.data.cachedTables and not conf.search:164errMsg = "unable to retrieve the tables for any database"165raise SqlmapNoneDataException(errMsg)166else:167for db, tables in kb.data.cachedTables.items():168kb.data.cachedTables[db] = sorted(tables) if tables else tables169170return kb.data.cachedTables171172def searchTable(self):173foundTbls = {}174tblList = conf.tbl.split(',')175rootQuery = queries[DBMS.MSSQL].search_table176tblCond = rootQuery.inband.condition177tblConsider, tblCondParam = self.likeOrExact("table")178179if conf.db == CURRENT_DB:180conf.db = self.getCurrentDb()181182if conf.db:183enumDbs = conf.db.split(',')184elif not len(kb.data.cachedDbs):185enumDbs = self.getDbs()186else:187enumDbs = kb.data.cachedDbs188189for db in enumDbs:190db = safeSQLIdentificatorNaming(db)191foundTbls[db] = []192193for tbl in tblList:194tbl = safeSQLIdentificatorNaming(tbl, True)195196infoMsg = "searching table"197if tblConsider == "1":198infoMsg += "s LIKE"199infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(tbl)200logger.info(infoMsg)201202tblQuery = "%s%s" % (tblCond, tblCondParam)203tblQuery = tblQuery % unsafeSQLIdentificatorNaming(tbl)204205for db in foundTbls.keys():206db = safeSQLIdentificatorNaming(db)207208if conf.excludeSysDbs and db in self.excludeDbsList:209infoMsg = "skipping system database '%s'" % db210singleTimeLogMessage(infoMsg)211continue212213if conf.exclude and re.search(conf.exclude, db, re.I) is not None:214infoMsg = "skipping database '%s'" % db215singleTimeLogMessage(infoMsg)216continue217218if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:219query = rootQuery.inband.query.replace("%s", db)220query += tblQuery221values = inject.getValue(query, blind=False, time=False)222223if not isNoneValue(values):224if isinstance(values, six.string_types):225values = [values]226227for foundTbl in values:228if foundTbl is None:229continue230231foundTbls[db].append(foundTbl)232else:233infoMsg = "fetching number of table"234if tblConsider == "1":235infoMsg += "s LIKE"236infoMsg += " '%s' in database '%s'" % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(db))237logger.info(infoMsg)238239query = rootQuery.blind.count240query = query.replace("%s", db)241query += " AND %s" % tblQuery242count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)243244if not isNumPosStrValue(count):245warnMsg = "no table"246if tblConsider == "1":247warnMsg += "s LIKE"248warnMsg += " '%s' " % unsafeSQLIdentificatorNaming(tbl)249warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(db)250logger.warning(warnMsg)251252continue253254indexRange = getLimitRange(count)255256for index in indexRange:257query = rootQuery.blind.query258query = query.replace("%s", db)259query += " AND %s" % tblQuery260query = agent.limitQuery(index, query, tblCond)261tbl = inject.getValue(query, union=False, error=False)262kb.hintValue = tbl263foundTbls[db].append(tbl)264265for db, tbls in list(foundTbls.items()):266if len(tbls) == 0:267foundTbls.pop(db)268269if not foundTbls:270warnMsg = "no databases contain any of the provided tables"271logger.warning(warnMsg)272return273274conf.dumper.dbTables(foundTbls)275self.dumpFoundTables(foundTbls)276277def searchColumn(self):278rootQuery = queries[DBMS.MSSQL].search_column279foundCols = {}280dbs = {}281whereTblsQuery = ""282infoMsgTbl = ""283infoMsgDb = ""284colList = conf.col.split(',')285286if conf.exclude:287colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None]288289origTbl = conf.tbl290origDb = conf.db291colCond = rootQuery.inband.condition292tblCond = rootQuery.inband.condition2293colConsider, colCondParam = self.likeOrExact("column")294295if conf.db == CURRENT_DB:296conf.db = self.getCurrentDb()297298if conf.db:299enumDbs = conf.db.split(',')300elif not len(kb.data.cachedDbs):301enumDbs = self.getDbs()302else:303enumDbs = kb.data.cachedDbs304305for db in enumDbs:306db = safeSQLIdentificatorNaming(db)307dbs[db] = {}308309for column in colList:310column = safeSQLIdentificatorNaming(column)311conf.db = origDb312conf.tbl = origTbl313314infoMsg = "searching column"315if colConsider == "1":316infoMsg += "s LIKE"317infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(column)318319foundCols[column] = {}320321if conf.tbl:322_ = conf.tbl.split(',')323whereTblsQuery = " AND (" + " OR ".join("%s = '%s'" % (tblCond, unsafeSQLIdentificatorNaming(tbl)) for tbl in _) + ")"324infoMsgTbl = " for table%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(tbl for tbl in _))325326if conf.db == CURRENT_DB:327conf.db = self.getCurrentDb()328329if conf.db:330_ = conf.db.split(',')331infoMsgDb = " in database%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(db for db in _))332elif conf.excludeSysDbs:333infoMsgDb = " not in system database%s '%s'" % ("s" if len(self.excludeDbsList) > 1 else "", ", ".join(db for db in self.excludeDbsList))334else:335infoMsgDb = " across all databases"336337logger.info("%s%s%s" % (infoMsg, infoMsgTbl, infoMsgDb))338339colQuery = "%s%s" % (colCond, colCondParam)340colQuery = colQuery % unsafeSQLIdentificatorNaming(column)341342for db in (_ for _ in dbs if _):343db = safeSQLIdentificatorNaming(db)344345if conf.excludeSysDbs and db in self.excludeDbsList:346continue347348if conf.exclude and re.search(conf.exclude, db, re.I) is not None:349continue350351if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:352query = rootQuery.inband.query % (db, db, db, db, db, db)353query += " AND %s" % colQuery.replace("[DB]", db)354query += whereTblsQuery.replace("[DB]", db)355values = inject.getValue(query, blind=False, time=False)356357if not isNoneValue(values):358if isinstance(values, six.string_types):359values = [values]360361for foundTbl in values:362foundTbl = safeSQLIdentificatorNaming(unArrayizeValue(foundTbl), True)363364if foundTbl is None:365continue366367if foundTbl not in dbs[db]:368dbs[db][foundTbl] = {}369370if colConsider == '1':371conf.db = db372conf.tbl = foundTbl373conf.col = column374375self.getColumns(onlyColNames=True, colTuple=(colConsider, colCondParam), bruteForce=False)376377if db in kb.data.cachedColumns and foundTbl in kb.data.cachedColumns[db] and not isNoneValue(kb.data.cachedColumns[db][foundTbl]):378dbs[db][foundTbl].update(kb.data.cachedColumns[db][foundTbl])379380kb.data.cachedColumns = {}381else:382dbs[db][foundTbl][column] = None383384if db in foundCols[column]:385foundCols[column][db].append(foundTbl)386else:387foundCols[column][db] = [foundTbl]388else:389foundCols[column][db] = []390391infoMsg = "fetching number of tables containing column"392if colConsider == "1":393infoMsg += "s LIKE"394infoMsg += " '%s' in database '%s'" % (column, db)395logger.info("%s%s" % (infoMsg, infoMsgTbl))396397query = rootQuery.blind.count398query = query % (db, db, db, db, db, db)399query += " AND %s" % colQuery.replace("[DB]", db)400query += whereTblsQuery.replace("[DB]", db)401count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)402403if not isNumPosStrValue(count):404warnMsg = "no tables contain column"405if colConsider == "1":406warnMsg += "s LIKE"407warnMsg += " '%s' " % column408warnMsg += "in database '%s'" % db409logger.warning(warnMsg)410411continue412413indexRange = getLimitRange(count)414415for index in indexRange:416query = rootQuery.blind.query417query = query % (db, db, db, db, db, db)418query += " AND %s" % colQuery.replace("[DB]", db)419query += whereTblsQuery.replace("[DB]", db)420query = agent.limitQuery(index, query, colCond.replace("[DB]", db))421tbl = inject.getValue(query, union=False, error=False)422kb.hintValue = tbl423424tbl = safeSQLIdentificatorNaming(tbl, True)425426if tbl not in dbs[db]:427dbs[db][tbl] = {}428429if colConsider == "1":430conf.db = db431conf.tbl = tbl432conf.col = column433434self.getColumns(onlyColNames=True, colTuple=(colConsider, colCondParam), bruteForce=False)435436if db in kb.data.cachedColumns and tbl in kb.data.cachedColumns[db]:437dbs[db][tbl].update(kb.data.cachedColumns[db][tbl])438kb.data.cachedColumns = {}439else:440dbs[db][tbl][column] = None441442foundCols[column][db].append(tbl)443444conf.dumper.dbColumns(foundCols, colConsider, dbs)445self.dumpFoundColumn(dbs, foundCols, colConsider)446447448