diff --git a/data/xml/errors.xml b/data/xml/errors.xml index 1fcf85ea1..3afdbc91c 100644 --- a/data/xml/errors.xml +++ b/data/xml/errors.xml @@ -196,4 +196,10 @@ + + + + + + diff --git a/data/xml/queries.xml b/data/xml/queries.xml index 9b7f6198a..c606cbce7 100644 --- a/data/xml/queries.xml +++ b/data/xml/queries.xml @@ -1088,4 +1088,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/controller/checks.py b/lib/controller/checks.py index a26a2e652..13d7d1fc5 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -879,12 +879,12 @@ def heuristicCheckDbms(injection): kb.injection = injection for dbms in getPublicTypeMembers(DBMS, True): - randStr1, randStr2 = randomStr(), randomStr() - Backend.forceDbms(dbms) - if conf.noEscape and dbms not in FROM_DUMMY_TABLE: continue + randStr1, randStr2 = randomStr(), randomStr() + Backend.forceDbms(dbms) + 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)): retVal = dbms diff --git a/lib/controller/handler.py b/lib/controller/handler.py index b26218ae5..5fca7156a 100644 --- a/lib/controller/handler.py +++ b/lib/controller/handler.py @@ -11,6 +11,7 @@ from lib.core.data import kb from lib.core.dicts import DBMS_DICT from lib.core.enums import DBMS from lib.core.exception import SqlmapConnectionException +from lib.core.settings import MCKOI_ALIASES from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MYSQL_ALIASES from lib.core.settings import ORACLE_ALIASES @@ -29,6 +30,8 @@ from lib.core.settings import DERBY_ALIASES from lib.core.settings import VERTICA_ALIASES from lib.utils.sqlalchemy import SQLAlchemy +from plugins.dbms.mckoi import MckoiMap +from plugins.dbms.mckoi.connector import Connector as MckoiConn from plugins.dbms.mssqlserver import MSSQLServerMap from plugins.dbms.mssqlserver.connector import Connector as MSSQLServerConn from plugins.dbms.mysql import MySQLMap @@ -85,6 +88,7 @@ def setHandler(): (DBMS.MONETDB, MONETDB_ALIASES, MonetDBMap, MonetDBConn), (DBMS.DERBY, DERBY_ALIASES, DerbyMap, DerbyConn), (DBMS.VERTICA, VERTICA_ALIASES, VerticaMap, VerticaConn), + (DBMS.MCKOI, MCKOI_ALIASES, MckoiMap, MckoiConn), ] _ = 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 719b6758a..dd6cc5dae 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -47,6 +47,7 @@ from lib.core.settings import BOUNDED_INJECTION_MARKER from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import GENERIC_SQL_COMMENT +from lib.core.settings import GENERIC_SQL_COMMENT_MARKER from lib.core.settings import INFERENCE_MARKER from lib.core.settings import NULL from lib.core.settings import PAYLOAD_DELIMITER @@ -297,8 +298,8 @@ class Agent(object): where = getTechniqueData().where if where is None else where comment = getTechniqueData().comment if comment is None else comment - if Backend.getIdentifiedDbms() == DBMS.ACCESS and any((comment or "").startswith(_) for _ in ("--", "[GENERIC_SQL_COMMENT]")): - comment = queries[DBMS.ACCESS].comment.query + if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI) and any((comment or "").startswith(_) for _ in ("--", GENERIC_SQL_COMMENT_MARKER)): + comment = queries[Backend.getIdentifiedDbms()].comment.query if comment is not None: expression += comment @@ -454,7 +455,7 @@ 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,): + if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI): nulledCastedField = rootQuery.isnull.query % (nulledCastedField, nulledCastedField) else: nulledCastedField = rootQuery.isnull.query % nulledCastedField @@ -656,7 +657,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): + 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): if fieldsExists: concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1) concatenatedQuery += "||'%s'" % kb.chars.stop @@ -1058,12 +1059,15 @@ class Agent(object): def forgeQueryOutputLength(self, expression): lengthQuery = queries[Backend.getIdentifiedDbms()].length.query select = re.search(r"\ASELECT\s+", expression, re.I) + selectFrom = re.search(r"\ASELECT\s+(.+)\s+FROM\s+(.+)", expression, re.I) selectTopExpr = re.search(r"\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM", expression, re.I) selectMinMaxExpr = re.search(r"\ASELECT\s+(MIN|MAX)\(.+?\)\s+FROM", expression, re.I) _, _, _, _, _, _, fieldsStr, _ = self.getFields(expression) - if selectTopExpr or selectMinMaxExpr: + if Backend.getIdentifiedDbms() in (DBMS.MCKOI,) and selectFrom: + lengthExpr = "SELECT %s FROM %s" % (lengthQuery % selectFrom.group(1), selectFrom.group(2)) + elif selectTopExpr or selectMinMaxExpr: lengthExpr = lengthQuery % ("(%s)" % expression) elif select: lengthExpr = expression.replace(fieldsStr, lengthQuery % fieldsStr, 1) diff --git a/lib/core/common.py b/lib/core/common.py index ec8d21676..b35e1074b 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -4069,7 +4069,7 @@ def safeSQLIdentificatorNaming(name, isTable=False): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, 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): + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI): retVal = "\"%s\"" % retVal elif Backend.getIdentifiedDbms() in (DBMS.ORACLE,): retVal = "\"%s\"" % retVal.upper() @@ -4107,7 +4107,7 @@ 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): + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.INFORMIX, DBMS.HSQLDB, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI): retVal = name.replace("\"", "") elif Backend.getIdentifiedDbms() in (DBMS.ORACLE,): retVal = name.replace("\"", "").upper() diff --git a/lib/core/dicts.py b/lib/core/dicts.py index 821df4b23..679dc6f5b 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -18,6 +18,7 @@ from lib.core.settings import H2_ALIASES from lib.core.settings import HSQLDB_ALIASES from lib.core.settings import INFORMIX_ALIASES from lib.core.settings import MAXDB_ALIASES +from lib.core.settings import MCKOI_ALIASES from lib.core.settings import MONETDB_ALIASES from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MYSQL_ALIASES @@ -204,6 +205,7 @@ DBMS_DICT = { DBMS.MONETDB: (MONETDB_ALIASES, "pymonetdb", "https://github.com/gijzelaerr/pymonetdb", "monetdb"), DBMS.DERBY: (DERBY_ALIASES, "pydrda", "https://github.com/nakagami/pydrda/", None), DBMS.VERTICA: (VERTICA_ALIASES, "vertica-python", "https://github.com/vertica/vertica-python", "vertica+vertica_python"), + DBMS.MCKOI: (MCKOI_ALIASES, None, None, None), } FROM_DUMMY_TABLE = { diff --git a/lib/core/enums.py b/lib/core/enums.py index dcfba516c..2d51dd080 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -48,6 +48,7 @@ class DBMS(object): MONETDB = "MonetDB" DERBY = "Apache Derby" VERTICA = "Vertica" + MCKOI = "Mckoi" class DBMS_DIRECTORY_NAME(object): ACCESS = "access" @@ -66,6 +67,7 @@ class DBMS_DIRECTORY_NAME(object): MONETDB = "monetdb" DERBY = "derby" VERTICA = "vertica" + MCKOI = "mckoi" class FORK(object): MARIADB = "MariaDB" diff --git a/lib/core/settings.py b/lib/core/settings.py index 81751c623..c61bb55c7 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.35" +VERSION = "1.4.1.36" 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) @@ -75,6 +75,7 @@ RANDOM_STRING_MARKER = "[RANDSTR]" SLEEP_TIME_MARKER = "[SLEEPTIME]" INFERENCE_MARKER = "[INFERENCE]" SINGLE_QUOTE_MARKER = "[SINGLE_QUOTE]" +GENERIC_SQL_COMMENT_MARKER = "[GENERIC_SQL_COMMENT]" PAYLOAD_DELIMITER = "__PAYLOAD_DELIMITER__" CHAR_INFERENCE_MARK = "%c" @@ -261,6 +262,7 @@ INFORMIX_SYSTEM_DBS = ("sysmaster", "sysutils", "sysuser", "sysadmin") MONETDB_SYSTEM_DBS = ("tmp", "json", "profiler") DERBY_SYSTEM_DBS = ("NULLID", "SQLJ", "SYS", "SYSCAT", "SYSCS_DIAG", "SYSCS_UTIL", "SYSFUN", "SYSIBM", "SYSPROC", "SYSSTAT") VERTICA_SYSTEM_DBS = ("v_catalog", "v_internal", "v_monitor",) +MCKOI_SYSTEM_DBS = ("",) MSSQL_ALIASES = ("microsoft sql server", "mssqlserver", "mssql", "ms") MYSQL_ALIASES = ("mysql", "my") + ("mariadb", "maria", "memsql") @@ -278,13 +280,16 @@ INFORMIX_ALIASES = ("informix", "ibm informix", "ibminformix") MONETDB_ALIASES = ("monet", "monetdb",) DERBY_ALIASES = ("derby", "apache derby",) VERTICA_ALIASES = ("vertica",) +MCKOI_ALIASES = ("mckoi",) + +UPPER_CASE_IDENTIFIERS = {DBMS.ORACLE, DBMS.DB2, DBMS.FIREBIRD, DBMS.HSQLDB, DBMS.MAXDB, DBMS.H2, DBMS.DERBY} 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 +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 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_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)) USER_AGENT_ALIASES = ("ua", "useragent", "user-agent") REFERER_ALIASES = ("ref", "referer", "referrer") @@ -293,6 +298,7 @@ HOST_ALIASES = ("host",) # Default schemas to use (when unable to enumerate) H2_DEFAULT_SCHEMA = HSQLDB_DEFAULT_SCHEMA = "PUBLIC" VERTICA_DEFAULT_SCHEMA = "public" +MCKOI_DEFAULT_SCHEMA = "APP" # Names that can't be used to name files on Windows OS WINDOWS_RESERVED_NAMES = ("CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9") diff --git a/lib/request/inject.py b/lib/request/inject.py index 0393d0327..cf42b3347 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -496,7 +496,7 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser if not any((kb.testMode, conf.dummy, conf.offline)) and value is None and Backend.getDbms() and conf.dbmsHandler and not conf.noCast and not conf.hexConvert: warnMsg = "in case of continuous data retrieval problems you are advised to try " warnMsg += "a switch '--no-cast' " - warnMsg += "or switch '--hex'" if Backend.getIdentifiedDbms() not in (DBMS.ACCESS, DBMS.FIREBIRD) else "" + warnMsg += "or switch '--hex'" if Backend.getIdentifiedDbms() not in (DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MONETDB, DBMS.MCKOI) else "" singleTimeWarnMessage(warnMsg) # Dirty patch (safe-encoded unicode characters) diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 37853748f..bd90f75bc 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -108,6 +108,14 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None return 0, retVal + if Backend.isDbms(DBMS.MCKOI): + match = re.search(r"\ASELECT\b(.+)\bFROM\b(.+)\Z", expression, re.I) + if match: + original = queries[Backend.getIdentifiedDbms()].inference.query + right = original.split('<')[1] + payload = payload.replace(right, "(SELECT %s FROM %s)" % (right, match.group(2).strip())) + expression = match.group(1).strip() + try: # Set kb.partRun in case "common prediction" feature (a.k.a. "good samaritan") is used or the engine is called from the API if conf.predictOutput: diff --git a/lib/utils/brute.py b/lib/utils/brute.py index ed2c2b661..4004bffd9 100644 --- a/lib/utils/brute.py +++ b/lib/utils/brute.py @@ -41,6 +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.threads import getCurrentThreadData from lib.core.threads import runThreads from lib.request import inject @@ -83,7 +84,7 @@ def tableExists(tableFile, regex=None): pushValue(conf.db) - if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if conf.db and Backend.getIdentifiedDbms() in UPPER_CASE_IDENTIFIERS: conf.db = conf.db.upper() message = "which common tables (wordlist) file do you want to use?\n" @@ -131,7 +132,11 @@ def tableExists(tableFile, regex=None): else: fullTableName = table - result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), fullTableName))) + if Backend.isDbms(DBMS.MCKOI): + _ = randomInt(1) + result = inject.checkBooleanExpression("%s" % safeStringFormat("%d=(SELECT %d FROM %s)", (_, _, fullTableName))) + else: + result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), fullTableName))) kb.locks.io.acquire() @@ -197,7 +202,7 @@ def columnExists(columnFile, regex=None): errMsg = "missing table parameter" raise SqlmapMissingMandatoryOptionException(errMsg) - if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if conf.db and Backend.getIdentifiedDbms() in UPPER_CASE_IDENTIFIERS: conf.db = conf.db.upper() result = inject.checkBooleanExpression(safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (randomStr(), randomStr()))) @@ -250,7 +255,10 @@ def columnExists(columnFile, regex=None): kb.locks.count.release() break - result = inject.checkBooleanExpression(safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (column, table))) + if Backend.isDbms(DBMS.MCKOI): + result = inject.checkBooleanExpression(safeStringFormat("0<(SELECT COUNT(%s) FROM %s)", (column, table))) + else: + result = inject.checkBooleanExpression(safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (column, table))) kb.locks.io.acquire() @@ -291,6 +299,8 @@ def columnExists(columnFile, regex=None): result = not inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE %s REGEXP '[^0-9]')", (column, table, column))) elif Backend.getIdentifiedDbms() in (DBMS.SQLITE,): result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE %s NOT GLOB '*[^0-9]*')", (column, table, column))) + elif Backend.getIdentifiedDbms() in (DBMS.MCKOI,): + result = inject.checkBooleanExpression("%s" % safeStringFormat("0=(SELECT MAX(%s)-MAX(%s) FROM %s)", (column, column, table))) else: result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE ROUND(%s)=ROUND(%s))", (column, table, column, column))) diff --git a/plugins/dbms/derby/takeover.py b/plugins/dbms/derby/takeover.py index 432fa6f78..cd2bf4671 100644 --- a/plugins/dbms/derby/takeover.py +++ b/plugins/dbms/derby/takeover.py @@ -5,11 +5,24 @@ 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 __init__(self): - self.__basedir = None - self.__datadir = None + def osCmd(self): + errMsg = "on Apache Derby it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) - GenericTakeover.__init__(self) + def osShell(self): + errMsg = "on Apache Derby it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Apache Derby it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Apache Derby it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/mckoi/__init__.py b/plugins/dbms/mckoi/__init__.py new file mode 100644 index 000000000..8568be2b5 --- /dev/null +++ b/plugins/dbms/mckoi/__init__.py @@ -0,0 +1,29 @@ +#!/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 MCKOI_SYSTEM_DBS +from lib.core.unescaper import unescaper +from plugins.dbms.mckoi.enumeration import Enumeration +from plugins.dbms.mckoi.filesystem import Filesystem +from plugins.dbms.mckoi.fingerprint import Fingerprint +from plugins.dbms.mckoi.syntax import Syntax +from plugins.dbms.mckoi.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class MckoiMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Mckoi methods + """ + + def __init__(self): + self.excludeDbsList = MCKOI_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.MCKOI] = Syntax.escape diff --git a/plugins/dbms/mckoi/connector.py b/plugins/dbms/mckoi/connector.py new file mode 100644 index 000000000..96c343e2e --- /dev/null +++ b/plugins/dbms/mckoi/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 Mckoi it is not (currently) possible to establish a " + errMsg += "direct connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/mckoi/enumeration.py b/plugins/dbms/mckoi/enumeration.py new file mode 100644 index 000000000..c9bd17e85 --- /dev/null +++ b/plugins/dbms/mckoi/enumeration.py @@ -0,0 +1,84 @@ +#!/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 getBanner(self): + warnMsg = "on Mckoi it is not possible to get a banner" + logger.warn(warnMsg) + + return None + + def getCurrentUser(self): + warnMsg = "on Mckoi it is not possible to enumerate the current user" + logger.warn(warnMsg) + + def getCurrentDb(self): + warnMsg = "on Mckoi it is not possible to get name of the current database" + logger.warn(warnMsg) + + def isDba(self, user=None): + warnMsg = "on Mckoi it is not possible to test if current user is DBA" + logger.warn(warnMsg) + + def getUsers(self): + warnMsg = "on Mckoi it is not possible to enumerate the users" + logger.warn(warnMsg) + + return [] + + def getPasswordHashes(self): + warnMsg = "on Mckoi it is not possible to enumerate the user password hashes" + logger.warn(warnMsg) + + return {} + + def getPrivileges(self, *args, **kwargs): + warnMsg = "on Mckoi it is not possible to enumerate the user privileges" + logger.warn(warnMsg) + + return {} + + def getDbs(self): + warnMsg = "on Mckoi it is not possible to enumerate databases (use only '--tables')" + logger.warn(warnMsg) + + return [] + + def searchDb(self): + warnMsg = "on Mckoi it is not possible to search databases" + logger.warn(warnMsg) + + return [] + + def searchTable(self): + warnMsg = "on Mckoi it is not possible to search tables" + logger.warn(warnMsg) + + return [] + + def searchColumn(self): + warnMsg = "on Mckoi it is not possible to search columns" + logger.warn(warnMsg) + + return [] + + def search(self): + warnMsg = "on Mckoi search option is not available" + logger.warn(warnMsg) + + def getHostname(self): + warnMsg = "on Mckoi it is not possible to enumerate the hostname" + logger.warn(warnMsg) + + def getStatements(self): + warnMsg = "on Mckoi it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/mckoi/filesystem.py b/plugins/dbms/mckoi/filesystem.py new file mode 100644 index 000000000..45c76d130 --- /dev/null +++ b/plugins/dbms/mckoi/filesystem.py @@ -0,0 +1,18 @@ +#!/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.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + def readFile(self, remoteFile): + errMsg = "on Mckoi it is not possible to read files" + raise SqlmapUnsupportedFeatureException(errMsg) + + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): + errMsg = "on Mckoi it is not possible to write files" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/mckoi/fingerprint.py b/plugins/dbms/mckoi/fingerprint.py new file mode 100644 index 000000000..3e1602f4c --- /dev/null +++ b/plugins/dbms/mckoi/fingerprint.py @@ -0,0 +1,93 @@ +#!/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 MCKOI_ALIASES +from lib.core.settings import MCKOI_DEFAULT_SCHEMA +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.MCKOI) + + 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.MCKOI + 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(MCKOI_ALIASES): + setDbms(DBMS.MCKOI) + return True + + infoMsg = "testing %s" % DBMS.MCKOI + logger.info(infoMsg) + + result = inject.checkBooleanExpression("DATEOB()>=DATEOB(NULL)") + + if result: + infoMsg = "confirming %s" % DBMS.MCKOI + logger.info(infoMsg) + + result = inject.checkBooleanExpression("ABS(1/0)>ABS(0/1)") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.MCKOI + logger.warn(warnMsg) + + return False + + setDbms(DBMS.MCKOI) + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.MCKOI + logger.warn(warnMsg) + + return False + + def forceDbmsEnum(self): + conf.db = MCKOI_DEFAULT_SCHEMA diff --git a/plugins/dbms/mckoi/syntax.py b/plugins/dbms/mckoi/syntax.py new file mode 100644 index 000000000..dc6c66174 --- /dev/null +++ b/plugins/dbms/mckoi/syntax.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == u"SELECT 'abcdefgh' FROM foobar" + True + """ + + return expression diff --git a/plugins/dbms/mckoi/takeover.py b/plugins/dbms/mckoi/takeover.py new file mode 100644 index 000000000..eeb4aa562 --- /dev/null +++ b/plugins/dbms/mckoi/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 Mckoi it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Mckoi it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Mckoi it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Mckoi it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/monetdb/takeover.py b/plugins/dbms/monetdb/takeover.py index 432fa6f78..921e01df7 100644 --- a/plugins/dbms/monetdb/takeover.py +++ b/plugins/dbms/monetdb/takeover.py @@ -5,11 +5,24 @@ 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 __init__(self): - self.__basedir = None - self.__datadir = None + def osCmd(self): + errMsg = "on MonetDB it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) - GenericTakeover.__init__(self) + def osShell(self): + errMsg = "on MonetDB it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on MonetDB it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on MonetDB it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/vertica/takeover.py b/plugins/dbms/vertica/takeover.py index 432fa6f78..4337628e4 100644 --- a/plugins/dbms/vertica/takeover.py +++ b/plugins/dbms/vertica/takeover.py @@ -5,11 +5,24 @@ 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 __init__(self): - self.__basedir = None - self.__datadir = None + def osCmd(self): + errMsg = "on Vertica it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) - GenericTakeover.__init__(self) + def osShell(self): + errMsg = "on Vertica it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Vertica it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Vertica 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 31be6fbae..5eb713b59 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -48,6 +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 VERTICA_DEFAULT_SCHEMA from lib.request import inject from lib.techniques.union.use import unionUse @@ -208,7 +209,10 @@ class Databases(object): logger.error(errMsg) bruteForce = True - elif Backend.isDbms(DBMS.ACCESS): + elif Backend.getIdentifiedDbms() in (DBMS.MCKOI,): + bruteForce = True + + elif Backend.getIdentifiedDbms() in (DBMS.ACCESS,): try: tables = self.getTables(False) except SqlmapNoneDataException: @@ -216,7 +220,7 @@ class Databases(object): if not tables: errMsg = "cannot retrieve table names, " - errMsg += "back-end DBMS is Access" + errMsg += "back-end DBMS is %s" % Backend.getIdentifiedDbms() logger.error(errMsg) bruteForce = True else: @@ -225,7 +229,7 @@ class Databases(object): if conf.db == CURRENT_DB: conf.db = self.getCurrentDb() - if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.DERBY): + if conf.db and Backend.getIdentifiedDbms() in UPPER_CASE_IDENTIFIERS: conf.db = conf.db.upper() if conf.db: @@ -256,7 +260,7 @@ class Databases(object): return kb.data.cachedTables - message = "do you want to use common table existence check? %s " % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") + message = "do you want to use common table existence check? %s " % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI) else "[y/N/q]") choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() if choice == 'N': @@ -348,7 +352,7 @@ class Databases(object): infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(db) logger.info(infoMsg) - if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.ACCESS): + if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.ACCESS, DBMS.MCKOI): query = rootQuery.blind.count else: query = rootQuery.blind.count % unsafeSQLIdentificatorNaming(db) @@ -375,7 +379,7 @@ class Databases(object): for index in indexRange: if Backend.isDbms(DBMS.SYBASE): query = rootQuery.blind.query % (db, (kb.data.cachedTables[-1] if kb.data.cachedTables else " ")) - elif Backend.getIdentifiedDbms() in (DBMS.MAXDB, DBMS.ACCESS): + elif Backend.getIdentifiedDbms() in (DBMS.MAXDB, DBMS.ACCESS, DBMS.MCKOI): query = rootQuery.blind.query % (kb.data.cachedTables[-1] if kb.data.cachedTables else " ") elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD): query = rootQuery.blind.query % index @@ -454,7 +458,7 @@ class Databases(object): raise SqlmapNoneDataException(errMsg) 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_IDENTIFIERS: conf.db = conf.db.upper() if ',' in conf.db: @@ -514,9 +518,9 @@ class Databases(object): logger.error(errMsg) bruteForce = True - elif Backend.isDbms(DBMS.ACCESS): + elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI): errMsg = "cannot retrieve column names, " - errMsg += "back-end DBMS is %s" % DBMS.ACCESS + errMsg += "back-end DBMS is %s" % Backend.getIdentifiedDbms() logger.error(errMsg) bruteForce = True @@ -547,7 +551,7 @@ class Databases(object): return kb.data.cachedColumns - message = "do you want to use common column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") + message = "do you want to use common column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI) else "[y/N/q]") choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() if choice == 'N': @@ -665,7 +669,7 @@ class Databases(object): if conf.getComments: _ = queries[Backend.getIdentifiedDbms()].column_comment if hasattr(_, "query"): - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY): + if Backend.getIdentifiedDbms() in UPPER_CASE_IDENTIFIERS: query = _.query % (unsafeSQLIdentificatorNaming(conf.db.upper()), unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(name.upper())) else: query = _.query % (unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(name)) @@ -829,7 +833,7 @@ class Databases(object): if conf.getComments: _ = queries[Backend.getIdentifiedDbms()].column_comment if hasattr(_, "query"): - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY): + if Backend.getIdentifiedDbms() in UPPER_CASE_IDENTIFIERS: query = _.query % (unsafeSQLIdentificatorNaming(conf.db.upper()), unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(column.upper())) else: query = _.query % (unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(column)) @@ -935,7 +939,7 @@ class Databases(object): db = db.upper() table = table.upper() - if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): + if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MCKOI): query = "SELECT %s FROM %s" % (queries[Backend.getIdentifiedDbms()].count.query % '*', safeSQLIdentificatorNaming(table, True)) else: query = "SELECT %s FROM %s.%s" % (queries[Backend.getIdentifiedDbms()].count.query % '*', safeSQLIdentificatorNaming(db), safeSQLIdentificatorNaming(table, True)) @@ -963,7 +967,7 @@ class Databases(object): if not conf.db: conf.db, conf.tbl = conf.tbl.split('.', 1) - if conf.tbl is not None and conf.db is None and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): + if conf.tbl is not None and conf.db is None and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MCKOI): warnMsg = "missing database parameter. sqlmap is going to " warnMsg += "use the current database to retrieve the " warnMsg += "number of entries for table '%s'" % unsafeSQLIdentificatorNaming(conf.tbl) diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index 5a903433f..74d664564 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -178,7 +178,7 @@ class Entries(object): if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY): 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): + elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.MCKOI): query = rootQuery.inband.query % (colString, tbl) elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): # Partial inband and error @@ -287,7 +287,7 @@ class Entries(object): if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY): 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): + elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MCKOI): query = rootQuery.blind.count % tbl elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): query = rootQuery.blind.count % ("%s.%s" % (conf.db, tbl)) @@ -325,8 +325,8 @@ class Entries(object): continue - elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL, DBMS.INFORMIX): - if Backend.isDbms(DBMS.ACCESS): + elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL, DBMS.INFORMIX, DBMS.MCKOI): + if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI): table = tbl elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): table = "%s.%s" % (conf.db, tbl) diff --git a/plugins/generic/search.py b/plugins/generic/search.py index 8da8c6786..0d359c875 100644 --- a/plugins/generic/search.py +++ b/plugins/generic/search.py @@ -148,7 +148,7 @@ class Search(object): bruteForce = True if bruteForce: - message = "do you want to use common table existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") + message = "do you want to use common table existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI) else "[y/N/q]") choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() if choice == 'N': @@ -350,7 +350,7 @@ class Search(object): bruteForce = True if bruteForce: - message = "do you want to use common column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") + message = "do you want to use common column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI) else "[y/N/q]") choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() if choice == 'N':