From e3028f195e0d29e1f5bb6d976c168fc7e6156c06 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 11 Jan 2021 17:36:23 +0100 Subject: [PATCH] Support for Raima Database Manager DBMS --- data/xml/errors.xml | 5 ++ data/xml/queries.xml | 39 +++++++++++++ lib/controller/handler.py | 4 ++ lib/core/agent.py | 9 ++- lib/core/common.py | 4 +- lib/core/dicts.py | 3 + lib/core/dump.py | 8 +-- lib/core/enums.py | 2 + lib/core/profiling.py | 3 - lib/core/settings.py | 10 ++-- lib/request/inject.py | 2 +- lib/utils/brute.py | 2 +- lib/utils/pivotdumptable.py | 3 +- plugins/dbms/raima/__init__.py | 29 ++++++++++ plugins/dbms/raima/connector.py | 15 +++++ plugins/dbms/raima/enumeration.py | 84 ++++++++++++++++++++++++++++ plugins/dbms/raima/filesystem.py | 18 ++++++ plugins/dbms/raima/fingerprint.py | 93 +++++++++++++++++++++++++++++++ plugins/dbms/raima/syntax.py | 22 ++++++++ plugins/dbms/raima/takeover.py | 28 ++++++++++ plugins/generic/databases.py | 34 ++++++----- plugins/generic/entries.py | 23 ++++---- sqlmap.py | 1 - 23 files changed, 397 insertions(+), 44 deletions(-) create mode 100644 plugins/dbms/raima/__init__.py create mode 100644 plugins/dbms/raima/connector.py create mode 100644 plugins/dbms/raima/enumeration.py create mode 100644 plugins/dbms/raima/filesystem.py create mode 100644 plugins/dbms/raima/fingerprint.py create mode 100644 plugins/dbms/raima/syntax.py create mode 100644 plugins/dbms/raima/takeover.py diff --git a/data/xml/errors.xml b/data/xml/errors.xml index 568b61bbe..99af38203 100644 --- a/data/xml/errors.xml +++ b/data/xml/errors.xml @@ -217,4 +217,9 @@ + + + + + diff --git a/data/xml/queries.xml b/data/xml/queries.xml index b61bdb7c6..75a6f4b53 100644 --- a/data/xml/queries.xml +++ b/data/xml/queries.xml @@ -1626,4 +1626,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/controller/handler.py b/lib/controller/handler.py index 106b8051f..a54a13140 100644 --- a/lib/controller/handler.py +++ b/lib/controller/handler.py @@ -33,6 +33,7 @@ from lib.core.settings import MYSQL_ALIASES from lib.core.settings import ORACLE_ALIASES from lib.core.settings import PGSQL_ALIASES from lib.core.settings import PRESTO_ALIASES +from lib.core.settings import RAIMA_ALIASES from lib.core.settings import SQLITE_ALIASES from lib.core.settings import SYBASE_ALIASES from lib.core.settings import VERTICA_ALIASES @@ -82,6 +83,8 @@ from plugins.dbms.postgresql.connector import Connector as PostgreSQLConn from plugins.dbms.postgresql import PostgreSQLMap from plugins.dbms.presto.connector import Connector as PrestoConn from plugins.dbms.presto import PrestoMap +from plugins.dbms.raima.connector import Connector as RaimaConn +from plugins.dbms.raima import RaimaMap from plugins.dbms.sqlite.connector import Connector as SQLiteConn from plugins.dbms.sqlite import SQLiteMap from plugins.dbms.sybase.connector import Connector as SybaseConn @@ -121,6 +124,7 @@ def setHandler(): (DBMS.CACHE, CACHE_ALIASES, CacheMap, CacheConn), (DBMS.EXTREMEDB, EXTREMEDB_ALIASES, ExtremeDBMap, ExtremeDBConn), (DBMS.FRONTBASE, FRONTBASE_ALIASES, FrontBaseMap, FrontBaseConn), + (DBMS.RAIMA, RAIMA_ALIASES, RaimaMap, RaimaConn), ] _ = max(_ if (conf.get("dbms") or Backend.getIdentifiedDbms() or kb.heuristicExtendedDbms or "").lower() in _[1] else () for _ in items) diff --git a/lib/core/agent.py b/lib/core/agent.py index c17407c48..d0df1a1cc 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -487,7 +487,10 @@ class Agent(object): else: if not (Backend.isDbms(DBMS.SQLITE) and not isDBMSVersionAtLeast('3')): nulledCastedField = rootQuery.cast.query % field - if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI): + + if re.search("COUNT\(", field) and Backend.getIdentifiedDbms() in (DBMS.RAIMA,): + pass + elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI): nulledCastedField = rootQuery.isnull.query % (nulledCastedField, nulledCastedField) else: nulledCastedField = rootQuery.isnull.query % nulledCastedField @@ -696,7 +699,7 @@ class Agent(object): elif fieldsNoSelect: concatenatedQuery = "CONCAT('%s',%s,'%s')" % (kb.chars.start, concatenatedQuery, kb.chars.stop) - elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE, DBMS.DB2, DBMS.FIREBIRD, DBMS.HSQLDB, DBMS.H2, DBMS.MONETDB, DBMS.DERBY, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.ALTIBASE, DBMS.MIMERSQL, DBMS.CRATEDB, DBMS.CUBRID, DBMS.CACHE, DBMS.EXTREMEDB, DBMS.FRONTBASE): + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE, DBMS.DB2, DBMS.FIREBIRD, DBMS.HSQLDB, DBMS.H2, DBMS.MONETDB, DBMS.DERBY, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.ALTIBASE, DBMS.MIMERSQL, DBMS.CRATEDB, DBMS.CUBRID, DBMS.CACHE, DBMS.EXTREMEDB, DBMS.FRONTBASE, DBMS.RAIMA): if fieldsExists: concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1) concatenatedQuery += "||'%s'" % kb.chars.stop @@ -1006,7 +1009,7 @@ class Agent(object): fromFrom = limitedQuery[fromIndex + 1:] orderBy = None - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.MIMERSQL, DBMS.CUBRID, DBMS.EXTREMEDB): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.MIMERSQL, DBMS.CUBRID, DBMS.EXTREMEDB, DBMS.RAIMA): limitStr = queries[Backend.getIdentifiedDbms()].limit.query % (num, 1) limitedQuery += " %s" % limitStr diff --git a/lib/core/common.py b/lib/core/common.py index 3f643e992..d161feeee 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -4193,7 +4193,7 @@ def safeSQLIdentificatorNaming(name, isTable=False): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.CUBRID, DBMS.SQLITE): # Note: in SQLite double-quotes are treated as string if column/identifier is non-existent (e.g. SELECT "foobar" FROM users) retVal = "`%s`" % retVal - elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.EXTREMEDB, DBMS.FRONTBASE): + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.EXTREMEDB, DBMS.FRONTBASE, DBMS.RAIMA): retVal = "\"%s\"" % retVal elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.ALTIBASE, DBMS.MIMERSQL): retVal = "\"%s\"" % retVal.upper() @@ -4231,7 +4231,7 @@ def unsafeSQLIdentificatorNaming(name): if isinstance(name, six.string_types): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.CUBRID, DBMS.SQLITE): retVal = name.replace("`", "") - elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.EXTREMEDB, DBMS.FRONTBASE): + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.EXTREMEDB, DBMS.FRONTBASE, DBMS.RAIMA): retVal = name.replace("\"", "") elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.ALTIBASE, DBMS.MIMERSQL): retVal = name.replace("\"", "").upper() diff --git a/lib/core/dicts.py b/lib/core/dicts.py index 29389b333..5aa1bdda9 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -33,6 +33,7 @@ from lib.core.settings import NULL from lib.core.settings import ORACLE_ALIASES from lib.core.settings import PGSQL_ALIASES from lib.core.settings import PRESTO_ALIASES +from lib.core.settings import RAIMA_ALIASES from lib.core.settings import SQLITE_ALIASES from lib.core.settings import SYBASE_ALIASES from lib.core.settings import VERTICA_ALIASES @@ -244,6 +245,7 @@ DBMS_DICT = { DBMS.CACHE: (CACHE_ALIASES, "python jaydebeapi & python-jpype", "https://pypi.python.org/pypi/JayDeBeApi/ & http://jpype.sourceforge.net/", None), DBMS.EXTREMEDB: (EXTREMEDB_ALIASES, None, None, None), DBMS.FRONTBASE: (FRONTBASE_ALIASES, None, None, None), + DBMS.RAIMA: (RAIMA_ALIASES, None, None, None), } # Reference: https://blog.jooq.org/tag/sysibm-sysdummy1/ @@ -280,6 +282,7 @@ HEURISTIC_NULL_EVAL = { DBMS.CUBRID: "(NULL SETEQ NULL)", DBMS.CACHE: "%SQLUPPER NULL", DBMS.EXTREMEDB: "NULLIFZERO(hashcode(NULL))", + DBMS.RAIMA: "IF(ROWNUMBER()>0,CONVERT(NULL,TINYINT),NULL))", } SQL_STATEMENTS = { diff --git a/lib/core/dump.py b/lib/core/dump.py index a9d0d81d4..89d4b0ac0 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -243,7 +243,7 @@ class Dump(object): for db, tables in dbTables.items(): tables = sorted(filter(None, tables)) - self._write("Database: %s" % unsafeSQLIdentificatorNaming(db) if db else "Current database") + self._write("Database: %s" % unsafeSQLIdentificatorNaming(db) if db and METADB_SUFFIX not in db else "") if len(tables) == 1: self._write("[1 table]") @@ -298,7 +298,7 @@ class Dump(object): maxlength2 = max(maxlength2, len("TYPE")) lines2 = "-" * (maxlength2 + 2) - self._write("Database: %s\nTable: %s" % (unsafeSQLIdentificatorNaming(db) if db else "Current database", unsafeSQLIdentificatorNaming(table))) + self._write("Database: %s\nTable: %s" % (unsafeSQLIdentificatorNaming(db) if db and METADB_SUFFIX not in db else "", unsafeSQLIdentificatorNaming(table))) if len(columns) == 1: self._write("[1 column]") @@ -353,7 +353,7 @@ class Dump(object): maxlength1 = max(maxlength1, getConsoleLength(getUnicode(table))) for db, counts in dbTables.items(): - self._write("Database: %s" % unsafeSQLIdentificatorNaming(db) if db else "Current database") + self._write("Database: %s" % unsafeSQLIdentificatorNaming(db) if db and METADB_SUFFIX not in db else "") lines1 = "-" * (maxlength1 + 2) blank1 = " " * (maxlength1 - len("Table")) @@ -479,7 +479,7 @@ class Dump(object): separator += "+%s" % lines separator += "+" - self._write("Database: %s\nTable: %s" % (unsafeSQLIdentificatorNaming(db) if db else "Current database", unsafeSQLIdentificatorNaming(table))) + self._write("Database: %s\nTable: %s" % (unsafeSQLIdentificatorNaming(db) if db and METADB_SUFFIX not in db else "", unsafeSQLIdentificatorNaming(table))) if conf.dumpFormat == DUMP_FORMAT.SQLITE: cols = [] diff --git a/lib/core/enums.py b/lib/core/enums.py index aedf1934e..deb2beacf 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -57,6 +57,7 @@ class DBMS(object): CACHE = "InterSystems Cache" EXTREMEDB = "eXtremeDB" FRONTBASE = "FrontBase" + RAIMA = "Raima Database Manager" class DBMS_DIRECTORY_NAME(object): ACCESS = "access" @@ -84,6 +85,7 @@ class DBMS_DIRECTORY_NAME(object): CACHE = "cache" EXTREMEDB = "extremedb" FRONTBASE = "frontbase" + RAIMA = "raima" class FORK(object): MARIADB = "MariaDB" diff --git a/lib/core/profiling.py b/lib/core/profiling.py index 8d4f643a0..84a8558e1 100644 --- a/lib/core/profiling.py +++ b/lib/core/profiling.py @@ -5,14 +5,11 @@ Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ -import codecs import cProfile import os -from lib.core.common import getSafeExString from lib.core.data import logger from lib.core.data import paths -from lib.core.settings import UNICODE_ENCODING def profile(profileOutputFile=None): """ diff --git a/lib/core/settings.py b/lib/core/settings.py index 01443d3c2..f0d5a11ee 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from lib.core.enums import OS from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.5.1.17" +VERSION = "1.5.1.18" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE) @@ -282,6 +282,7 @@ CUBRID_SYSTEM_DBS = ("DBA",) CACHE_SYSTEM_DBS = ("%Dictionary", "INFORMATION_SCHEMA", "%SYS") EXTREMEDB_SYSTEM_DBS = ("",) FRONTBASE_SYSTEM_DBS = ("DEFINITION_SCHEMA", "INFORMATION_SCHEMA") +RAIMA_SYSTEM_DBS = ("",) # Note: () + () MSSQL_ALIASES = ("microsoft sql server", "mssqlserver", "mssql", "ms") @@ -289,7 +290,7 @@ MYSQL_ALIASES = ("mysql", "my") + ("mariadb", "maria", "memsql", "tidb", "percon PGSQL_ALIASES = ("postgresql", "postgres", "pgsql", "psql", "pg") + ("cockroach", "cockroachdb", "redshift", "greenplum", "yellowbrick", "enterprisedb", "aurora") ORACLE_ALIASES = ("oracle", "orcl", "ora", "or") SQLITE_ALIASES = ("sqlite", "sqlite3") -ACCESS_ALIASES = ("msaccess", "access", "jet", "microsoft access") +ACCESS_ALIASES = ("microsoft access", "msaccess", "access", "jet") FIREBIRD_ALIASES = ("firebird", "mozilla firebird", "interbase", "ibase", "fb") MAXDB_ALIASES = ("max", "maxdb", "sap maxdb", "sap db") SYBASE_ALIASES = ("sybase", "sybase sql server") @@ -309,13 +310,14 @@ CUBRID_ALIASES = ("cubrid",) CACHE_ALIASES = ("intersystems cache", "cachedb", "cache") EXTREMEDB_ALIASES = ("extremedb", "extreme") FRONTBASE_ALIASES = ("frontbase",) +RAIMA_ALIASES = ("raima database manager", "raima", "raimadb", "raimadm", "rdm", "rds", "velocis") DBMS_DIRECTORY_DICT = dict((getattr(DBMS, _), getattr(DBMS_DIRECTORY_NAME, _)) for _ in dir(DBMS) if not _.startswith("_")) -SUPPORTED_DBMS = set(MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES + SQLITE_ALIASES + ACCESS_ALIASES + FIREBIRD_ALIASES + MAXDB_ALIASES + SYBASE_ALIASES + DB2_ALIASES + HSQLDB_ALIASES + H2_ALIASES + INFORMIX_ALIASES + MONETDB_ALIASES + DERBY_ALIASES + VERTICA_ALIASES + MCKOI_ALIASES + PRESTO_ALIASES + ALTIBASE_ALIASES + MIMERSQL_ALIASES + CRATEDB_ALIASES + CUBRID_ALIASES + CACHE_ALIASES + EXTREMEDB_ALIASES) +SUPPORTED_DBMS = set(MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES + SQLITE_ALIASES + ACCESS_ALIASES + FIREBIRD_ALIASES + MAXDB_ALIASES + SYBASE_ALIASES + DB2_ALIASES + HSQLDB_ALIASES + H2_ALIASES + INFORMIX_ALIASES + MONETDB_ALIASES + DERBY_ALIASES + VERTICA_ALIASES + MCKOI_ALIASES + PRESTO_ALIASES + ALTIBASE_ALIASES + MIMERSQL_ALIASES + CRATEDB_ALIASES + CUBRID_ALIASES + CACHE_ALIASES + EXTREMEDB_ALIASES + RAIMA_ALIASES) SUPPORTED_OS = ("linux", "windows") -DBMS_ALIASES = ((DBMS.MSSQL, MSSQL_ALIASES), (DBMS.MYSQL, MYSQL_ALIASES), (DBMS.PGSQL, PGSQL_ALIASES), (DBMS.ORACLE, ORACLE_ALIASES), (DBMS.SQLITE, SQLITE_ALIASES), (DBMS.ACCESS, ACCESS_ALIASES), (DBMS.FIREBIRD, FIREBIRD_ALIASES), (DBMS.MAXDB, MAXDB_ALIASES), (DBMS.SYBASE, SYBASE_ALIASES), (DBMS.DB2, DB2_ALIASES), (DBMS.HSQLDB, HSQLDB_ALIASES), (DBMS.H2, H2_ALIASES), (DBMS.INFORMIX, INFORMIX_ALIASES), (DBMS.MONETDB, MONETDB_ALIASES), (DBMS.DERBY, DERBY_ALIASES), (DBMS.VERTICA, VERTICA_ALIASES), (DBMS.MCKOI, MCKOI_ALIASES), (DBMS.PRESTO, PRESTO_ALIASES), (DBMS.ALTIBASE, ALTIBASE_ALIASES), (DBMS.MIMERSQL, MIMERSQL_ALIASES), (DBMS.CRATEDB, CRATEDB_ALIASES), (DBMS.CUBRID, CUBRID_ALIASES), (DBMS.CACHE, CACHE_ALIASES), (DBMS.EXTREMEDB, EXTREMEDB_ALIASES), (DBMS.FRONTBASE, FRONTBASE_ALIASES)) +DBMS_ALIASES = ((DBMS.MSSQL, MSSQL_ALIASES), (DBMS.MYSQL, MYSQL_ALIASES), (DBMS.PGSQL, PGSQL_ALIASES), (DBMS.ORACLE, ORACLE_ALIASES), (DBMS.SQLITE, SQLITE_ALIASES), (DBMS.ACCESS, ACCESS_ALIASES), (DBMS.FIREBIRD, FIREBIRD_ALIASES), (DBMS.MAXDB, MAXDB_ALIASES), (DBMS.SYBASE, SYBASE_ALIASES), (DBMS.DB2, DB2_ALIASES), (DBMS.HSQLDB, HSQLDB_ALIASES), (DBMS.H2, H2_ALIASES), (DBMS.INFORMIX, INFORMIX_ALIASES), (DBMS.MONETDB, MONETDB_ALIASES), (DBMS.DERBY, DERBY_ALIASES), (DBMS.VERTICA, VERTICA_ALIASES), (DBMS.MCKOI, MCKOI_ALIASES), (DBMS.PRESTO, PRESTO_ALIASES), (DBMS.ALTIBASE, ALTIBASE_ALIASES), (DBMS.MIMERSQL, MIMERSQL_ALIASES), (DBMS.CRATEDB, CRATEDB_ALIASES), (DBMS.CUBRID, CUBRID_ALIASES), (DBMS.CACHE, CACHE_ALIASES), (DBMS.EXTREMEDB, EXTREMEDB_ALIASES), (DBMS.FRONTBASE, FRONTBASE_ALIASES), (DBMS.RAIMA, RAIMA_ALIASES)) USER_AGENT_ALIASES = ("ua", "useragent", "user-agent") REFERER_ALIASES = ("ref", "referer", "referrer") diff --git a/lib/request/inject.py b/lib/request/inject.py index 66871a960..2de0e5e93 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -113,7 +113,7 @@ def _goInference(payload, expression, charsetType=None, firstChar=None, lastChar else: expression = "SELECT %s FROM (%s)" % (field, expression) - if field and conf.hexConvert or conf.binaryFields and field in conf.binaryFields: + if field and conf.hexConvert or conf.binaryFields and field in conf.binaryFields or Backend.getIdentifiedDbms() in (DBMS.RAIMA,): nulledCastedField = agent.nullAndCastField(field) injExpression = expression.replace(field, nulledCastedField, 1) else: diff --git a/lib/utils/brute.py b/lib/utils/brute.py index a03fe1b40..7fdc140e2 100644 --- a/lib/utils/brute.py +++ b/lib/utils/brute.py @@ -104,7 +104,7 @@ def tableExists(tableFile, regex=None): tables = filterListValue(tables, regex) for conf.db in (conf.db.split(',') if conf.db else [conf.db]): - if conf.db: + if conf.db and METADB_SUFFIX not in conf.db: infoMsg = "checking database '%s'" % conf.db logger.info(infoMsg) diff --git a/lib/utils/pivotdumptable.py b/lib/utils/pivotdumptable.py index fd9128d94..1d33797fd 100644 --- a/lib/utils/pivotdumptable.py +++ b/lib/utils/pivotdumptable.py @@ -30,6 +30,7 @@ from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapNoneDataException from lib.core.settings import MAX_INT from lib.core.settings import NULL +from lib.core.settings import SINGLE_QUOTE_MARKER from lib.core.unescaper import unescaper from lib.request import inject from lib.utils.safe2bin import safechardecode @@ -128,7 +129,7 @@ def pivotDumpTable(table, colList, count=None, blind=True, alias=None): if column == colList[0]: query = dumpNode.query.replace("'%s'" if unescaper.escape(pivotValue, False) != pivotValue else "%s", "%s") % (agent.preprocessField(table, column), table, agent.preprocessField(table, column), unescaper.escape(pivotValue, False)) else: - query = dumpNode.query2.replace("'%s'" if unescaper.escape(pivotValue, False) != pivotValue else "%s", "%s") % (agent.preprocessField(table, column), table, agent.preprocessField(table, colList[0]), unescaper.escape(pivotValue, False)) + query = dumpNode.query2.replace("'%s'" if unescaper.escape(pivotValue, False) != pivotValue else "%s", "%s") % (agent.preprocessField(table, column), table, agent.preprocessField(table, colList[0]), unescaper.escape(pivotValue, False) if SINGLE_QUOTE_MARKER not in dumpNode.query2 else pivotValue) query = agent.whereQuery(query) return unArrayizeValue(inject.getValue(query, blind=blind, time=blind, union=not blind, error=not blind)) diff --git a/plugins/dbms/raima/__init__.py b/plugins/dbms/raima/__init__.py new file mode 100644 index 000000000..565c30629 --- /dev/null +++ b/plugins/dbms/raima/__init__.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import RAIMA_SYSTEM_DBS +from lib.core.unescaper import unescaper +from plugins.dbms.raima.enumeration import Enumeration +from plugins.dbms.raima.filesystem import Filesystem +from plugins.dbms.raima.fingerprint import Fingerprint +from plugins.dbms.raima.syntax import Syntax +from plugins.dbms.raima.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class RaimaMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Raima methods + """ + + def __init__(self): + self.excludeDbsList = RAIMA_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.RAIMA] = Syntax.escape diff --git a/plugins/dbms/raima/connector.py b/plugins/dbms/raima/connector.py new file mode 100644 index 000000000..1ddfb7181 --- /dev/null +++ b/plugins/dbms/raima/connector.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + def connect(self): + errMsg = "on Raima it is not (currently) possible to establish a " + errMsg += "direct connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/raima/enumeration.py b/plugins/dbms/raima/enumeration.py new file mode 100644 index 000000000..980d2e3a2 --- /dev/null +++ b/plugins/dbms/raima/enumeration.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import logger +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getBanner(self): + warnMsg = "on Raima it is not possible to get the banner" + logger.warn(warnMsg) + + return None + + def getCurrentUser(self): + warnMsg = "on Raima it is not possible to enumerate the current user" + logger.warn(warnMsg) + + def getCurrentDb(self): + warnMsg = "on Raima it is not possible to get name of the current database" + logger.warn(warnMsg) + + def isDba(self, user=None): + warnMsg = "on Raima it is not possible to test if current user is DBA" + logger.warn(warnMsg) + + def getUsers(self): + warnMsg = "on Raima it is not possible to enumerate the users" + logger.warn(warnMsg) + + return [] + + def getPasswordHashes(self): + warnMsg = "on Raima it is not possible to enumerate the user password hashes" + logger.warn(warnMsg) + + return {} + + def getPrivileges(self, *args, **kwargs): + warnMsg = "on Raima it is not possible to enumerate the user privileges" + logger.warn(warnMsg) + + return {} + + def getDbs(self): + warnMsg = "on Raima it is not possible to enumerate databases (use only '--tables')" + logger.warn(warnMsg) + + return [] + + def searchDb(self): + warnMsg = "on Raima it is not possible to search databases" + logger.warn(warnMsg) + + return [] + + def searchTable(self): + warnMsg = "on Raima it is not possible to search tables" + logger.warn(warnMsg) + + return [] + + def searchColumn(self): + warnMsg = "on Raima it is not possible to search columns" + logger.warn(warnMsg) + + return [] + + def search(self): + warnMsg = "on Raima search option is not available" + logger.warn(warnMsg) + + def getHostname(self): + warnMsg = "on Raima it is not possible to enumerate the hostname" + logger.warn(warnMsg) + + def getStatements(self): + warnMsg = "on Raima it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/raima/filesystem.py b/plugins/dbms/raima/filesystem.py new file mode 100644 index 000000000..f9982e22c --- /dev/null +++ b/plugins/dbms/raima/filesystem.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + def readFile(self, remoteFile): + errMsg = "on Raima it is not possible to read files" + raise SqlmapUnsupportedFeatureException(errMsg) + + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): + errMsg = "on Raima it is not possible to write files" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/raima/fingerprint.py b/plugins/dbms/raima/fingerprint.py new file mode 100644 index 000000000..12b17937c --- /dev/null +++ b/plugins/dbms/raima/fingerprint.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +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 +from lib.core.enums import DBMS +from lib.core.session import setDbms +from lib.core.settings import METADB_SUFFIX +from lib.core.settings import RAIMA_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.RAIMA) + + 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.RAIMA + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + 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(RAIMA_ALIASES): + setDbms(DBMS.RAIMA) + return True + + infoMsg = "testing %s" % DBMS.RAIMA + logger.info(infoMsg) + + result = inject.checkBooleanExpression("ROWNUMBER()=ROWNUMBER()") + + if result: + infoMsg = "confirming %s" % DBMS.RAIMA + logger.info(infoMsg) + + result = inject.checkBooleanExpression("INSSTR('[RANDSTR1]',0,0,'[RANDSTR2]') IS NOT NULL") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.RAIMA + logger.warn(warnMsg) + + return False + + setDbms(DBMS.RAIMA) + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.RAIMA + logger.warn(warnMsg) + + return False + + def forceDbmsEnum(self): + conf.db = ("%s%s" % (DBMS.RAIMA, METADB_SUFFIX)).replace(' ', '_') diff --git a/plugins/dbms/raima/syntax.py b/plugins/dbms/raima/syntax.py new file mode 100644 index 000000000..19853b3e8 --- /dev/null +++ b/plugins/dbms/raima/syntax.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHAR(97)||CHAR(98)||CHAR(99)||CHAR(100)||CHAR(101)||CHAR(102)||CHAR(103)||CHAR(104) FROM foobar" + True + """ + + def escaper(value): + return "||".join("CHAR(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/raima/takeover.py b/plugins/dbms/raima/takeover.py new file mode 100644 index 000000000..17ba3671c --- /dev/null +++ b/plugins/dbms/raima/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def osCmd(self): + errMsg = "on Raima it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Raima it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Raima it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Raima it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 289d73454..37386a80b 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -47,6 +47,7 @@ from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapUserQuitException from lib.core.settings import CURRENT_DB +from lib.core.settings import METADB_SUFFIX from lib.core.settings import PLUS_ONE_DBMSES from lib.core.settings import REFLECTED_VALUE_MARKER from lib.core.settings import UPPER_CASE_DBMSES @@ -222,7 +223,7 @@ class Databases(object): logger.warn(warnMsg) bruteForce = True - elif Backend.getIdentifiedDbms() in (DBMS.MCKOI, DBMS.EXTREMEDB): + elif Backend.getIdentifiedDbms() in (DBMS.MCKOI, DBMS.EXTREMEDB, DBMS.RAIMA): bruteForce = True elif Backend.getIdentifiedDbms() in (DBMS.ACCESS,): @@ -336,8 +337,9 @@ class Databases(object): comment = unArrayizeValue(inject.getValue(query, blind=False, time=False)) if not isNoneValue(comment): - infoMsg = "retrieved comment '%s' for table '%s' " % (comment, unsafeSQLIdentificatorNaming(table)) - infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(db) + infoMsg = "retrieved comment '%s' for table '%s'" % (comment, unsafeSQLIdentificatorNaming(table)) + if METADB_SUFFIX not in db: + infoMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(db) logger.info(infoMsg) else: warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() @@ -418,8 +420,9 @@ class Databases(object): comment = unArrayizeValue(inject.getValue(query, union=False, error=False)) if not isNoneValue(comment): - infoMsg = "retrieved comment '%s' for table '%s' " % (comment, unsafeSQLIdentificatorNaming(table)) - infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(db) + infoMsg = "retrieved comment '%s' for table '%s'" % (comment, unsafeSQLIdentificatorNaming(table)) + if METADB_SUFFIX not in db: + infoMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(db) logger.info(infoMsg) else: warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() @@ -516,8 +519,9 @@ class Databases(object): tblList = list(tblList) elif not conf.search: - errMsg = "unable to retrieve the tables " - errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) + errMsg = "unable to retrieve the tables" + if METADB_SUFFIX not in conf.db: + errMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) raise SqlmapNoneDataException(errMsg) else: return kb.data.cachedColumns @@ -534,7 +538,7 @@ class Databases(object): logger.warn(warnMsg) bruteForce = True - elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB): + elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB, DBMS.RAIMA): warnMsg = "cannot retrieve column names, " warnMsg += "back-end DBMS is %s" % Backend.getIdentifiedDbms() logger.warn(warnMsg) @@ -549,7 +553,7 @@ class Databases(object): resumeAvailable = True break - if resumeAvailable and not conf.freshQueries: + if resumeAvailable and not (conf.freshQueries and not colList): columns = {} for column in colList: @@ -632,7 +636,8 @@ class Databases(object): values = [(_,) for _ in colList] else: infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) - infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) + if METADB_SUFFIX not in conf.db: + infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) values = None @@ -781,7 +786,8 @@ class Databases(object): columns[safeSQLIdentificatorNaming(value)] = None else: infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) - infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) + if METADB_SUFFIX not in conf.db: + infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) @@ -802,7 +808,8 @@ class Databases(object): if not columns: errMsg = "unable to retrieve the %scolumns " % ("number of " if not Backend.isDbms(DBMS.MSSQL) else "") errMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) - errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) + if METADB_SUFFIX not in conf.db: + errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.error(errMsg) continue @@ -904,7 +911,8 @@ class Databases(object): if not kb.data.cachedColumns: warnMsg = "unable to retrieve column names for " warnMsg += ("table '%s' " % unsafeSQLIdentificatorNaming(unArrayizeValue(tblList))) if len(tblList) == 1 else "any table " - warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) + if METADB_SUFFIX not in conf.db: + warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.warn(warnMsg) if bruteForce is None: diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index 8cc32b538..3cb50a0fd 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -42,6 +42,7 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapUnsupportedFeatureException from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD from lib.core.settings import CURRENT_DB +from lib.core.settings import METADB_SUFFIX from lib.core.settings import NULL from lib.core.settings import PLUS_ONE_DBMSES from lib.core.settings import UPPER_CASE_DBMSES @@ -137,9 +138,9 @@ class Entries(object): kb.dumpTable = "%s.%s" % (conf.db, tbl) if safeSQLIdentificatorNaming(conf.db) not in kb.data.cachedColumns or safeSQLIdentificatorNaming(tbl, True) not in kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] or not kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)]: - warnMsg = "unable to enumerate the columns for table " - warnMsg += "'%s' in database" % unsafeSQLIdentificatorNaming(tbl) - warnMsg += " '%s'" % unsafeSQLIdentificatorNaming(conf.db) + warnMsg = "unable to enumerate the columns for table '%s'" % unsafeSQLIdentificatorNaming(tbl) + if METADB_SUFFIX not in conf.db: + warnMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) warnMsg += ", skipping" if len(tblList) > 1 else "" logger.warn(warnMsg) @@ -153,7 +154,8 @@ class Entries(object): if not colList: warnMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(tbl) - warnMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) + if METADB_SUFFIX not in conf.db: + warnMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) warnMsg += " (no usable column names)" logger.warn(warnMsg) continue @@ -166,7 +168,8 @@ class Entries(object): if conf.col: infoMsg += " of column(s) '%s'" % colNames infoMsg += " for table '%s'" % unsafeSQLIdentificatorNaming(tbl) - infoMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) + if METADB_SUFFIX not in conf.db: + infoMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) for column in colList: @@ -182,7 +185,7 @@ class Entries(object): if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL): query = rootQuery.inband.query % (colString, tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper()))) - elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.MCKOI, DBMS.EXTREMEDB): + elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.MCKOI, DBMS.EXTREMEDB, DBMS.RAIMA): query = rootQuery.inband.query % (colString, tbl) elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): # Partial inband and error @@ -291,12 +294,10 @@ class Entries(object): if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL): 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, DBMS.MCKOI, DBMS.EXTREMEDB): + elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.MAXDB, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MCKOI, DBMS.EXTREMEDB, DBMS.RAIMA): query = rootQuery.blind.count % tbl elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): query = rootQuery.blind.count % ("%s.%s" % (conf.db, tbl)) - elif Backend.isDbms(DBMS.MAXDB): - query = rootQuery.blind.count % tbl elif Backend.isDbms(DBMS.INFORMIX): query = rootQuery.blind.count % (conf.db, tbl) else: @@ -329,8 +330,8 @@ class Entries(object): continue - elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL, DBMS.INFORMIX, DBMS.MCKOI): - if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB): + elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL, DBMS.INFORMIX, DBMS.MCKOI, DBMS.RAIMA): + if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.RAIMA): table = tbl elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL, DBMS.MAXDB): table = "%s.%s" % (conf.db, tbl) diff --git a/sqlmap.py b/sqlmap.py index 18e1d58ac..f18df7fbe 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -80,7 +80,6 @@ try: from lib.core.settings import VERSION from lib.parse.cmdline import cmdLineParser from lib.utils.crawler import crawl - from thirdparty import six except KeyboardInterrupt: errMsg = "user aborted"