sqlmap/plugins/dbms/mssqlserver/enumeration.py

415 lines
16 KiB
Python
Raw Normal View History

#!/usr/bin/env python
"""
2012-07-12 21:38:03 +04:00
Copyright (c) 2006-2012 sqlmap developers (http://sqlmap.org/)
2010-10-15 03:18:29 +04:00
See the file 'doc/COPYING' for copying permission
"""
from lib.core.agent import agent
from lib.core.common import arrayizeValue
from lib.core.common import Backend
2012-02-16 18:42:28 +04:00
from lib.core.common import getLimitRange
from lib.core.common import isInferenceAvailable
from lib.core.common import isNoneValue
2010-12-18 00:45:20 +03:00
from lib.core.common import isNumPosStrValue
from lib.core.common import isTechniqueAvailable
2011-03-30 01:54:15 +04:00
from lib.core.common import safeSQLIdentificatorNaming
from lib.core.common import unsafeSQLIdentificatorNaming
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import queries
from lib.core.enums import CHARSET_TYPE
2010-12-10 16:04:36 +03:00
from lib.core.enums import EXPECTED
2011-01-15 13:14:05 +03:00
from lib.core.enums import PAYLOAD
from lib.core.exception import sqlmapNoneDataException
2012-02-16 18:42:28 +04:00
from lib.core.settings import CURRENT_DB
from lib.request import inject
from plugins.generic.enumeration import Enumeration as GenericEnumeration
class Enumeration(GenericEnumeration):
def __init__(self):
GenericEnumeration.__init__(self)
2010-11-08 15:45:23 +03:00
def getPrivileges(self, *args):
warnMsg = "on Microsoft SQL Server it is not possible to fetch "
warnMsg += "database users privileges, sqlmap will check whether "
warnMsg += "or not the database users are database administrators"
logger.warn(warnMsg)
users = []
areAdmins = set()
if conf.user:
users = [ conf.user ]
elif not len(kb.data.cachedUsers):
users = self.getUsers()
else:
users = kb.data.cachedUsers
for user in users:
if user is None:
continue
isDba = self.isDba(user)
if isDba is True:
areAdmins.add(user)
kb.data.cachedUsersPrivileges[user] = None
return ( kb.data.cachedUsersPrivileges, areAdmins )
def getTables(self):
if len(kb.data.cachedTables) > 0:
return kb.data.cachedTables
self.forceDbmsEnum()
2012-02-16 18:42:28 +04:00
if conf.db == CURRENT_DB:
conf.db = self.getCurrentDb()
if conf.db:
dbs = conf.db.split(",")
else:
dbs = self.getDbs()
for db in dbs:
dbs[dbs.index(db)] = safeSQLIdentificatorNaming(db)
dbs = filter(None, dbs)
infoMsg = "fetching tables for database"
2012-01-14 01:46:21 +04:00
infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db if isinstance(db, basestring) else db[0] for db in sorted(dbs)))
logger.info(infoMsg)
rootQuery = queries[Backend.getIdentifiedDbms()].tables
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR)) or conf.direct:
for db in dbs:
if conf.excludeSysDbs and db in self.excludeDbsList:
infoMsg = "skipping system database '%s'" % db
logger.info(infoMsg)
continue
for query in (rootQuery.inband.query, rootQuery.inband.query2, rootQuery.inband.query3):
query = query.replace("%s", db)
value = inject.getValue(query, blind=False)
if not isNoneValue(value):
break
if not isNoneValue(value):
value = filter(None, arrayizeValue(value))
value = [safeSQLIdentificatorNaming(_, True) for _ in value]
kb.data.cachedTables[db] = value
if not kb.data.cachedTables and isInferenceAvailable() and not conf.direct:
for db in dbs:
if conf.excludeSysDbs and db in self.excludeDbsList:
infoMsg = "skipping system database '%s'" % db
logger.info(infoMsg)
continue
2011-04-30 17:20:05 +04:00
infoMsg = "fetching number of tables for "
infoMsg += "database '%s'" % db
logger.info(infoMsg)
for query in (rootQuery.blind.count, rootQuery.blind.count2, rootQuery.blind.count3):
2012-02-01 16:53:07 +04:00
_ = query.replace("%s", db)
2012-04-03 16:19:37 +04:00
count = inject.getValue(_, inband=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
if not isNoneValue(count):
break
2010-12-18 00:45:20 +03:00
if not isNumPosStrValue(count):
2012-04-03 16:19:37 +04:00
if count != 0:
warnMsg = "unable to retrieve the number of "
warnMsg += "tables for database '%s'" % db
logger.warn(warnMsg)
continue
tables = []
for index in xrange(int(count)):
_ = (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
table = inject.getValue(_, inband=False, error=False)
2012-02-29 17:56:40 +04:00
if not isNoneValue(table):
kb.hintValue = table
table = safeSQLIdentificatorNaming(table, True)
tables.append(table)
if tables:
kb.data.cachedTables[db] = tables
else:
2011-04-30 17:20:05 +04:00
warnMsg = "unable to retrieve the tables "
warnMsg += "for database '%s'" % db
logger.warn(warnMsg)
if not kb.data.cachedTables:
errMsg = "unable to retrieve the tables for any database"
raise sqlmapNoneDataException(errMsg)
2011-10-28 16:49:35 +04:00
else:
for db, tables in kb.data.cachedTables.items():
2011-10-28 17:07:23 +04:00
kb.data.cachedTables[db] = sorted(tables) if tables else tables
return kb.data.cachedTables
def searchTable(self):
foundTbls = {}
tblList = conf.tbl.split(",")
rootQuery = queries[Backend.getIdentifiedDbms()].search_table
tblCond = rootQuery.inband.condition
tblConsider, tblCondParam = self.likeOrExact("table")
if conf.db and conf.db != CURRENT_DB:
enumDbs = conf.db.split(",")
elif not len(kb.data.cachedDbs):
enumDbs = self.getDbs()
else:
enumDbs = kb.data.cachedDbs
for db in enumDbs:
2011-03-30 01:54:15 +04:00
db = safeSQLIdentificatorNaming(db)
foundTbls[db] = []
for tbl in tblList:
2011-03-30 01:54:15 +04:00
tbl = safeSQLIdentificatorNaming(tbl, True)
infoMsg = "searching table"
if tblConsider == "1":
infoMsg += "s like"
2011-03-30 01:54:15 +04:00
infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(tbl)
logger.info(infoMsg)
tblQuery = "%s%s" % (tblCond, tblCondParam)
2011-03-30 01:54:15 +04:00
tblQuery = tblQuery % unsafeSQLIdentificatorNaming(tbl)
for db in foundTbls.keys():
2011-03-30 01:54:15 +04:00
db = safeSQLIdentificatorNaming(db)
if conf.excludeSysDbs and db in self.excludeDbsList:
infoMsg = "skipping system database '%s'" % db
logger.info(infoMsg)
continue
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR)) or conf.direct:
2012-02-01 16:53:07 +04:00
query = rootQuery.inband.query.replace("%s", db)
query += tblQuery
values = inject.getValue(query, blind=False)
if not isNoneValue(values):
if isinstance(values, basestring):
values = [ values ]
for foundTbl in values:
2011-02-10 14:34:16 +03:00
if foundTbl is None:
continue
foundTbls[db].append(foundTbl)
else:
infoMsg = "fetching number of table"
if tblConsider == "1":
infoMsg += "s like"
2011-03-30 01:54:15 +04:00
infoMsg += " '%s' in database '%s'" % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(db))
logger.info(infoMsg)
2012-02-01 16:53:07 +04:00
query = rootQuery.blind.count
query = query.replace("%s", db)
query += " AND %s" % tblQuery
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
2010-12-18 00:45:20 +03:00
if not isNumPosStrValue(count):
warnMsg = "no table"
if tblConsider == "1":
warnMsg += "s like"
2011-03-30 01:54:15 +04:00
warnMsg += " '%s' " % unsafeSQLIdentificatorNaming(tbl)
warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(db)
logger.warn(warnMsg)
continue
2012-02-16 18:42:28 +04:00
indexRange = getLimitRange(count)
for index in indexRange:
2012-02-01 16:53:07 +04:00
query = rootQuery.blind.query
query = query.replace("%s", db)
query += " AND %s" % tblQuery
query = agent.limitQuery(index, query, tblCond)
tbl = inject.getValue(query, inband=False, error=False)
kb.hintValue = tbl
foundTbls[db].append(tbl)
for db, tbls in foundTbls.items():
if len(tbls) == 0:
foundTbls.pop(db)
if not foundTbls:
warnMsg = "no databases contain any of the provided tables"
logger.warn(warnMsg)
return
conf.dumper.dbTables(foundTbls)
self.dumpFoundTables(foundTbls)
def searchColumn(self):
rootQuery = queries[Backend.getIdentifiedDbms()].search_column
foundCols = {}
dbs = {}
whereTblsQuery = ""
infoMsgTbl = ""
infoMsgDb = ""
colList = conf.col.split(",")
origTbl = conf.tbl
origDb = conf.db
colCond = rootQuery.inband.condition
tblCond = rootQuery.inband.condition2
colConsider, colCondParam = self.likeOrExact("column")
if conf.db and conf.db != CURRENT_DB:
enumDbs = conf.db.split(",")
elif not len(kb.data.cachedDbs):
enumDbs = self.getDbs()
else:
enumDbs = kb.data.cachedDbs
for db in enumDbs:
2011-03-30 01:54:15 +04:00
db = safeSQLIdentificatorNaming(db)
dbs[db] = {}
for column in colList:
2011-03-30 01:54:15 +04:00
column = safeSQLIdentificatorNaming(column)
conf.db = origDb
conf.tbl = origTbl
infoMsg = "searching column"
if colConsider == "1":
infoMsg += "s like"
2011-03-30 01:54:15 +04:00
infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(column)
foundCols[column] = {}
if conf.tbl:
_ = conf.tbl.split(",")
whereTblsQuery = " AND (" + " OR ".join("%s = '%s'" % (tblCond, unsafeSQLIdentificatorNaming(tbl)) for tbl in _) + ")"
infoMsgTbl = " for table%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(tbl for tbl in _))
if conf.db and conf.db != CURRENT_DB:
_ = conf.db.split(",")
infoMsgDb = " in database%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(db for db in _))
elif conf.excludeSysDbs:
infoMsg2 = "skipping system database%s '%s'" % ("s" if len(self.excludeDbsList) > 1 else "", ", ".join(db for db in self.excludeDbsList))
logger.info(infoMsg2)
else:
infoMsgDb = " across all databases"
logger.info("%s%s%s" % (infoMsg, infoMsgTbl, infoMsgDb))
colQuery = "%s%s" % (colCond, colCondParam)
2011-03-30 01:54:15 +04:00
colQuery = colQuery % unsafeSQLIdentificatorNaming(column)
for db in dbs.keys():
2011-03-30 01:54:15 +04:00
db = safeSQLIdentificatorNaming(db)
if conf.excludeSysDbs and db in self.excludeDbsList:
continue
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR)) or conf.direct:
query = rootQuery.inband.query % (db, db, db, db, db, db)
query += " AND %s" % colQuery.replace("[DB]", db)
query += whereTblsQuery.replace("[DB]", db)
values = inject.getValue(query, blind=False)
if not isNoneValue(values):
if isinstance(values, basestring):
values = [ values ]
for foundTbl in values:
2011-03-30 01:54:15 +04:00
foundTbl = safeSQLIdentificatorNaming(foundTbl, True)
2011-02-10 14:28:24 +03:00
if foundTbl is None:
continue
if foundTbl not in dbs[db]:
dbs[db][foundTbl] = {}
if colConsider == "1":
conf.db = db
conf.tbl = foundTbl
conf.col = column
2012-03-08 19:24:05 +04:00
self.getColumns(onlyColNames=True, bruteForce=False)
2011-06-16 18:27:44 +04:00
if db in kb.data.cachedColumns and foundTbl in kb.data.cachedColumns[db]\
and not isNoneValue(kb.data.cachedColumns[db][foundTbl]):
dbs[db][foundTbl].update(kb.data.cachedColumns[db][foundTbl])
kb.data.cachedColumns = {}
else:
dbs[db][foundTbl][column] = None
if db in foundCols[column]:
foundCols[column][db].append(foundTbl)
else:
foundCols[column][db] = [ foundTbl ]
else:
foundCols[column][db] = []
infoMsg = "fetching number of tables containing column"
if colConsider == "1":
infoMsg += "s like"
infoMsg += " '%s' in database '%s'" % (column, db)
logger.info("%s%s" % (infoMsg, infoMsgTbl))
2012-02-01 16:53:07 +04:00
query = rootQuery.blind.count
query = query % (db, db, db, db, db, db)
query += " AND %s" % colQuery.replace("[DB]", db)
query += whereTblsQuery.replace("[DB]", db)
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
2010-12-18 00:45:20 +03:00
if not isNumPosStrValue(count):
warnMsg = "no tables contain column"
if colConsider == "1":
warnMsg += "s like"
warnMsg += " '%s' " % column
warnMsg += "in database '%s'" % db
logger.warn(warnMsg)
continue
2012-02-16 18:42:28 +04:00
indexRange = getLimitRange(count)
for index in indexRange:
2012-02-01 16:53:07 +04:00
query = rootQuery.blind.query
query = query % (db, db, db, db, db, db)
query += " AND %s" % colQuery.replace("[DB]", db)
query += whereTblsQuery.replace("[DB]", db)
query = agent.limitQuery(index, query, colCond.replace("[DB]", db))
tbl = inject.getValue(query, inband=False, error=False)
kb.hintValue = tbl
2011-03-30 01:54:15 +04:00
tbl = safeSQLIdentificatorNaming(tbl, True)
if tbl not in dbs[db]:
dbs[db][tbl] = {}
if colConsider == "1":
conf.db = db
conf.tbl = tbl
conf.col = column
2012-03-08 19:24:05 +04:00
self.getColumns(onlyColNames=True, bruteForce=False)
2011-06-16 18:27:44 +04:00
if db in kb.data.cachedColumns and tbl in kb.data.cachedColumns[db]:
dbs[db][tbl].update(kb.data.cachedColumns[db][tbl])
kb.data.cachedColumns = {}
else:
dbs[db][tbl][column] = None
foundCols[column][db].append(tbl)
conf.dumper.dbColumns(foundCols, colConsider, dbs)
self.dumpFoundColumn(dbs, foundCols, colConsider)