From 36c96ef796d8b36aefda68943e1bc21e84cb16ec Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Sat, 25 Jun 2011 09:44:24 +0000 Subject: [PATCH] Added DB2 support - patch provided by Sebastian Bittig --- doc/THANKS | 4 ++ lib/controller/handler.py | 6 +- lib/core/agent.py | 13 ++-- lib/core/common.py | 4 +- lib/core/dicts.py | 11 +++ lib/core/enums.py | 1 + lib/core/option.py | 7 +- lib/core/settings.py | 11 ++- 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 | 113 +++++++++++++++++++++++++++++++ plugins/dbms/db2/syntax.py | 72 ++++++++++++++++++++ plugins/dbms/db2/takeover.py | 32 +++++++++ plugins/generic/enumeration.py | 116 +++++++++++++++++++++++++------- xml/errors.xml | 4 ++ xml/queries.xml | 69 +++++++++++++++++++ 18 files changed, 537 insertions(+), 38 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/doc/THANKS b/doc/THANKS index 63e779f2c..57438435b 100644 --- a/doc/THANKS +++ b/doc/THANKS @@ -33,6 +33,10 @@ Daniele Bellucci for starting sqlmap project and developing it between July and August 2006 +Sebastian Bittig and the rest of the team at +r-tec IT Systeme GmbH + for providing with the DB2 fingerprint and enumeration support patch + Anthony Boynes for reporting several bugs diff --git a/lib/controller/handler.py b/lib/controller/handler.py index 7b21df61b..1b3117c19 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", "IBM 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 f12b91cb5..601ccf529 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 @@ -544,12 +544,15 @@ class Agent: inbandQuery = self.prefixQuery("UNION ALL SELECT ", prefix=prefix) if query.startswith("TOP"): - # TOP enumeration on DBMS.MSSQL is too specific and it has to go into it's own brackets - # because those NULLs cause problems with ORDER BY clause + # TOP enumeration on DBMS.MSSQL is too specific and it has to go + # into its own brackets because those NULLs cause problems with + # ORDER BY clause if Backend.isDbms(DBMS.MSSQL): inbandQuery += ",".join(map(lambda x: char if x != position else '(SELECT %s)' % query, range(0, count))) inbandQuery = self.suffixQuery(inbandQuery, comment, suffix) + return inbandQuery + topNum = re.search("\ATOP\s+([\d]+)\s+", query, re.I).group(1) query = query[len("TOP %s " % topNum):] inbandQuery += "TOP %s " % topNum @@ -643,7 +646,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 47f25ff30..ee61bc9a8 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2581,7 +2581,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) @@ -2598,7 +2598,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/dicts.py b/lib/core/dicts.py index 4582911f0..d097e4a74 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" + } diff --git a/lib/core/enums.py b/lib/core/enums.py index 4c1a59841..261435b28 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 0762dde31..beb1fa386 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -97,6 +97,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 LOCALHOST from lib.core.settings import MAX_NUMBER_OF_THREADS @@ -757,9 +758,9 @@ def __setDBMS(): errMsg += "it and sqlmap will fingerprint it for you." raise sqlmapUnsupportedDBMSException, errMsg - for aliases in (MSSQL_ALIASES, MYSQL_ALIASES, PGSQL_ALIASES, \ - ORACLE_ALIASES, SQLITE_ALIASES, ACCESS_ALIASES, \ - FIREBIRD_ALIASES, MAXDB_ALIASES, SYBASE_ALIASES): + for aliases in (MSSQL_ALIASES, MYSQL_ALIASES, PGSQL_ALIASES, ORACLE_ALIASES, \ + SQLITE_ALIASES, ACCESS_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 64484afee..263d23034 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -146,6 +146,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" ] @@ -156,8 +158,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 = [ "db2", "ibm db2", "ibmdb2" ] -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/"], @@ -168,7 +171,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, "python ibm-db", "http://code.google.com/p/ibm-db/"] } REFERER_ALIASES = ( "ref", "referer", "referrer" ) @@ -178,7 +182,8 @@ FROM_TABLE = { DBMS.ORACLE: " FROM DUAL", DBMS.ACCESS: " FROM MSysObjects", DBMS.FIREBIRD: " FROM RDB$DATABASE", - DBMS.MAXDB: " FROM VERSIONS" + DBMS.MAXDB: " FROM VERSIONS", + DBMS.DB2: " FROM SYSIBM.SYSDUMMY1" } SQL_STATEMENTS = { diff --git a/plugins/dbms/db2/__init__.py b/plugins/dbms/db2/__init__.py new file mode 100644 index 000000000..d699ecccc --- /dev/null +++ b/plugins/dbms/db2/__init__.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +""" +$Id$ + +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..b72f31fa9 --- /dev/null +++ b/plugins/dbms/db2/connector.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +""" +$Id$ + +Copyright (c) 2006-2011 sqlmap developers (http://sqlmap.sourceforge.net/) +See the file 'doc/COPYING' for copying permission +""" + +try: + import ibm_db +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://code.google.com/p/ibm-db/ + User guide: http://code.google.com/p/ibm-db/wiki/ibm_db_README + API: http://code.google.com/p/ibm-db/wiki/APIs + Debian package: + License: Apache + """ + + def __init__(self): + GenericConnector.__init__(self) diff --git a/plugins/dbms/db2/enumeration.py b/plugins/dbms/db2/enumeration.py new file mode 100644 index 000000000..974e87746 --- /dev/null +++ b/plugins/dbms/db2/enumeration.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +$Id$ + +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..c28a3da1a --- /dev/null +++ b/plugins/dbms/db2/filesystem.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +""" +$Id$ + +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..df770ecc2 --- /dev/null +++ b/plugins/dbms/db2/fingerprint.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python + +""" +$Id$ + +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("%d=(SELECT %d FROM SYSIBM.SYSDUMMY1)" % (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())) + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.DB2 + logger.warn(warnMsg) + + return False diff --git a/plugins/dbms/db2/syntax.py b/plugins/dbms/db2/syntax.py new file mode 100644 index 000000000..4fc810056 --- /dev/null +++ b/plugins/dbms/db2/syntax.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python + +""" +$Id$ + +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..d490276ec --- /dev/null +++ b/plugins/dbms/db2/takeover.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +""" +$Id$ + +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 354b0d5a4..577c4b5a4 100644 --- a/plugins/generic/enumeration.py +++ b/plugins/generic/enumeration.py @@ -45,6 +45,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 @@ -100,8 +101,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 IBM 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": @@ -193,7 +200,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 @@ -228,7 +235,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: @@ -423,7 +430,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: @@ -499,6 +506,25 @@ class Enumeration: if privilege.upper() == "Y": privileges.add(mysqlPrivs[count]) + # 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) @@ -563,7 +589,7 @@ class Enumeration: privileges = set() - if Backend.isDbms(DBMS.ORACLE): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): plusOne = True else: plusOne = False @@ -621,6 +647,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) @@ -663,12 +708,19 @@ class Enumeration: warnMsg += "names will be fetched from 'mysql' database" logger.warn(warnMsg) - if Backend.isDbms(DBMS.ORACLE): + elif Backend.isDbms(DBMS.ORACLE): warnMsg = "schema names are going to be used on Oracle " warnMsg += "for enumeration as the counterpart to database " warnMsg += "names on other DBMSes" logger.warn(warnMsg) + infoMsg = "fetching database (schema) names" + elif Backend.isDbms(DBMS.DB2): + warnMsg = "schema names are going to be used on IBM 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" @@ -701,7 +753,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 @@ -762,7 +814,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: @@ -874,7 +926,7 @@ class Enumeration: tables = [] - if Backend.isDbms(DBMS.ORACLE): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): plusOne = True else: plusOne = False @@ -928,7 +980,7 @@ class Enumeration: 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: @@ -939,7 +991,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(",") @@ -950,7 +1002,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(",") @@ -1059,7 +1111,7 @@ class Enumeration: if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery - elif Backend.isDbms(DBMS.ORACLE): + elif Backend.getIdentifiedDbms() in ( DBMS.ORACLE, DBMS.DB2): query = rootQuery.inband.query % unsafeSQLIdentificatorNaming(tbl.upper()) query += condQuery elif Backend.isDbms(DBMS.MSSQL): @@ -1126,7 +1178,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 @@ -1165,7 +1217,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 @@ -1185,7 +1237,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, conf.db, column, conf.db, @@ -1255,7 +1307,11 @@ class Enumeration: return kb.data.cachedColumns def __tableGetCount(self, db, table): - query = "SELECT COUNT(*) FROM %s.%s" % (safeSQLIdentificatorNaming(db), safeSQLIdentificatorNaming(table, True)) + if Backend.isDbms(DBMS.DB2): + query = "SELECT COUNT(*) FROM %s.%s--" % (safeSQLIdentificatorNaming(db.upper()), safeSQLIdentificatorNaming(table.upper(), True)) + else: + query = "SELECT COUNT(*) FROM %s.%s" % (safeSQLIdentificatorNaming(db), safeSQLIdentificatorNaming(table, True)) + count = inject.getValue(query, expected=EXPECTED.INT, charsetType=2) if count is not None and isinstance(count, basestring) and count.isdigit(): @@ -1451,7 +1507,7 @@ class Enumeration: conf.db = safeSQLIdentificatorNaming(conf.db) 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(",") @@ -1525,7 +1581,7 @@ class Enumeration: row = map(lambda x: x.replace(randStr, CONCAT_VALUE_DELIMITER).replace(randStr2, CONCAT_ROW_DELIMITER), row) entries.append(row) - if Backend.isDbms(DBMS.ORACLE): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.inband.query % (colString, tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper()))) elif Backend.isDbms(DBMS.SQLITE): query = rootQuery.inband.query % (colString, tbl) @@ -1590,7 +1646,7 @@ class Enumeration: infoMsg += "on database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) - if Backend.isDbms(DBMS.ORACLE): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.count % (tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper()))) elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): query = rootQuery.blind.count % tbl @@ -1638,7 +1694,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 @@ -1654,7 +1710,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, tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())), index) @@ -1822,6 +1878,9 @@ class Enumeration: for db in dbList: db = safeSQLIdentificatorNaming(db) + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + db = db.upper() + infoMsg = "searching database" if dbConsider == "1": infoMsg += "s like" @@ -1887,6 +1946,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) @@ -1932,7 +1993,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" @@ -1999,6 +2060,8 @@ class Enumeration: query = rootQuery.blind.query query += tblQuery query += exclDbsQuery + if Backend.isDbms(DBMS.DB2): + query += ") AS foobar" query = agent.limitQuery(index, query) foundDb = inject.getValue(query, inband=False, error=False) foundDb = safeSQLIdentificatorNaming(foundDb) @@ -2096,6 +2159,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" @@ -2181,6 +2247,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/errors.xml b/xml/errors.xml index 4f8b70baa..1ac9acf9e 100644 --- a/xml/errors.xml +++ b/xml/errors.xml @@ -48,6 +48,10 @@ + + + + diff --git a/xml/queries.xml b/xml/queries.xml index 9cb35d081..bd7c5772c 100644 --- a/xml/queries.xml +++ b/xml/queries.xml @@ -544,4 +544,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +