From d227413a14f704beacc680b579e7cce8f941e7bf Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 27 Jan 2020 17:32:31 +0100 Subject: [PATCH] Adding support for Altibase --- data/xml/errors.xml | 5 ++ data/xml/queries.xml | 74 +++++++++++++++++++++- lib/controller/checks.py | 3 +- lib/controller/handler.py | 4 ++ lib/core/agent.py | 8 ++- lib/core/common.py | 6 +- lib/core/dicts.py | 20 +++++- lib/core/dump.py | 2 + lib/core/enums.py | 4 +- lib/core/settings.py | 13 ++-- lib/utils/brute.py | 6 +- plugins/dbms/altibase/__init__.py | 30 +++++++++ plugins/dbms/altibase/connector.py | 15 +++++ plugins/dbms/altibase/enumeration.py | 20 ++++++ plugins/dbms/altibase/filesystem.py | 11 ++++ plugins/dbms/altibase/fingerprint.py | 95 ++++++++++++++++++++++++++++ plugins/dbms/altibase/syntax.py | 22 +++++++ plugins/dbms/altibase/takeover.py | 28 ++++++++ plugins/generic/databases.py | 47 +++++++++----- plugins/generic/entries.py | 13 ++-- plugins/generic/search.py | 9 +-- plugins/generic/users.py | 6 +- 22 files changed, 394 insertions(+), 47 deletions(-) create mode 100644 plugins/dbms/altibase/__init__.py create mode 100644 plugins/dbms/altibase/connector.py create mode 100644 plugins/dbms/altibase/enumeration.py create mode 100644 plugins/dbms/altibase/filesystem.py create mode 100644 plugins/dbms/altibase/fingerprint.py create mode 100644 plugins/dbms/altibase/syntax.py create mode 100644 plugins/dbms/altibase/takeover.py diff --git a/data/xml/errors.xml b/data/xml/errors.xml index 77c46bde3..3e9cbe545 100644 --- a/data/xml/errors.xml +++ b/data/xml/errors.xml @@ -210,4 +210,9 @@ + + + + + diff --git a/data/xml/queries.xml b/data/xml/queries.xml index ea2de83a5..7be42a59f 100644 --- a/data/xml/queries.xml +++ b/data/xml/queries.xml @@ -894,7 +894,6 @@ - @@ -1192,4 +1191,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 72aca0be1..07541f5f8 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -54,6 +54,7 @@ from lib.core.datatype import AttribDict from lib.core.datatype import InjectionDict from lib.core.decorators import stackedmethod from lib.core.dicts import FROM_DUMMY_TABLE +from lib.core.dicts import HEURISTIC_NULL_EVAL from lib.core.enums import DBMS from lib.core.enums import HASHDB_KEYS from lib.core.enums import HEURISTIC_TEST @@ -888,7 +889,7 @@ def heuristicCheckDbms(injection): continue if checkBooleanExpression("(SELECT '%s'%s)=%s%s%s" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), SINGLE_QUOTE_MARKER, randStr1, SINGLE_QUOTE_MARKER)): - if not checkBooleanExpression("(SELECT '%s'%s)=%s%s%s" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), SINGLE_QUOTE_MARKER, randStr2, SINGLE_QUOTE_MARKER)): + if dbms in HEURISTIC_NULL_EVAL and checkBooleanExpression("(SELECT %s%s) IS NULL" % (HEURISTIC_NULL_EVAL[dbms], FROM_DUMMY_TABLE.get(dbms, ""))) or not checkBooleanExpression("(SELECT '%s'%s)=%s%s%s" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), SINGLE_QUOTE_MARKER, randStr2, SINGLE_QUOTE_MARKER)): retVal = dbms break diff --git a/lib/controller/handler.py b/lib/controller/handler.py index 86ee67f21..2f5a95e4e 100644 --- a/lib/controller/handler.py +++ b/lib/controller/handler.py @@ -12,6 +12,7 @@ from lib.core.dicts import DBMS_DICT from lib.core.enums import DBMS from lib.core.exception import SqlmapConnectionException from lib.core.settings import ACCESS_ALIASES +from lib.core.settings import ALTIBASE_ALIASES from lib.core.settings import DB2_ALIASES from lib.core.settings import DERBY_ALIASES from lib.core.settings import FIREBIRD_ALIASES @@ -33,6 +34,8 @@ from lib.utils.sqlalchemy import SQLAlchemy from plugins.dbms.access.connector import Connector as AccessConn from plugins.dbms.access import AccessMap +from plugins.dbms.altibase.connector import Connector as AltibaseConn +from plugins.dbms.altibase import AltibaseMap from plugins.dbms.db2.connector import Connector as DB2Conn from plugins.dbms.db2 import DB2Map from plugins.dbms.derby.connector import Connector as DerbyConn @@ -93,6 +96,7 @@ def setHandler(): (DBMS.VERTICA, VERTICA_ALIASES, VerticaMap, VerticaConn), (DBMS.MCKOI, MCKOI_ALIASES, MckoiMap, MckoiConn), (DBMS.PRESTO, PRESTO_ALIASES, PrestoMap, PrestoConn), + (DBMS.ALTIBASE, ALTIBASE_ALIASES, AltibaseMap, AltibaseConn), ] _ = 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 cffc043a6..823af4ee6 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -659,7 +659,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): + 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): if fieldsExists: concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1) concatenatedQuery += "||'%s'" % kb.chars.stop @@ -948,10 +948,14 @@ 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): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO,): limitStr = queries[Backend.getIdentifiedDbms()].limit.query % (num, 1) limitedQuery += " %s" % limitStr + elif Backend.getIdentifiedDbms() in (DBMS.ALTIBASE,): + limitStr = queries[Backend.getIdentifiedDbms()].limit.query % (num + 1, 1) + limitedQuery += " %s" % limitStr + elif Backend.getIdentifiedDbms() in (DBMS.DERBY,): limitStr = queries[Backend.getIdentifiedDbms()].limit.query % (1, num) limitedQuery += " %s" % limitStr diff --git a/lib/core/common.py b/lib/core/common.py index 49b4a158f..4e66d778e 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -4074,7 +4074,7 @@ def safeSQLIdentificatorNaming(name, isTable=False): 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): retVal = "\"%s\"" % retVal - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE,): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.ALTIBASE): retVal = "\"%s\"" % retVal.upper() elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): if isTable: @@ -4110,9 +4110,9 @@ def unsafeSQLIdentificatorNaming(name): if isinstance(name, six.string_types): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.SQLITE): retVal = name.replace("`", "") - elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.INFORMIX, DBMS.HSQLDB, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO): + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO): retVal = name.replace("\"", "") - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE,): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.ALTIBASE): retVal = name.replace("\"", "").upper() elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): retVal = name.replace("[", "").replace("]", "") diff --git a/lib/core/dicts.py b/lib/core/dicts.py index 561502372..1d4b2481f 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -10,6 +10,7 @@ from lib.core.enums import DBMS from lib.core.enums import OS from lib.core.enums import POST_HINT from lib.core.settings import ACCESS_ALIASES +from lib.core.settings import ALTIBASE_ALIASES from lib.core.settings import BLANK from lib.core.settings import DB2_ALIASES from lib.core.settings import DERBY_ALIASES @@ -208,6 +209,7 @@ DBMS_DICT = { DBMS.VERTICA: (VERTICA_ALIASES, "vertica-python", "https://github.com/vertica/vertica-python", "vertica+vertica_python"), DBMS.MCKOI: (MCKOI_ALIASES, None, None, None), DBMS.PRESTO: (PRESTO_ALIASES, "presto-python-client", "https://github.com/prestodb/presto-python-client", None), + DBMS.ALTIBASE: (ALTIBASE_ALIASES, None, None, None), } # Reference: https://blog.jooq.org/tag/sysibm-sysdummy1/ @@ -219,7 +221,23 @@ FROM_DUMMY_TABLE = { DBMS.DB2: " FROM SYSIBM.SYSDUMMY1", DBMS.HSQLDB: " FROM INFORMATION_SCHEMA.SYSTEM_USERS", DBMS.INFORMIX: " FROM SYSMASTER:SYSDUAL", - DBMS.DERBY: " FROM SYSIBM.SYSDUMMY1" + DBMS.DERBY: " FROM SYSIBM.SYSDUMMY1", +} + +HEURISTIC_NULL_EVAL = { + DBMS.ACCESS: "CVAR(NULL)", + DBMS.MAXDB: "ALPHA(NULL)", + DBMS.MSSQL: "DIFFERENCE(NULL,NULL)", + DBMS.MYSQL: "QUARTER(NULL)", + DBMS.ORACLE: "INSTR2(NULL,NULL)", + DBMS.PGSQL: "QUOTE_IDENT(NULL)", + DBMS.SQLITE: "UNLIKELY(NULL)", + DBMS.MONETDB: "CODE(NULL)", + DBMS.DERBY: "NULLIF(USER,SESSION_USER)", + DBMS.VERTICA: "BITSTRING_TO_BINARY(NULL)", + DBMS.MCKOI: "TONUMBER(NULL)", + DBMS.PRESTO: "FROM_HEX(NULL)", + DBMS.ALTIBASE: "TDESENCRYPT(NULL,NULL)", } SQL_STATEMENTS = { diff --git a/lib/core/dump.py b/lib/core/dump.py index c4f14a153..d9e3176f3 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -168,6 +168,8 @@ class Dump(object): self.string("current database (no practical usage on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB) elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.MONETDB, DBMS.VERTICA): self.string("current schema (equivalent to database on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB) + elif Backend.getIdentifiedDbms() in (DBMS.ALTIBASE,): + self.string("current user (equivalent to database on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB) else: self.string("current database", data, content_type=CONTENT_TYPE.CURRENT_DB) diff --git a/lib/core/enums.py b/lib/core/enums.py index 6b37ee1da..8e31e6fa0 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -42,14 +42,15 @@ class DBMS(object): PGSQL = "PostgreSQL" SQLITE = "SQLite" SYBASE = "Sybase" + INFORMIX = "Informix" HSQLDB = "HSQLDB" H2 = "H2" - INFORMIX = "Informix" MONETDB = "MonetDB" DERBY = "Apache Derby" VERTICA = "Vertica" MCKOI = "Mckoi" PRESTO = "Presto" + ALTIBASE = "Altibase" class DBMS_DIRECTORY_NAME(object): ACCESS = "access" @@ -70,6 +71,7 @@ class DBMS_DIRECTORY_NAME(object): VERTICA = "vertica" MCKOI = "mckoi" PRESTO = "presto" + ALTIBASE = "altibase" class FORK(object): MARIADB = "MariaDB" diff --git a/lib/core/settings.py b/lib/core/settings.py index fc4420636..b4848faf7 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.4.1.56" +VERSION = "1.4.1.57" 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) @@ -264,6 +264,7 @@ DERBY_SYSTEM_DBS = ("NULLID", "SQLJ", "SYS", "SYSCAT", "SYSCS_DIAG", "SYSCS_UTIL VERTICA_SYSTEM_DBS = ("v_catalog", "v_internal", "v_monitor",) MCKOI_SYSTEM_DBS = ("",) PRESTO_SYSTEM_DBS = ("information_schema",) +ALTIBASE_SYSTEM_DBS = ("SYSTEM_",) MSSQL_ALIASES = ("microsoft sql server", "mssqlserver", "mssql", "ms") MYSQL_ALIASES = ("mysql", "my") + ("mariadb", "maria", "memsql", "tidb") @@ -283,20 +284,22 @@ DERBY_ALIASES = ("derby", "apache derby",) VERTICA_ALIASES = ("vertica",) MCKOI_ALIASES = ("mckoi",) PRESTO_ALIASES = ("presto",) - -UPPER_CASE_IDENTIFIERS = set((DBMS.ORACLE, DBMS.DB2, DBMS.FIREBIRD, DBMS.HSQLDB, DBMS.MAXDB, DBMS.H2, DBMS.DERBY)) +ALTIBASE_ALIASES = ("altibase",) DBMS_DIRECTORY_DICT = dict((getattr(DBMS, _), getattr(DBMS_DIRECTORY_NAME, _)) for _ in dir(DBMS) if not _.startswith("_")) -SUPPORTED_DBMS = 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 +SUPPORTED_DBMS = 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 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_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)) USER_AGENT_ALIASES = ("ua", "useragent", "user-agent") REFERER_ALIASES = ("ref", "referer", "referrer") HOST_ALIASES = ("host",) +# DBMSes with upper case identifiers +UPPER_CASE_DBMSES = set((DBMS.ORACLE, DBMS.DB2, DBMS.FIREBIRD, DBMS.HSQLDB, DBMS.MAXDB, DBMS.H2, DBMS.DERBY, DBMS.ALTIBASE)) + # Default schemas to use (when unable to enumerate) H2_DEFAULT_SCHEMA = HSQLDB_DEFAULT_SCHEMA = "PUBLIC" VERTICA_DEFAULT_SCHEMA = "public" diff --git a/lib/utils/brute.py b/lib/utils/brute.py index 4004bffd9..f3877dae1 100644 --- a/lib/utils/brute.py +++ b/lib/utils/brute.py @@ -41,7 +41,7 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.settings import BRUTE_COLUMN_EXISTS_TEMPLATE from lib.core.settings import BRUTE_TABLE_EXISTS_TEMPLATE from lib.core.settings import METADB_SUFFIX -from lib.core.settings import UPPER_CASE_IDENTIFIERS +from lib.core.settings import UPPER_CASE_DBMSES from lib.core.threads import getCurrentThreadData from lib.core.threads import runThreads from lib.request import inject @@ -84,7 +84,7 @@ def tableExists(tableFile, regex=None): pushValue(conf.db) - if conf.db and Backend.getIdentifiedDbms() in UPPER_CASE_IDENTIFIERS: + if conf.db and Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.db = conf.db.upper() message = "which common tables (wordlist) file do you want to use?\n" @@ -202,7 +202,7 @@ def columnExists(columnFile, regex=None): errMsg = "missing table parameter" raise SqlmapMissingMandatoryOptionException(errMsg) - if conf.db and Backend.getIdentifiedDbms() in UPPER_CASE_IDENTIFIERS: + if conf.db and Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.db = conf.db.upper() result = inject.checkBooleanExpression(safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (randomStr(), randomStr()))) diff --git a/plugins/dbms/altibase/__init__.py b/plugins/dbms/altibase/__init__.py new file mode 100644 index 000000000..a89266d6d --- /dev/null +++ b/plugins/dbms/altibase/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import ALTIBASE_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.altibase.enumeration import Enumeration +from plugins.dbms.altibase.filesystem import Filesystem +from plugins.dbms.altibase.fingerprint import Fingerprint +from plugins.dbms.altibase.syntax import Syntax +from plugins.dbms.altibase.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class AltibaseMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Altibase methods + """ + + def __init__(self): + self.excludeDbsList = ALTIBASE_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.ALTIBASE] = Syntax.escape diff --git a/plugins/dbms/altibase/connector.py b/plugins/dbms/altibase/connector.py new file mode 100644 index 000000000..138564733 --- /dev/null +++ b/plugins/dbms/altibase/connector.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2020 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 Altibase it is not (currently) possible to establish a " + errMsg += "direct connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/altibase/enumeration.py b/plugins/dbms/altibase/enumeration.py new file mode 100644 index 000000000..162768951 --- /dev/null +++ b/plugins/dbms/altibase/enumeration.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2020 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 getStatements(self): + warnMsg = "on Altibase it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] + + def getHostname(self): + warnMsg = "on Altibase it is not possible to enumerate the hostname" + logger.warn(warnMsg) diff --git a/plugins/dbms/altibase/filesystem.py b/plugins/dbms/altibase/filesystem.py new file mode 100644 index 000000000..e8c642492 --- /dev/null +++ b/plugins/dbms/altibase/filesystem.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + pass diff --git a/plugins/dbms/altibase/fingerprint.py b/plugins/dbms/altibase/fingerprint.py new file mode 100644 index 000000000..425d89a04 --- /dev/null +++ b/plugins/dbms/altibase/fingerprint.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2020 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 ALTIBASE_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.ALTIBASE) + + 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.ALTIBASE + 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(ALTIBASE_ALIASES): + setDbms(DBMS.ALTIBASE) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.ALTIBASE + logger.info(infoMsg) + + # Reference: http://support.altibase.com/fileDownload.do?gubun=admin&no=228 + result = inject.checkBooleanExpression("CHOSUNG(NULL) IS NULL") + + if result: + infoMsg = "confirming %s" % DBMS.ALTIBASE + logger.info(infoMsg) + + result = inject.checkBooleanExpression("TDESENCRYPT(NULL,NULL) IS NULL") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.ALTIBASE + logger.warn(warnMsg) + + return False + + setDbms(DBMS.ALTIBASE) + + self.getBanner() + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.ALTIBASE + logger.warn(warnMsg) + + return False diff --git a/plugins/dbms/altibase/syntax.py b/plugins/dbms/altibase/syntax.py new file mode 100644 index 000000000..f9355c077 --- /dev/null +++ b/plugins/dbms/altibase/syntax.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2020 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 CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104) FROM foobar" + True + """ + + def escaper(value): + return "||".join("CHR(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/altibase/takeover.py b/plugins/dbms/altibase/takeover.py new file mode 100644 index 000000000..c83884a81 --- /dev/null +++ b/plugins/dbms/altibase/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2020 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 Altibase it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Altibase it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Altibase it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Altibase 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 b8389d089..2786931d6 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -48,7 +48,7 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapUserQuitException from lib.core.settings import CURRENT_DB from lib.core.settings import REFLECTED_VALUE_MARKER -from lib.core.settings import UPPER_CASE_IDENTIFIERS +from lib.core.settings import UPPER_CASE_DBMSES from lib.core.settings import VERTICA_DEFAULT_SCHEMA from lib.request import inject from lib.techniques.union.use import unionUse @@ -87,6 +87,11 @@ class Databases(object): warnMsg += "schema names for enumeration as the counterpart to database " warnMsg += "names on other DBMSes" singleTimeWarnMessage(warnMsg) + elif Backend.getIdentifiedDbms() in (DBMS.ALTIBASE,): + warnMsg = "on %s you'll need to use " % Backend.getIdentifiedDbms() + warnMsg += "user names for enumeration as the counterpart to database " + warnMsg += "names on other DBMSes" + singleTimeWarnMessage(warnMsg) return kb.data.currentDb @@ -110,6 +115,14 @@ class Databases(object): infoMsg = "fetching database (schema) names" + elif Backend.getIdentifiedDbms() in (DBMS.ALTIBASE,): + warnMsg = "user names are going to be used on %s " % Backend.getIdentifiedDbms() + warnMsg += "for enumeration as the counterpart to database " + warnMsg += "names on other DBMSes" + logger.warn(warnMsg) + + infoMsg = "fetching database (user) names" + else: infoMsg = "fetching database names" @@ -142,7 +155,7 @@ class Databases(object): errMsg = "unable to retrieve the number of databases" logger.error(errMsg) else: - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.ALTIBASE) indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: @@ -229,7 +242,7 @@ class Databases(object): if conf.db == CURRENT_DB: conf.db = self.getCurrentDb() - if conf.db and Backend.getIdentifiedDbms() in UPPER_CASE_IDENTIFIERS: + if conf.db and Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.db = conf.db.upper() if conf.db: @@ -316,7 +329,7 @@ class Databases(object): if conf.getComments: _ = queries[Backend.getIdentifiedDbms()].table_comment if hasattr(_, "query"): - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE): query = _.query % (unsafeSQLIdentificatorNaming(db.upper()), unsafeSQLIdentificatorNaming(table.upper())) else: query = _.query % (unsafeSQLIdentificatorNaming(db), unsafeSQLIdentificatorNaming(table)) @@ -373,7 +386,7 @@ class Databases(object): tables = [] - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.ALTIBASE) indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: @@ -398,7 +411,7 @@ class Databases(object): if conf.getComments: _ = queries[Backend.getIdentifiedDbms()].table_comment if hasattr(_, "query"): - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE): query = _.query % (unsafeSQLIdentificatorNaming(db.upper()), unsafeSQLIdentificatorNaming(table.upper())) else: query = _.query % (unsafeSQLIdentificatorNaming(db), unsafeSQLIdentificatorNaming(table)) @@ -458,7 +471,7 @@ class Databases(object): raise SqlmapNoneDataException(errMsg) elif conf.db is not None: - if Backend.getIdentifiedDbms() in UPPER_CASE_IDENTIFIERS: + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.db = conf.db.upper() if ',' in conf.db: @@ -469,7 +482,7 @@ class Databases(object): conf.db = safeSQLIdentificatorNaming(conf.db) if conf.col: - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.col = conf.col.upper() colList = conf.col.split(',') @@ -485,7 +498,7 @@ class Databases(object): colList = [_ for _ in colList if _] if conf.tbl: - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.DERBY): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.tbl = conf.tbl.upper() tblList = conf.tbl.split(',') @@ -593,7 +606,7 @@ class Databases(object): query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE): query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery @@ -669,7 +682,7 @@ class Databases(object): if conf.getComments: _ = queries[Backend.getIdentifiedDbms()].column_comment if hasattr(_, "query"): - if Backend.getIdentifiedDbms() in UPPER_CASE_IDENTIFIERS: + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: query = _.query % (unsafeSQLIdentificatorNaming(conf.db.upper()), unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(name.upper())) else: query = _.query % (unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(name)) @@ -735,7 +748,7 @@ class Databases(object): query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE): query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery @@ -809,7 +822,7 @@ class Databases(object): elif Backend.isDbms(DBMS.MONETDB): query = safeStringFormat(rootQuery.blind.query, (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db), index)) field = None - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE): query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery field = None @@ -833,7 +846,7 @@ class Databases(object): if conf.getComments: _ = queries[Backend.getIdentifiedDbms()].column_comment if hasattr(_, "query"): - if Backend.getIdentifiedDbms() in UPPER_CASE_IDENTIFIERS: + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: query = _.query % (unsafeSQLIdentificatorNaming(conf.db.upper()), unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(column.upper())) else: query = _.query % (unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(column)) @@ -850,7 +863,7 @@ class Databases(object): if not onlyColNames: if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO): query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl), column, unsafeSQLIdentificatorNaming(conf.db)) - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE): query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl.upper()), column, unsafeSQLIdentificatorNaming(conf.db.upper())) elif Backend.isDbms(DBMS.MSSQL): query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db, conf.db, column, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) @@ -935,7 +948,7 @@ class Databases(object): if not db or not table: return None - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: db = db.upper() table = table.upper() @@ -1027,7 +1040,7 @@ class Databases(object): errMsg = "unable to retrieve the number of statements" raise SqlmapNoneDataException(errMsg) - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.ALTIBASE) indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index 99fd9d646..da60c5ea0 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -43,6 +43,7 @@ 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 NULL +from lib.core.settings import UPPER_CASE_DBMSES from lib.request import inject from lib.utils.hash import attackDumpedTable from lib.utils.pivotdumptable import pivotDumpTable @@ -70,7 +71,7 @@ class Entries(object): conf.db = self.getCurrentDb() elif conf.db is not None: - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.DERBY): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.db = conf.db.upper() if ',' in conf.db: @@ -86,7 +87,7 @@ class Entries(object): conf.db = safeSQLIdentificatorNaming(conf.db) if conf.tbl: - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.DERBY): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.tbl = conf.tbl.upper() tblList = conf.tbl.split(',') @@ -176,7 +177,7 @@ class Entries(object): entries = [] query = None - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE): 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): query = rootQuery.inband.query % (colString, tbl) @@ -285,7 +286,7 @@ class Entries(object): infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE): 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): query = rootQuery.blind.count % tbl @@ -380,7 +381,7 @@ class Entries(object): else: emptyColumns = [] - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.ALTIBASE) indexRange = getLimitRange(count, plusOne=plusOne) if len(colList) < len(indexRange) > CHECK_ZERO_COLUMNS_THRESHOLD: @@ -407,7 +408,7 @@ class Entries(object): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO): query = rootQuery.blind.query % (agent.preprocessField(tbl, column), conf.db, conf.tbl, sorted(colList, key=len)[0], index) - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE): query = rootQuery.blind.query % (agent.preprocessField(tbl, column), tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())), index) elif Backend.isDbms(DBMS.SQLITE): query = rootQuery.blind.query % (agent.preprocessField(tbl, column), tbl, index) diff --git a/plugins/generic/search.py b/plugins/generic/search.py index 0d359c875..731adae3c 100644 --- a/plugins/generic/search.py +++ b/plugins/generic/search.py @@ -34,6 +34,7 @@ from lib.core.exception import SqlmapMissingMandatoryOptionException 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 UPPER_CASE_DBMSES from lib.request import inject from lib.utils.brute import columnExists from lib.utils.brute import tableExists @@ -63,7 +64,7 @@ class Search(object): values = [] db = safeSQLIdentificatorNaming(db) - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.DERBY): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: db = db.upper() infoMsg = "searching database" @@ -170,7 +171,7 @@ class Search(object): values = [] tbl = safeSQLIdentificatorNaming(tbl, True) - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.FIREBIRD, DBMS.HSQLDB, DBMS.H2, DBMS.DERBY): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: tbl = tbl.upper() conf.db = conf.db.upper() if conf.db else conf.db @@ -393,7 +394,7 @@ class Search(object): conf.db = origDb conf.tbl = origTbl - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.DERBY): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: column = column.upper() conf.db = conf.db.upper() if conf.db else conf.db conf.tbl = conf.tbl.upper() if conf.tbl else conf.tbl @@ -602,7 +603,7 @@ class Search(object): logger.warn(warnMsg) def search(self): - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: for item in ('db', 'tbl', 'col'): if getattr(conf, item, None): setattr(conf, item, getattr(conf, item).upper()) diff --git a/plugins/generic/users.py b/plugins/generic/users.py index f94fc6eab..24be99e6f 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -128,7 +128,7 @@ class Users(object): errMsg = "unable to retrieve the number of database users" raise SqlmapNoneDataException(errMsg) - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.ALTIBASE) indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: @@ -293,7 +293,7 @@ class Users(object): passwords = [] - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.ALTIBASE) indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: @@ -541,7 +541,7 @@ class Users(object): privileges = set() - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.ALTIBASE) indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: