From d5eeb91b3548266bcd050deed3bed24cd0371351 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Sat, 30 Apr 2011 22:11:36 +0000 Subject: [PATCH] Aligned Sybase and MaxDB to recent enhancements to --dbs, --tables and --columns --- plugins/dbms/maxdb/enumeration.py | 156 ++++++++++------- plugins/dbms/sybase/enumeration.py | 265 ++++++++++++++++------------- 2 files changed, 247 insertions(+), 174 deletions(-) diff --git a/plugins/dbms/maxdb/enumeration.py b/plugins/dbms/maxdb/enumeration.py index f7e0880cb..4245f9e81 100644 --- a/plugins/dbms/maxdb/enumeration.py +++ b/plugins/dbms/maxdb/enumeration.py @@ -10,12 +10,15 @@ See the file 'doc/COPYING' for copying permission from lib.core.common import Backend from lib.core.common import isTechniqueAvailable from lib.core.common import randomStr +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 PAYLOAD from lib.core.exception import sqlmapMissingMandatoryOptionException +from lib.core.exception import sqlmapNoneDataException from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): @@ -30,70 +33,46 @@ class Enumeration(GenericEnumeration): return {} - def searchDb(self): - warnMsg = "on SAP MaxDB it is not possible to search databases" - logger.warn(warnMsg) + def getDbs(self): + if len(kb.data.cachedDbs) > 0: + return kb.data.cachedDbs - return [] - - def getColumns(self, onlyColNames=False): - if not conf.tbl: - warnMsg = "missing table parameter, sqlmap will enumerate " - warnMsg += "the whole database management system schema" - logger.warn(warnMsg) - - return self.getSchema() - - if "." in conf.tbl: - conf.db, conf.tbl = conf.tbl.split(".") - - self.forceDbmsEnum() - - rootQuery = queries[Backend.getIdentifiedDbms()].columns - - infoMsg = "fetching columns " - infoMsg += "for table '%s' " % conf.tbl - if conf.db: - infoMsg += "on schema '%s'" % conf.db + infoMsg = "fetching database names" logger.info(infoMsg) + rootQuery = queries[Backend.getIdentifiedDbms()].dbs randStr = randomStr() - query = rootQuery.inband.query % (conf.tbl, ("'%s'" % conf.db) if conf.db != "USER" else 'USER') - retVal = self.__pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.columnname' % randStr,'%s.datatype' % randStr,'%s.len' % randStr], blind=True) + query = rootQuery.inband.query + retVal = self.__pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.schemaname' % randStr], blind=True) if retVal: - table = {} - columns = {} + kb.data.cachedDbs = retVal[0].values()[0] - for columnname, datatype, length in zip(retVal[0]["%s.columnname" % randStr], retVal[0]["%s.datatype" % randStr], retVal[0]["%s.len" % randStr]): - columns[columnname] = "%s(%s)" % (datatype, length) - - table[conf.tbl] = columns - kb.data.cachedColumns[conf.db] = table - - return kb.data.cachedColumns + return kb.data.cachedDbs def getTables(self, bruteForce=None): + if len(kb.data.cachedTables) > 0: + return kb.data.cachedTables + self.forceDbmsEnum() - infoMsg = "fetching tables" + if conf.db == "CD": + conf.db = self.getCurrentDb() + if conf.db: - infoMsg += " for schema '%s'" % conf.db + dbs = conf.db.split(",") + else: + dbs = self.getDbs() + + for db in dbs: + dbs[dbs.index(db)] = safeSQLIdentificatorNaming(db) + + infoMsg = "fetching tables for database" + infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db for db in dbs)) logger.info(infoMsg) rootQuery = queries[Backend.getIdentifiedDbms()].tables - if conf.db: - if "," in conf.db: - dbs = conf.db.split(",") - else: - dbs = [conf.db] - else: - if not len(kb.data.cachedDbs): - dbs = self.getDbs() - else: - dbs = kb.data.cachedDbs - for db in dbs: randStr = randomStr() query = rootQuery.inband.query % (("'%s'" % db) if db != "USER" else 'USER') @@ -108,18 +87,79 @@ class Enumeration(GenericEnumeration): return kb.data.cachedTables - def getDbs(self): - infoMsg = "fetching database names" - logger.info(infoMsg) + def getColumns(self, onlyColNames=False): + self.forceDbmsEnum() - rootQuery = queries[Backend.getIdentifiedDbms()].dbs + if conf.db is None or conf.db == "CD": + if conf.db is None: + warnMsg = "missing database parameter, sqlmap is going " + warnMsg += "to use the current database to enumerate " + warnMsg += "table(s) columns" + logger.warn(warnMsg) - randStr = randomStr() - query = rootQuery.inband.query + conf.db = self.getCurrentDb() - retVal = self.__pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.schemaname' % randStr], blind=True) + elif conf.db is not None: + if ',' in conf.db: + errMsg = "only one database name is allowed when enumerating " + errMsg += "the tables' columns" + raise sqlmapMissingMandatoryOptionException, errMsg - if retVal: - kb.data.cachedDbs = retVal[0].values()[0] + conf.db = safeSQLIdentificatorNaming(conf.db) - return kb.data.cachedDbs + if conf.tbl: + tblList = conf.tbl.split(",") + else: + self.getTables() + + if len(kb.data.cachedTables) > 0: + tblList = kb.data.cachedTables.values() + + if isinstance(tblList[0], (set, tuple, list)): + tblList = tblList[0] + else: + errMsg = "unable to retrieve the tables" + errMsg += "on database '%s'" % conf.db + raise sqlmapNoneDataException, errMsg + + for tbl in tblList: + tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl) + + rootQuery = queries[Backend.getIdentifiedDbms()].columns + + for tbl in tblList: + if conf.db is not None and len(kb.data.cachedColumns) > 0 \ + and conf.db in kb.data.cachedColumns and tbl in \ + kb.data.cachedColumns[conf.db]: + infoMsg = "fetched tables' columns on " + infoMsg += "database '%s'" % conf.db + logger.info(infoMsg) + + return { conf.db: kb.data.cachedColumns[conf.db]} + + infoMsg = "fetching columns " + infoMsg += "for table '%s' " % tbl + infoMsg += "on database '%s'" % conf.db + logger.info(infoMsg) + + randStr = randomStr() + query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), ("'%s'" % unsafeSQLIdentificatorNaming(conf.db)) if unsafeSQLIdentificatorNaming(conf.db) != "USER" else 'USER') + retVal = self.__pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.columnname' % randStr,'%s.datatype' % randStr,'%s.len' % randStr], blind=True) + + if retVal: + table = {} + columns = {} + + for columnname, datatype, length in zip(retVal[0]["%s.columnname" % randStr], retVal[0]["%s.datatype" % randStr], retVal[0]["%s.len" % randStr]): + columns[columnname] = "%s(%s)" % (datatype, length) + + table[tbl] = columns + kb.data.cachedColumns[conf.db] = table + + return kb.data.cachedColumns + + def searchDb(self): + warnMsg = "on SAP MaxDB it is not possible to search databases" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/sybase/enumeration.py b/plugins/dbms/sybase/enumeration.py index 3867a7581..eb45a673e 100644 --- a/plugins/dbms/sybase/enumeration.py +++ b/plugins/dbms/sybase/enumeration.py @@ -10,6 +10,8 @@ See the file 'doc/COPYING' for copying permission from lib.core.common import Backend from lib.core.common import isTechniqueAvailable from lib.core.common import randomStr +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 @@ -17,6 +19,7 @@ from lib.core.data import queries from lib.core.dicts import sybaseTypes from lib.core.enums import PAYLOAD from lib.core.exception import sqlmapMissingMandatoryOptionException +from lib.core.exception import sqlmapNoneDataException from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): @@ -46,122 +49,6 @@ class Enumeration(GenericEnumeration): return kb.data.cachedUsers - def getColumns(self, onlyColNames=False): - if not conf.tbl: - warnMsg = "missing table parameter, sqlmap will enumerate " - warnMsg += "the whole database management system schema" - logger.warn(warnMsg) - - return self.getSchema() - - if "." in conf.tbl: - conf.db, conf.tbl = conf.tbl.split(".") - - self.forceDbmsEnum() - - if not conf.db: - warnMsg = "missing database parameter, sqlmap is going to " - warnMsg += "use the current database to enumerate table " - warnMsg += "'%s' columns" % conf.tbl - logger.warn(warnMsg) - - conf.db = self.getCurrentDb() - rootQuery = queries[Backend.getIdentifiedDbms()].columns - - infoMsg = "fetching columns " - infoMsg += "for table '%s' " % conf.tbl - infoMsg += "on database '%s'" % conf.db - logger.info(infoMsg) - - if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct: - blinds = [False, True] - else: - blinds = [True] - - for blind in blinds: - randStr = randomStr() - query = rootQuery.inband.query % (conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, conf.tbl) - retVal = self.__pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.name' % randStr,'%s.usertype' % randStr], blind=blind) - - if retVal: - table = {} - columns = {} - - for name, type_ in zip(retVal[0]["%s.name" % randStr], retVal[0]["%s.usertype" % randStr]): - columns[name] = sybaseTypes[type_] if type_ else None - - table[conf.tbl] = columns - kb.data.cachedColumns[conf.db] = table - - break - - return kb.data.cachedColumns - - def getTables(self, bruteForce=None): - self.forceDbmsEnum() - - infoMsg = "fetching tables" - if conf.db: - infoMsg += " for database '%s'" % conf.db - logger.info(infoMsg) - - rootQuery = queries[Backend.getIdentifiedDbms()].tables - - if conf.db: - if "," in conf.db: - dbs = conf.db.split(",") - else: - dbs = [conf.db] - else: - if not len(kb.data.cachedDbs): - dbs = self.getDbs() - else: - dbs = kb.data.cachedDbs - - if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct: - blinds = [False, True] - else: - blinds = [True] - - for db in dbs: - for blind in blinds: - randStr = randomStr() - query = rootQuery.inband.query % db - retVal = self.__pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.name' % randStr], blind=blind) - - if retVal: - for table in retVal[0].values()[0]: - if not kb.data.cachedTables.has_key(db): - kb.data.cachedTables[db] = [table] - else: - kb.data.cachedTables[db].append(table) - break - - return kb.data.cachedTables - - def getDbs(self): - infoMsg = "fetching database names" - logger.info(infoMsg) - - rootQuery = queries[Backend.getIdentifiedDbms()].dbs - - randStr = randomStr() - query = rootQuery.inband.query - - if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct: - blinds = [False, True] - else: - blinds = [True] - - for blind in blinds: - retVal = self.__pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.name' % randStr], blind=blind) - - if retVal: - kb.data.cachedDbs = retVal[0].values()[0] - break - - return kb.data.cachedDbs - def getPrivileges(self, *args): warnMsg = "on Sybase it is not possible to fetch " warnMsg += "database users privileges, sqlmap will check whether " @@ -191,6 +78,152 @@ class Enumeration(GenericEnumeration): return ( kb.data.cachedUsersPrivileges, areAdmins ) + def getDbs(self): + if len(kb.data.cachedDbs) > 0: + return kb.data.cachedDbs + + infoMsg = "fetching database names" + logger.info(infoMsg) + + rootQuery = queries[Backend.getIdentifiedDbms()].dbs + randStr = randomStr() + query = rootQuery.inband.query + + if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct: + blinds = [False, True] + else: + blinds = [True] + + for blind in blinds: + retVal = self.__pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.name' % randStr], blind=blind) + + if retVal: + kb.data.cachedDbs = retVal[0].values()[0] + break + + return kb.data.cachedDbs + + def getTables(self, bruteForce=None): + if len(kb.data.cachedTables) > 0: + return kb.data.cachedTables + + self.forceDbmsEnum() + + if conf.db == "CD": + 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) + + infoMsg = "fetching tables for database" + infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db for db in dbs)) + logger.info(infoMsg) + + if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct: + blinds = [False, True] + else: + blinds = [True] + + for db in dbs: + for blind in blinds: + randStr = randomStr() + query = rootQuery.inband.query % db + retVal = self.__pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.name' % randStr], blind=blind) + + if retVal: + for table in retVal[0].values()[0]: + if not kb.data.cachedTables.has_key(db): + kb.data.cachedTables[db] = [table] + else: + kb.data.cachedTables[db].append(table) + break + + return kb.data.cachedTables + + def getColumns(self, onlyColNames=False): + self.forceDbmsEnum() + + if conf.db is None or conf.db == "CD": + if conf.db is None: + warnMsg = "missing database parameter, sqlmap is going " + warnMsg += "to use the current database to enumerate " + warnMsg += "table(s) columns" + logger.warn(warnMsg) + + conf.db = self.getCurrentDb() + + elif conf.db is not None: + if ',' in conf.db: + errMsg = "only one database name is allowed when enumerating " + errMsg += "the tables' columns" + raise sqlmapMissingMandatoryOptionException, errMsg + + conf.db = safeSQLIdentificatorNaming(conf.db) + + if conf.tbl: + tblList = conf.tbl.split(",") + else: + self.getTables() + + if len(kb.data.cachedTables) > 0: + tblList = kb.data.cachedTables.values() + + if isinstance(tblList[0], (set, tuple, list)): + tblList = tblList[0] + else: + errMsg = "unable to retrieve the tables" + errMsg += "on database '%s'" % conf.db + raise sqlmapNoneDataException, errMsg + + for tbl in tblList: + tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl) + + rootQuery = queries[Backend.getIdentifiedDbms()].columns + + if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct: + blinds = [False, True] + else: + blinds = [True] + + for tbl in tblList: + if conf.db is not None and len(kb.data.cachedColumns) > 0 \ + and conf.db in kb.data.cachedColumns and tbl in \ + kb.data.cachedColumns[conf.db]: + infoMsg = "fetched tables' columns on " + infoMsg += "database '%s'" % conf.db + logger.info(infoMsg) + + return { conf.db: kb.data.cachedColumns[conf.db]} + + infoMsg = "fetching columns " + infoMsg += "for table '%s' " % tbl + infoMsg += "on database '%s'" % conf.db + logger.info(infoMsg) + + for blind in blinds: + randStr = randomStr() + query = rootQuery.inband.query % (conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl)) + retVal = self.__pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.name' % randStr,'%s.usertype' % randStr], blind=blind) + + if retVal: + table = {} + columns = {} + + for name, type_ in zip(retVal[0]["%s.name" % randStr], retVal[0]["%s.usertype" % randStr]): + columns[name] = sybaseTypes[type_] if type_ else None + + table[tbl] = columns + kb.data.cachedColumns[conf.db] = table + + break + + return kb.data.cachedColumns + def searchDb(self): warnMsg = "on Sybase searching of databases is not implemented" logger.warn(warnMsg)