From 6e392b6054b4b829b5152bcd6da2d1976f5d06e7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 6 May 2011 09:30:39 +0000 Subject: [PATCH] applying contributed patch for DB2 --- lib/controller/handler.py | 6 +- lib/core/agent.py | 6 +- lib/core/common.py | 4 +- lib/core/data.py | 1 + lib/core/dicts.py | 11 +++ lib/core/enums.py | 1 + lib/core/option.py | 3 +- lib/core/settings.py | 8 ++- plugins/dbms/db2/__init__.py | 36 ++++++++++ plugins/dbms/db2/connector.py | 31 +++++++++ plugins/dbms/db2/enumeration.py | 22 ++++++ plugins/dbms/db2/filesystem.py | 23 +++++++ plugins/dbms/db2/fingerprint.py | 114 ++++++++++++++++++++++++++++++++ plugins/dbms/db2/syntax.py | 72 ++++++++++++++++++++ plugins/dbms/db2/takeover.py | 32 +++++++++ plugins/generic/enumeration.py | 98 +++++++++++++++++++++------ xml/queries.xml | 67 +++++++++++++++++++ 17 files changed, 505 insertions(+), 30 deletions(-) create mode 100644 plugins/dbms/db2/__init__.py create mode 100644 plugins/dbms/db2/connector.py create mode 100644 plugins/dbms/db2/enumeration.py create mode 100644 plugins/dbms/db2/filesystem.py create mode 100644 plugins/dbms/db2/fingerprint.py create mode 100644 plugins/dbms/db2/syntax.py create mode 100644 plugins/dbms/db2/takeover.py diff --git a/lib/controller/handler.py b/lib/controller/handler.py index 7b21df61b..c70397df1 100644 --- a/lib/controller/handler.py +++ b/lib/controller/handler.py @@ -23,6 +23,7 @@ from lib.core.settings import ACCESS_ALIASES from lib.core.settings import FIREBIRD_ALIASES from lib.core.settings import MAXDB_ALIASES from lib.core.settings import SYBASE_ALIASES +from lib.core.settings import DB2_ALIASES from plugins.dbms.mssqlserver import MSSQLServerMap from plugins.dbms.mssqlserver.connector import Connector as MSSQLServerConn @@ -42,6 +43,8 @@ from plugins.dbms.maxdb import MaxDBMap from plugins.dbms.maxdb.connector import Connector as MaxDBConn from plugins.dbms.sybase import SybaseMap from plugins.dbms.sybase.connector import Connector as SybaseConn +from plugins.dbms.db2 import DB2Map +from plugins.dbms.db2.connector import Connector as DB2Conn def setHandler(): """ @@ -50,7 +53,7 @@ def setHandler(): """ count = 0 - dbmsNames = ( "MySQL", "Oracle", "PostgreSQL", "Microsoft SQL Server", "SQLite", "Microsoft Access", "Firebird", "SAP MaxDB", "Sybase" ) + dbmsNames = ( "MySQL", "Oracle", "PostgreSQL", "Microsoft SQL Server", "SQLite", "Microsoft Access", "Firebird", "SAP MaxDB", "Sybase", "DB2" ) dbmsObj = [ ( MYSQL_ALIASES, MySQLMap, MySQLConn ), ( ORACLE_ALIASES, OracleMap, OracleConn ), @@ -61,6 +64,7 @@ def setHandler(): ( FIREBIRD_ALIASES, FirebirdMap, FirebirdConn ), ( MAXDB_ALIASES, MaxDBMap, MaxDBConn ), ( SYBASE_ALIASES, SybaseMap, SybaseConn ), + ( DB2_ALIASES, DB2Map, DB2Conn ) ] if Backend.getIdentifiedDbms() is not None: diff --git a/lib/core/agent.py b/lib/core/agent.py index 143657685..253f73adf 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -407,7 +407,7 @@ class Agent: if Backend.isDbms(DBMS.MYSQL): concatenatedQuery = "CONCAT(%s,%s)" % (query1, query2) - elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE): + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE, DBMS.DB2): concatenatedQuery = "%s||%s" % (query1, query2) elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): @@ -466,7 +466,7 @@ class Agent: elif fieldsNoSelect: concatenatedQuery = "CONCAT('%s',%s,'%s')" % (kb.misc.start, concatenatedQuery, kb.misc.stop) - elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE): + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE, DBMS.DB2): if fieldsExists: concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.misc.start, 1) concatenatedQuery += "||'%s'" % kb.misc.stop @@ -643,7 +643,7 @@ class Agent: limitStr = queries[Backend.getIdentifiedDbms()].limit.query % (num+1, num+1) limitedQuery += " %s" % limitStr - elif Backend.isDbms(DBMS.ORACLE): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): if " ORDER BY " in limitedQuery and "(SELECT " in limitedQuery: orderBy = limitedQuery[limitedQuery.index(" ORDER BY "):] limitedQuery = limitedQuery[:limitedQuery.index(" ORDER BY ")] diff --git a/lib/core/common.py b/lib/core/common.py index 61453bd53..44aa1e21a 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2546,7 +2546,7 @@ def safeSQLIdentificatorNaming(name, isTable=False): if not re.match(r"\A[A-Za-z0-9_]+\Z", parts[i]): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS): parts[i] = "`%s`" % parts[i].strip("`") - elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.ORACLE, DBMS.PGSQL): + elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.ORACLE, DBMS.PGSQL, DBMS.DB2): parts[i] = "\"%s\"" % parts[i].strip("\"") retVal = ".".join(parts) @@ -2563,7 +2563,7 @@ def unsafeSQLIdentificatorNaming(name): if isinstance(name, basestring): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS): retVal = name.replace("`", "") - elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.ORACLE, DBMS.PGSQL): + elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.ORACLE, DBMS.PGSQL, DBMS.DB2): retVal = name.replace("\"", "") if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): prefix = "%s." % DEFAULT_MSSQL_SCHEMA diff --git a/lib/core/data.py b/lib/core/data.py index 716948a23..110b97f26 100644 --- a/lib/core/data.py +++ b/lib/core/data.py @@ -19,6 +19,7 @@ from lib.core.settings import ACCESS_ALIASES from lib.core.settings import FIREBIRD_ALIASES from lib.core.settings import MAXDB_ALIASES from lib.core.settings import SYBASE_ALIASES +from lib.core.settings import DB2_ALIASES # sqlmap paths paths = advancedDict() diff --git a/lib/core/dicts.py b/lib/core/dicts.py index 4582911f0..3aee461b4 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -98,3 +98,14 @@ firebirdPrivs = { "R": "REFERENCES", "E": "EXECUTE" } + +db2Privs = { + 1:"CONTROLAUTH", + 2:"ALTERAUTH", + 3:"DELETEAUTH", + 4:"INDEXAUTH", + 5:"INSERTAUTH", + 6:"REFAUTH", + 7:"SELECTAUTH", + 8:"UPDATEAUTH" + } \ No newline at end of file diff --git a/lib/core/enums.py b/lib/core/enums.py index f0366b5a4..6cae3e396 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -34,6 +34,7 @@ class DBMS: PGSQL = "PostgreSQL" SQLITE = "SQLite" SYBASE = "Sybase" + DB2 = "IBM DB2" class OS: LINUX = "Linux" diff --git a/lib/core/option.py b/lib/core/option.py index 7034fc671..0dfba201f 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -89,6 +89,7 @@ from lib.core.settings import ACCESS_ALIASES from lib.core.settings import FIREBIRD_ALIASES from lib.core.settings import MAXDB_ALIASES from lib.core.settings import SYBASE_ALIASES +from lib.core.settings import DB2_ALIASES from lib.core.settings import BURP_SPLITTER from lib.core.settings import MAX_NUMBER_OF_THREADS from lib.core.settings import TIME_DEFAULT_DELAY @@ -682,7 +683,7 @@ def __setDBMS(): for aliases in (MSSQL_ALIASES, MYSQL_ALIASES, PGSQL_ALIASES, \ ORACLE_ALIASES, SQLITE_ALIASES, ACCESS_ALIASES, \ - FIREBIRD_ALIASES, MAXDB_ALIASES, SYBASE_ALIASES): + FIREBIRD_ALIASES, MAXDB_ALIASES, SYBASE_ALIASES, DB2_ALIASES): if conf.dbms in aliases: conf.dbms = aliases[0] diff --git a/lib/core/settings.py b/lib/core/settings.py index 24eba1c2e..bb6a5c5e0 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -147,6 +147,8 @@ FIREBIRD_SYSTEM_DBS = ( "RDB$BACKUP_HISTORY", "RDB$CHARACTER_SETS", "RDB$CHECK_C "RDB$TRIGGER_MESSAGES", "RDB$TYPES", "RDB$USER_PRIVILEGES", "RDB$VIEW_RELATIONS" ) MAXDB_SYSTEM_DBS = ( "SYSINFO", "DOMAIN" ) SYBASE_SYSTEM_DBS = ( "master", "model", "sybsystemdb", "sybsystemprocs" ) +DB2_SYSTEM_DBS = ( "NULLID", "SQLJ", "SYSCAT", "SYSFUN", "SYSIBM", "SYSIBMADM", "SYSIBMINTERNAL", "SYSIBMTS", "SYSPROC", "SYSPUBLIC",\ + "SYSSTAT", "SYSTOOLS" ) MSSQL_ALIASES = [ "microsoft sql server", "mssqlserver", "mssql", "ms" ] MYSQL_ALIASES = [ "mysql", "my" ] @@ -157,8 +159,9 @@ ACCESS_ALIASES = [ "access", "jet", "microsoft access", "msaccess" ] FIREBIRD_ALIASES = [ "firebird", "mozilla firebird", "interbase", "ibase", "fb" ] MAXDB_ALIASES = [ "maxdb", "sap maxdb", "sap db" ] SYBASE_ALIASES = [ "sybase", "sybase sql server" ] +DB2_ALIASES = [ "ibm db2", "db2" ] -SUPPORTED_DBMS = MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES + SQLITE_ALIASES + ACCESS_ALIASES + FIREBIRD_ALIASES + MAXDB_ALIASES + SYBASE_ALIASES +SUPPORTED_DBMS = MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES + SQLITE_ALIASES + ACCESS_ALIASES + FIREBIRD_ALIASES + MAXDB_ALIASES + SYBASE_ALIASES + DB2_ALIASES SUPPORTED_OS = ( "linux", "windows" ) DBMS_DICT = { DBMS.MSSQL: [MSSQL_ALIASES, "python-pymssql", "http://pymssql.sourceforge.net/"], @@ -169,7 +172,8 @@ DBMS_DICT = { DBMS.MSSQL: [MSSQL_ALIASES, "python-pymssql", "http://pymssql.sour DBMS.ACCESS: [ACCESS_ALIASES, "python-pyodbc", "http://pyodbc.googlecode.com/"], DBMS.FIREBIRD: [FIREBIRD_ALIASES, "python-kinterbasdb", "http://kinterbasdb.sourceforge.net/"], DBMS.MAXDB: [MAXDB_ALIASES, None, None], - DBMS.SYBASE: [SYBASE_ALIASES, "python-pymssql", "http://pymssql.sourceforge.net/"] + DBMS.SYBASE: [SYBASE_ALIASES, "python-pymssql", "http://pymssql.sourceforge.net/"], + DBMS.DB2: [DB2_ALIASES, None, None] } REFERER_ALIASES = ( "ref", "referer", "referrer" ) diff --git a/plugins/dbms/db2/__init__.py b/plugins/dbms/db2/__init__.py new file mode 100644 index 000000000..2dbbf0540 --- /dev/null +++ b/plugins/dbms/db2/__init__.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +""" +$Id: __init__.py 3678 2011-04-15 12:33:18Z stamparm $ + +Copyright (c) 2006-2011 sqlmap developers (http://sqlmap.sourceforge.net/) +See the file 'doc/COPYING' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import DB2_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.db2.enumeration import Enumeration +from plugins.dbms.db2.filesystem import Filesystem +from plugins.dbms.db2.fingerprint import Fingerprint +from plugins.dbms.db2.syntax import Syntax +from plugins.dbms.db2.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class DB2Map(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines DB2 methods + """ + + def __init__(self): + self.excludeDbsList = DB2_SYSTEM_DBS + + Syntax.__init__(self) + Fingerprint.__init__(self) + Enumeration.__init__(self) + Filesystem.__init__(self) + Miscellaneous.__init__(self) + Takeover.__init__(self) + + unescaper[DBMS.DB2] = Syntax.unescape diff --git a/plugins/dbms/db2/connector.py b/plugins/dbms/db2/connector.py new file mode 100644 index 000000000..8e51ce880 --- /dev/null +++ b/plugins/dbms/db2/connector.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +""" +$Id: connector.py 3678 2011-04-15 12:33:18Z stamparm $ + +Copyright (c) 2006-2011 sqlmap developers (http://sqlmap.sourceforge.net/) +See the file 'doc/COPYING' for copying permission +""" + +try: + import pyodbc +except ImportError, _: + pass + +from lib.core.data import logger +from lib.core.exception import sqlmapConnectionException +from lib.core.exception import sqlmapUnsupportedFeatureException + +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: http://pyodbc.googlecode.com/ + User guide: http://code.google.com/p/pyodbc/wiki/GettingStarted + API: http://code.google.com/p/pyodbc/w/list + Debian package: python-pyodbc + License: MIT + """ + + def __init__(self): + GenericConnector.__init__(self) \ No newline at end of file diff --git a/plugins/dbms/db2/enumeration.py b/plugins/dbms/db2/enumeration.py new file mode 100644 index 000000000..0a44f7c04 --- /dev/null +++ b/plugins/dbms/db2/enumeration.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +$Id: enumeration.py 3678 2011-04-15 12:33:18Z stamparm $ + +Copyright (c) 2006-2011 sqlmap developers (http://sqlmap.sourceforge.net/) +See the file 'doc/COPYING' for copying permission +""" + + +from lib.core.data import logger +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def __init__(self): + GenericEnumeration.__init__(self) + + def getPasswordHashes(self): + warnMsg = "on DB2 it is not possible to list password hashes" + logger.warn(warnMsg) + + return {} \ No newline at end of file diff --git a/plugins/dbms/db2/filesystem.py b/plugins/dbms/db2/filesystem.py new file mode 100644 index 000000000..f140d0ab4 --- /dev/null +++ b/plugins/dbms/db2/filesystem.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +""" +$Id: filesystem.py 3678 2011-04-15 12:33:18Z stamparm $ + +Copyright (c) 2006-2011 sqlmap developers (http://sqlmap.sourceforge.net/) +See the file 'doc/COPYING' for copying permission +""" + +from lib.core.common import randomStr +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger +from lib.core.enums import PLACE +from lib.core.exception import sqlmapNoneDataException +from lib.request import inject +from lib.techniques.inband.union.use import unionUse + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + def __init__(self): + GenericFilesystem.__init__(self) diff --git a/plugins/dbms/db2/fingerprint.py b/plugins/dbms/db2/fingerprint.py new file mode 100644 index 000000000..3ad3335fd --- /dev/null +++ b/plugins/dbms/db2/fingerprint.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python + +""" +$Id: fingerprint.py 3678 2011-04-15 12:33:18Z stamparm $ + +Copyright (c) 2006-2011 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 Format +from lib.core.common import randomInt +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger +from lib.core.enums import DBMS +from lib.core.session import setDbms +from lib.core.settings import DB2_ALIASES +from lib.request import inject + +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.DB2) + + def versionCheck(self): + minor, major = None, None + + for version in reversed(xrange(5, 15)): + result = inject.checkBooleanExpression("(SELECT COUNT(*) FROM sysibm.sysversions WHERE versionnumber BETWEEN %d000000 AND %d999999)>0" % (version, version)) + if result: + major = version + + for version in reversed(xrange(0, 20)): + result = inject.checkBooleanExpression("(SELECT COUNT(*) FROM sysibm.sysversions WHERE versionnumber BETWEEN %d%02d0000 AND %d%02d9999)>0" % (major, version, major, version)) + if result: + minor = version + version = "%s.%s" % (major, minor) + break + break + + if major and minor: + return "%s.%s" % (major, minor) + else: + return None + + def getFingerprint(self): + value = "" + wsOsFp = Format.getOs("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += DBMS.DB2 + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = Format.getErrorParsedDBMSes() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + if not conf.extensiveFp and (Backend.isDbmsWithin(DB2_ALIASES) or conf.dbms in DB2_ALIASES): + setDbms(DBMS.DB2) + + return True + + logMsg = "testing %s" % DBMS.DB2 + logger.info(logMsg) + + randInt = randomInt() + result = inject.checkBooleanExpression("(SELECT %d FROM sysibm.sysdummy1) = %d" % (randInt, randInt)) + + if result: + logMsg = "confirming %s" % DBMS.DB2 + logger.info(logMsg) + + version = self.versionCheck() + + if version: + Backend.setVersion(version) + setDbms("%s %s" % (DBMS.DB2, Backend.getVersion())) + else: + setDbms(DBMS.DB2) + + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.DB2 + logger.warn(warnMsg) + + return False \ No newline at end of file diff --git a/plugins/dbms/db2/syntax.py b/plugins/dbms/db2/syntax.py new file mode 100644 index 000000000..517347741 --- /dev/null +++ b/plugins/dbms/db2/syntax.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python + +""" +$Id: syntax.py 3678 2011-04-15 12:33:18Z stamparm $ + +Copyright (c) 2006-2011 sqlmap developers (http://sqlmap.sourceforge.net/) +See the file 'doc/COPYING' for copying permission +""" + +from lib.core.data import logger +from lib.core.exception import sqlmapSyntaxException + +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + def __init__(self): + GenericSyntax.__init__(self) + + @staticmethod + def unescape(expression, quote=True): + if quote: + while True: + index = expression.find("'") + if index == -1: + break + + firstIndex = index + 1 + index = expression[firstIndex:].find("'") + + if index == -1: + raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression + + lastIndex = firstIndex + index + old = "'%s'" % expression[firstIndex:lastIndex] + unescaped = "" + + for i in range(firstIndex, lastIndex): + unescaped += "CHR(%d)" % (ord(expression[i])) + if i < lastIndex - 1: + unescaped += "||" + + expression = expression.replace(old, unescaped) + else: + expression = "||".join("CHR(%d)" % ord(c) for c in expression) + + return expression + + @staticmethod + def escape(expression): + logMsg = "escaping %s" % expression + logger.info(logMsg) + while True: + index = expression.find("CHR(") + if index == -1: + break + + firstIndex = index + index = expression[firstIndex:].find(")") + + if index == -1: + raise sqlmapSyntaxException, "Unenclosed ) in '%s'" % expression + + lastIndex = firstIndex + index + 1 + old = expression[firstIndex:lastIndex] + oldUpper = old.upper() + oldUpper = oldUpper.lstrip("CHR(").rstrip(")") + oldUpper = oldUpper.split("||") + + escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper]) + expression = expression.replace(old, escaped) + + return expression diff --git a/plugins/dbms/db2/takeover.py b/plugins/dbms/db2/takeover.py new file mode 100644 index 000000000..041b0ee40 --- /dev/null +++ b/plugins/dbms/db2/takeover.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +""" +$Id: takeover.py 3678 2011-04-15 12:33:18Z stamparm $ + +Copyright (c) 2006-2011 sqlmap developers (http://sqlmap.sourceforge.net/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.agent import agent +from lib.core.common import isTechniqueAvailable +from lib.core.common import normalizePath +from lib.core.common import ntToPosixSlashes +from lib.core.common import randomStr +from lib.core.common import readInput +from lib.core.data import kb +from lib.core.data import logger +from lib.core.data import paths +from lib.core.enums import PAYLOAD +from lib.request import inject +from lib.request.connect import Connect as Request + +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def __init__(self): + self.__basedir = None + self.__datadir = None + + GenericTakeover.__init__(self) \ No newline at end of file diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py index 118ddff0e..9fa41dd6e 100644 --- a/plugins/generic/enumeration.py +++ b/plugins/generic/enumeration.py @@ -43,6 +43,7 @@ from lib.core.dicts import firebirdTypes from lib.core.dicts import mysqlPrivs from lib.core.dicts import pgsqlPrivs from lib.core.dicts import firebirdPrivs +from lib.core.dicts import db2Privs from lib.core.enums import DBMS from lib.core.enums import EXPECTED from lib.core.enums import PAYLOAD @@ -98,8 +99,14 @@ class Enumeration: infoMsg = "fetching banner" logger.info(infoMsg) - query = queries[Backend.getIdentifiedDbms()].banner.query - kb.data.banner = unArrayizeValue(inject.getValue(query, safeCharEncode=False)) + # Needed for DB2 versions < 9 + if Backend.isDbms(DBMS.DB2) and int(Backend.getVersion().split(".")[0]) < 9: + query = queries[Backend.getIdentifiedDbms()].banner.query2 + kb.data.banner = unArrayizeValue(inject.getValue(query, safeCharEncode=False)) + else: + query = queries[Backend.getIdentifiedDbms()].banner.query + kb.data.banner = unArrayizeValue(inject.getValue(query, safeCharEncode=False)) + bannerParser(kb.data.banner) if conf.os and conf.os == "windows": @@ -191,7 +198,7 @@ class Enumeration: errMsg = "unable to retrieve the number of database users" raise sqlmapNoneDataException, errMsg - if Backend.isDbms(DBMS.ORACLE): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): plusOne = True else: plusOne = False @@ -419,7 +426,7 @@ class Enumeration: logger.info(infoMsg) - if conf.user and Backend.isDbms(DBMS.ORACLE): + if conf.user and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.user = conf.user.upper() if conf.user: @@ -559,7 +566,7 @@ class Enumeration: privileges = set() - if Backend.isDbms(DBMS.ORACLE): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): plusOne = True else: plusOne = False @@ -617,6 +624,25 @@ class Enumeration: elif Backend.isDbms(DBMS.FIREBIRD): privileges.add(firebirdPrivs[privilege.strip()]) + # In DB2 we get Y or G if the privilege is + # True, N otherwise + elif Backend.isDbms(DBMS.DB2): + privs = privilege.split(",") + privilege = privs[0] + privs = privs[1] + privs = list(privs.strip()) + i = 1 + + for priv in privs: + if priv.upper() in ("Y", "G"): + for position, db2Priv in db2Privs.items(): + if position == i: + privilege += ", " + db2Priv + + i += 1 + + privileges.add(privilege) + if self.__isAdminFromPrivileges(privileges): areAdmins.add(user) @@ -665,6 +691,12 @@ class Enumeration: warnMsg += "names on other DBMSes" logger.warn(warnMsg) + if Backend.isDbms(DBMS.DB2): + warnMsg = "schema names are going to be used on DB2 " + warnMsg += "for enumeration as the counterpart to database " + warnMsg += "names on other DBMSes" + logger.warn(warnMsg) + infoMsg = "fetching database (schema) names" else: infoMsg = "fetching database names" @@ -697,7 +729,7 @@ class Enumeration: errMsg = "unable to retrieve the number of databases" logger.error(errMsg) else: - if Backend.isDbms(DBMS.ORACLE): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): plusOne = True else: plusOne = False @@ -758,7 +790,7 @@ class Enumeration: if conf.db == "CD": conf.db = self.getCurrentDb() - if conf.db and Backend.isDbms(DBMS.ORACLE): + if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.db = conf.db.upper() if conf.db: @@ -870,7 +902,7 @@ class Enumeration: tables = [] - if Backend.isDbms(DBMS.ORACLE): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): plusOne = True else: plusOne = False @@ -914,10 +946,14 @@ class Enumeration: warnMsg += "table(s) columns" logger.warn(warnMsg) - conf.db = self.getCurrentDb() + # In DB2 we use the current user as default schema (db) + if Backend.isDbms(DBMS.DB2): + conf.db = self.getCurrentUser() + else: + conf.db = self.getCurrentDb() elif conf.db is not None: - if Backend.isDbms(DBMS.ORACLE): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.db = conf.db.upper() if ',' in conf.db: @@ -928,7 +964,7 @@ class Enumeration: conf.db = safeSQLIdentificatorNaming(conf.db) if conf.col: - if Backend.isDbms(DBMS.ORACLE): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.col = conf.col.upper() colList = conf.col.split(",") @@ -939,7 +975,7 @@ class Enumeration: colList[colList.index(col)] = safeSQLIdentificatorNaming(col) if conf.tbl: - if Backend.isDbms(DBMS.ORACLE): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.tbl = conf.tbl.upper() tblList = conf.tbl.split(",") @@ -1105,7 +1141,7 @@ class Enumeration: query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery - elif Backend.isDbms(DBMS.ORACLE): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.count % unsafeSQLIdentificatorNaming(tbl.upper()) query += condQuery @@ -1144,7 +1180,7 @@ class Enumeration: query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery field = None - elif Backend.isDbms(DBMS.ORACLE): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.query % unsafeSQLIdentificatorNaming(tbl.upper()) query += condQuery field = None @@ -1166,7 +1202,7 @@ class Enumeration: if not onlyColNames: if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl), column, unsafeSQLIdentificatorNaming(conf.db)) - elif Backend.isDbms(DBMS.ORACLE): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl.upper()), column) elif Backend.isDbms(DBMS.MSSQL): query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db, @@ -1266,7 +1302,11 @@ class Enumeration: warnMsg += "number of entries for table '%s'" % conf.tbl logger.warn(warnMsg) - conf.db = self.getCurrentDb() + # In DB2 we use the current user as default schema (db) + if Backend.isDbms(DBMS.DB2): + conf.db = self.getCurrentUser() + else: + conf.db = self.getCurrentDb() self.forceDbmsEnum() @@ -1419,7 +1459,11 @@ class Enumeration: warnMsg += "'%s' entries" % conf.tbl logger.warn(warnMsg) - conf.db = self.getCurrentDb() + # In DB2 we use the current user as default schema (db) + if Backend.isDbms(DBMS.DB2): + conf.db = self.getCurrentUser() + else: + conf.db = self.getCurrentDb() rootQuery = queries[Backend.getIdentifiedDbms()].dump_table @@ -1541,7 +1585,7 @@ class Enumeration: infoMsg += "on database '%s'" % conf.db logger.info(infoMsg) - if Backend.isDbms(DBMS.ORACLE): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.count % (conf.tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), conf.tbl.upper()))) elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): query = rootQuery.blind.count % conf.tbl @@ -1581,7 +1625,7 @@ class Enumeration: entries, lengths = retVal else: - if Backend.isDbms(DBMS.ORACLE): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): plusOne = True else: plusOne = False @@ -1598,7 +1642,7 @@ class Enumeration: if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): query = rootQuery.blind.query % (column, conf.db, conf.tbl, index) - elif Backend.isDbms(DBMS.ORACLE): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.query % (column, column, conf.tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), conf.tbl.upper())), index) @@ -1774,6 +1818,9 @@ class Enumeration: for db in dbList: db = safeSQLIdentificatorNaming(db) + if Backend.isDbms(DBMS.DB2): + db = db.upper() + infoMsg = "searching database" if dbConsider == "1": infoMsg += "s like" @@ -1839,6 +1886,8 @@ class Enumeration: query = rootQuery.blind.query query += dbQuery query += exclDbsQuery + if Backend.isDbms(DBMS.DB2): + query += ") AS foobar" query = agent.limitQuery(index, query, dbCond) value = inject.getValue(query, inband=False, error=False) @@ -1884,7 +1933,7 @@ class Enumeration: for tbl in tblList: tbl = safeSQLIdentificatorNaming(tbl, True) - if Backend.isDbms(DBMS.ORACLE): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): tbl = tbl.upper() infoMsg = "searching table" @@ -1951,6 +2000,8 @@ class Enumeration: query = rootQuery.blind.query query += tblQuery query += exclDbsQuery + if Backend.getIdentifiedDbms() == DBMS.DB2: + query += ") AS foobar" query = agent.limitQuery(index, query) foundDb = inject.getValue(query, inband=False, error=False) foundDb = safeSQLIdentificatorNaming(foundDb) @@ -2048,6 +2099,9 @@ class Enumeration: for column in colList: column = safeSQLIdentificatorNaming(column) + if Backend.isDbms(DBMS.DB2): + column = column.upper() + infoMsg = "searching column" if colConsider == "1": infoMsg += "s like" @@ -2132,6 +2186,8 @@ class Enumeration: query = rootQuery.blind.query query += colQuery query += exclDbsQuery + if Backend.isDbms(DBMS.DB2): + query += ") AS foobar" query = agent.limitQuery(index, query) db = inject.getValue(query, inband=False, error=False) db = safeSQLIdentificatorNaming(db) diff --git a/xml/queries.xml b/xml/queries.xml index 0ebb846ca..993585e4b 100644 --- a/xml/queries.xml +++ b/xml/queries.xml @@ -540,4 +540,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +