sqlmap/plugins/generic/databases.py

1102 lines
54 KiB
Python
Raw Permalink Normal View History

2019-05-08 13:47:52 +03:00
#!/usr/bin/env python
2012-07-20 22:17:35 +04:00
"""
2024-01-04 01:11:52 +03:00
Copyright (c) 2006-2024 sqlmap developers (https://sqlmap.org/)
2017-10-11 15:50:46 +03:00
See the file 'LICENSE' for copying permission
2012-07-20 22:17:35 +04:00
"""
2019-11-04 14:53:29 +03:00
import re
2012-07-20 22:17:35 +04:00
from lib.core.agent import agent
from lib.core.common import arrayizeValue
from lib.core.common import Backend
2019-03-29 04:28:16 +03:00
from lib.core.common import filterNone
2012-07-20 22:17:35 +04:00
from lib.core.common import filterPairValues
2014-05-17 17:00:09 +04:00
from lib.core.common import flattenValue
2012-07-20 22:17:35 +04:00
from lib.core.common import getLimitRange
from lib.core.common import isInferenceAvailable
from lib.core.common import isListLike
from lib.core.common import isNoneValue
from lib.core.common import isNumPosStrValue
from lib.core.common import isTechniqueAvailable
from lib.core.common import parseSqliteTableSchema
from lib.core.common import popValue
from lib.core.common import pushValue
from lib.core.common import readInput
from lib.core.common import safeSQLIdentificatorNaming
2020-01-17 19:14:41 +03:00
from lib.core.common import safeStringFormat
2018-02-13 17:53:50 +03:00
from lib.core.common import singleTimeLogMessage
from lib.core.common import singleTimeWarnMessage
2012-07-20 22:17:35 +04:00
from lib.core.common import unArrayizeValue
from lib.core.common import unsafeSQLIdentificatorNaming
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import paths
from lib.core.data import queries
from lib.core.decorators import stackedmethod
from lib.core.dicts import ALTIBASE_TYPES
2012-08-21 13:30:01 +04:00
from lib.core.dicts import FIREBIRD_TYPES
2016-09-23 19:03:31 +03:00
from lib.core.dicts import INFORMIX_TYPES
2012-07-20 22:17:35 +04:00
from lib.core.enums import CHARSET_TYPE
from lib.core.enums import DBMS
from lib.core.enums import EXPECTED
from lib.core.enums import FORK
2012-07-20 22:17:35 +04:00
from lib.core.enums import PAYLOAD
from lib.core.exception import SqlmapMissingMandatoryOptionException
from lib.core.exception import SqlmapNoneDataException
from lib.core.exception import SqlmapUserQuitException
2012-07-20 22:17:35 +04:00
from lib.core.settings import CURRENT_DB
from lib.core.settings import METADB_SUFFIX
from lib.core.settings import PLUS_ONE_DBMSES
2019-05-29 16:52:33 +03:00
from lib.core.settings import REFLECTED_VALUE_MARKER
2020-01-27 19:32:31 +03:00
from lib.core.settings import UPPER_CASE_DBMSES
2020-01-21 17:40:59 +03:00
from lib.core.settings import VERTICA_DEFAULT_SCHEMA
2012-07-20 22:17:35 +04:00
from lib.request import inject
2017-04-18 14:53:41 +03:00
from lib.utils.brute import columnExists
from lib.utils.brute import tableExists
2019-03-28 16:12:11 +03:00
from thirdparty import six
2012-07-20 22:17:35 +04:00
2019-05-29 17:42:04 +03:00
class Databases(object):
2012-07-20 22:17:35 +04:00
"""
This class defines databases' enumeration functionalities for plugins.
"""
def __init__(self):
kb.data.currentDb = ""
kb.data.cachedDbs = []
kb.data.cachedTables = {}
kb.data.cachedColumns = {}
kb.data.cachedCounts = {}
kb.data.dumpedTable = {}
2019-05-29 16:52:33 +03:00
kb.data.cachedStatements = []
2012-07-20 22:17:35 +04:00
def getCurrentDb(self):
infoMsg = "fetching current database"
logger.info(infoMsg)
query = queries[Backend.getIdentifiedDbms()].current_db.query
if not kb.data.currentDb:
kb.data.currentDb = unArrayizeValue(inject.getValue(query, safeCharEncode=False))
2020-01-21 17:40:59 +03:00
if not kb.data.currentDb and Backend.isDbms(DBMS.VERTICA):
kb.data.currentDb = VERTICA_DEFAULT_SCHEMA
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.PGSQL, DBMS.MONETDB, DBMS.DERBY, DBMS.VERTICA, DBMS.PRESTO, DBMS.MIMERSQL, DBMS.CRATEDB, DBMS.CACHE, DBMS.FRONTBASE, DBMS.CLICKHOUSE):
2020-02-26 19:33:47 +03:00
warnMsg = "on %s you'll need to use " % Backend.getIdentifiedDbms()
warnMsg += "schema names for enumeration as the counterpart to database "
warnMsg += "names on other DBMSes"
singleTimeWarnMessage(warnMsg)
2020-02-03 03:58:12 +03:00
elif Backend.getIdentifiedDbms() in (DBMS.ALTIBASE, DBMS.CUBRID):
2020-02-26 19:33:47 +03:00
warnMsg = "on %s you'll need to use " % Backend.getIdentifiedDbms()
2020-01-27 19:32:31 +03:00
warnMsg += "user names for enumeration as the counterpart to database "
warnMsg += "names on other DBMSes"
singleTimeWarnMessage(warnMsg)
2012-07-20 22:17:35 +04:00
return kb.data.currentDb
def getDbs(self):
if len(kb.data.cachedDbs) > 0:
return kb.data.cachedDbs
infoMsg = None
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
warnMsg = "information_schema not available, "
warnMsg += "back-end DBMS is MySQL < 5. database "
warnMsg += "names will be fetched from 'mysql' database"
logger.warning(warnMsg)
2012-07-20 22:17:35 +04:00
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.PGSQL, DBMS.MONETDB, DBMS.DERBY, DBMS.VERTICA, DBMS.PRESTO, DBMS.MIMERSQL, DBMS.CRATEDB, DBMS.CACHE, DBMS.FRONTBASE, DBMS.CLICKHOUSE):
2020-02-26 19:33:47 +03:00
warnMsg = "schema names are going to be used on %s " % Backend.getIdentifiedDbms()
2012-07-20 22:17:35 +04:00
warnMsg += "for enumeration as the counterpart to database "
warnMsg += "names on other DBMSes"
logger.warning(warnMsg)
2012-07-20 22:17:35 +04:00
infoMsg = "fetching database (schema) names"
2020-02-03 03:58:12 +03:00
elif Backend.getIdentifiedDbms() in (DBMS.ALTIBASE, DBMS.CUBRID):
2020-02-26 19:33:47 +03:00
warnMsg = "user names are going to be used on %s " % Backend.getIdentifiedDbms()
2020-01-27 19:32:31 +03:00
warnMsg += "for enumeration as the counterpart to database "
warnMsg += "names on other DBMSes"
logger.warning(warnMsg)
2020-01-27 19:32:31 +03:00
infoMsg = "fetching database (user) names"
2012-07-20 22:17:35 +04:00
else:
infoMsg = "fetching database names"
if infoMsg:
logger.info(infoMsg)
rootQuery = queries[Backend.getIdentifiedDbms()].dbs
2012-12-05 13:45:17 +04:00
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:
2012-07-20 22:17:35 +04:00
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
query = rootQuery.inband.query2
else:
query = rootQuery.inband.query
values = inject.getValue(query, blind=False, time=False)
2012-07-20 22:17:35 +04:00
if not isNoneValue(values):
kb.data.cachedDbs = arrayizeValue(values)
2012-07-20 22:17:35 +04:00
if not kb.data.cachedDbs and isInferenceAvailable() and not conf.direct:
infoMsg = "fetching number of databases"
logger.info(infoMsg)
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
query = rootQuery.blind.count2
else:
query = rootQuery.blind.count
count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
2012-07-20 22:17:35 +04:00
if not isNumPosStrValue(count):
errMsg = "unable to retrieve the number of databases"
logger.error(errMsg)
else:
plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES
2012-07-20 22:17:35 +04:00
indexRange = getLimitRange(count, plusOne=plusOne)
for index in indexRange:
if Backend.isDbms(DBMS.SYBASE):
query = rootQuery.blind.query % (kb.data.cachedDbs[-1] if kb.data.cachedDbs else " ")
elif Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
query = rootQuery.blind.query2 % index
else:
query = rootQuery.blind.query % index
2019-05-29 16:52:33 +03:00
db = unArrayizeValue(inject.getValue(query, union=False, error=False))
2012-07-20 22:17:35 +04:00
2019-05-29 16:52:33 +03:00
if not isNoneValue(db):
2012-07-20 22:17:35 +04:00
kb.data.cachedDbs.append(safeSQLIdentificatorNaming(db))
if not kb.data.cachedDbs and Backend.isDbms(DBMS.MSSQL):
2012-12-05 13:45:17 +04:00
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:
2012-07-20 22:17:35 +04:00
blinds = (False, True)
else:
blinds = (True,)
for blind in blinds:
count = 0
kb.data.cachedDbs = []
while True:
query = rootQuery.inband.query2 % count
value = unArrayizeValue(inject.getValue(query, blind=blind))
if not (value or "").strip():
2012-07-20 22:17:35 +04:00
break
else:
kb.data.cachedDbs.append(value)
2012-07-20 22:17:35 +04:00
count += 1
if kb.data.cachedDbs:
break
if not kb.data.cachedDbs:
infoMsg = "falling back to current database"
logger.info(infoMsg)
self.getCurrentDb()
if kb.data.currentDb:
kb.data.cachedDbs = [kb.data.currentDb]
else:
errMsg = "unable to retrieve the database names"
raise SqlmapNoneDataException(errMsg)
2012-07-20 22:17:35 +04:00
else:
kb.data.cachedDbs.sort()
2012-12-18 12:55:33 +04:00
if kb.data.cachedDbs:
kb.data.cachedDbs = [_ for _ in set(flattenValue(kb.data.cachedDbs)) if _]
2012-12-18 12:55:33 +04:00
2012-07-20 22:17:35 +04:00
return kb.data.cachedDbs
def getTables(self, bruteForce=None):
if len(kb.data.cachedTables) > 0:
return kb.data.cachedTables
self.forceDbmsEnum()
if bruteForce is None:
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
2020-02-26 23:47:10 +03:00
warnMsg = "information_schema not available, "
warnMsg += "back-end DBMS is MySQL < 5.0"
logger.warning(warnMsg)
2012-07-20 22:17:35 +04:00
bruteForce = True
elif Backend.getIdentifiedDbms() in (DBMS.MCKOI, DBMS.EXTREMEDB, DBMS.RAIMA):
2020-01-23 01:41:06 +03:00
bruteForce = True
elif Backend.getIdentifiedDbms() in (DBMS.ACCESS,):
2012-07-20 22:17:35 +04:00
try:
tables = self.getTables(False)
except SqlmapNoneDataException:
2012-07-20 22:17:35 +04:00
tables = None
if not tables:
2020-02-26 23:47:10 +03:00
warnMsg = "cannot retrieve table names, "
warnMsg += "back-end DBMS is %s" % Backend.getIdentifiedDbms()
logger.warning(warnMsg)
2012-07-20 22:17:35 +04:00
bruteForce = True
else:
return tables
if conf.db == CURRENT_DB:
conf.db = self.getCurrentDb()
2020-01-27 19:32:31 +03:00
if conf.db and Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES:
2012-07-20 22:17:35 +04:00
conf.db = conf.db.upper()
if conf.db:
2017-04-18 16:56:24 +03:00
dbs = conf.db.split(',')
2012-07-20 22:17:35 +04:00
else:
dbs = self.getDbs()
2014-06-22 02:19:10 +04:00
dbs = [_ for _ in dbs if _ and _.strip()]
2012-07-20 22:17:35 +04:00
for db in dbs:
dbs[dbs.index(db)] = safeSQLIdentificatorNaming(db)
if bruteForce:
resumeAvailable = False
for db, table in kb.brute.tables:
if db == conf.db:
resumeAvailable = True
break
2014-02-05 18:11:39 +04:00
if resumeAvailable and not conf.freshQueries:
2012-07-20 22:17:35 +04:00
for db, table in kb.brute.tables:
if db == conf.db:
if conf.db not in kb.data.cachedTables:
kb.data.cachedTables[conf.db] = [table]
else:
kb.data.cachedTables[conf.db].append(table)
return kb.data.cachedTables
2020-02-26 19:33:47 +03:00
message = "do you want to use common table existence check? %s " % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB) else "[y/N/q]")
2017-04-19 15:46:27 +03:00
choice = readInput(message, default='Y' if 'Y' in message else 'N').upper()
2012-07-20 22:17:35 +04:00
2017-04-18 16:48:05 +03:00
if choice == 'N':
2012-07-20 22:17:35 +04:00
return
2017-04-18 16:48:05 +03:00
elif choice == 'Q':
raise SqlmapUserQuitException
2012-07-20 22:17:35 +04:00
else:
return tableExists(paths.COMMON_TABLES)
infoMsg = "fetching tables for database"
2013-02-15 19:48:58 +04:00
infoMsg += "%s: '%s'" % ("s" if len(dbs) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(unArrayizeValue(db)) for db in sorted(dbs)))
2012-07-20 22:17:35 +04:00
logger.info(infoMsg)
rootQuery = queries[Backend.getIdentifiedDbms()].tables
2012-12-05 13:45:17 +04:00
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:
values = []
2012-07-20 22:17:35 +04:00
for query, condition in ((rootQuery.inband.query, getattr(rootQuery.inband, "condition", None)), (getattr(rootQuery.inband, "query2", None), getattr(rootQuery.inband, "condition2", None))):
if not isNoneValue(values) or not query:
break
2012-12-17 18:07:28 +04:00
if condition:
if not Backend.isDbms(DBMS.SQLITE):
query += " WHERE %s" % condition
2012-07-20 22:17:35 +04:00
if conf.excludeSysDbs:
infoMsg = "skipping system database%s '%s'" % ("s" if len(self.excludeDbsList) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(db) for db in self.excludeDbsList))
logger.info(infoMsg)
query += " IN (%s)" % ','.join("'%s'" % unsafeSQLIdentificatorNaming(db) for db in sorted(dbs) if db not in self.excludeDbsList)
else:
query += " IN (%s)" % ','.join("'%s'" % unsafeSQLIdentificatorNaming(db) for db in sorted(dbs))
2012-07-20 22:17:35 +04:00
if len(dbs) < 2 and ("%s," % condition) in query:
query = query.replace("%s," % condition, "", 1)
if query:
values = inject.getValue(query, blind=False, time=False)
2012-07-20 22:17:35 +04:00
if not isNoneValue(values):
values = [_ for _ in arrayizeValue(values) if _]
2012-07-20 22:17:35 +04:00
if len(values) > 0 and not isListLike(values[0]):
2013-01-11 14:17:41 +04:00
values = [(dbs[0], _) for _ in values]
2012-07-20 22:17:35 +04:00
for db, table in filterPairValues(values):
2024-06-24 19:19:24 +03:00
table = unArrayizeValue(table)
2019-05-17 01:34:11 +03:00
if not isNoneValue(table):
db = safeSQLIdentificatorNaming(db)
2024-06-24 19:19:24 +03:00
table = safeSQLIdentificatorNaming(table, True).strip()
2019-05-17 01:34:11 +03:00
if conf.getComments:
_ = queries[Backend.getIdentifiedDbms()].table_comment
if hasattr(_, "query"):
2020-01-27 19:32:31 +03:00
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE):
2019-05-17 01:34:11 +03:00
query = _.query % (unsafeSQLIdentificatorNaming(db.upper()), unsafeSQLIdentificatorNaming(table.upper()))
else:
query = _.query % (unsafeSQLIdentificatorNaming(db), unsafeSQLIdentificatorNaming(table))
comment = unArrayizeValue(inject.getValue(query, blind=False, time=False))
if not isNoneValue(comment):
infoMsg = "retrieved comment '%s' for table '%s'" % (comment, unsafeSQLIdentificatorNaming(table))
if METADB_SUFFIX not in db:
infoMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(db)
2019-05-17 01:34:11 +03:00
logger.info(infoMsg)
2018-05-22 00:44:21 +03:00
else:
2020-02-26 19:33:47 +03:00
warnMsg = "on %s it is not " % Backend.getIdentifiedDbms()
2019-06-01 13:38:37 +03:00
warnMsg += "possible to get table comments"
2019-05-17 01:34:11 +03:00
singleTimeWarnMessage(warnMsg)
2018-05-22 00:44:21 +03:00
2019-05-17 01:34:11 +03:00
if db not in kb.data.cachedTables:
kb.data.cachedTables[db] = [table]
2018-05-22 00:44:21 +03:00
else:
2019-05-17 01:34:11 +03:00
kb.data.cachedTables[db].append(table)
2012-07-20 22:17:35 +04:00
if not kb.data.cachedTables and isInferenceAvailable() and not conf.direct:
for db in dbs:
if conf.excludeSysDbs and db in self.excludeDbsList:
2013-02-15 19:48:58 +04:00
infoMsg = "skipping system database '%s'" % unsafeSQLIdentificatorNaming(db)
2012-07-20 22:17:35 +04:00
logger.info(infoMsg)
2018-02-13 17:53:50 +03:00
continue
2012-07-20 22:17:35 +04:00
2019-11-04 14:53:29 +03:00
if conf.exclude and re.search(conf.exclude, db, re.I) is not None:
2018-02-13 17:53:50 +03:00
infoMsg = "skipping database '%s'" % unsafeSQLIdentificatorNaming(db)
singleTimeLogMessage(infoMsg)
2012-07-20 22:17:35 +04:00
continue
2021-08-19 00:16:19 +03:00
for _query, _count in ((rootQuery.blind.query, rootQuery.blind.count), (getattr(rootQuery.blind, "query2", None), getattr(rootQuery.blind, "count2", None))):
if _query is None:
break
2012-07-20 22:17:35 +04:00
infoMsg = "fetching number of tables for "
infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(db)
logger.info(infoMsg)
2012-07-20 22:17:35 +04:00
if Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB):
2021-08-19 00:16:19 +03:00
query = _count % unsafeSQLIdentificatorNaming(db)
else:
query = _count
2012-10-23 12:33:30 +04:00
2021-08-19 00:16:19 +03:00
count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
2012-07-20 22:17:35 +04:00
if count == 0:
warnMsg = "database '%s' " % unsafeSQLIdentificatorNaming(db)
warnMsg += "appears to be empty"
logger.warning(warnMsg)
break
2012-07-20 22:17:35 +04:00
elif not isNumPosStrValue(count):
warnMsg = "unable to retrieve the number of "
warnMsg += "tables for database '%s'" % unsafeSQLIdentificatorNaming(db)
singleTimeWarnMessage(warnMsg)
continue
tables = []
plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES
indexRange = getLimitRange(count, plusOne=plusOne)
for index in indexRange:
if Backend.isDbms(DBMS.SYBASE):
2021-08-19 00:16:19 +03:00
query = _query % (db, (kb.data.cachedTables[-1] if kb.data.cachedTables else " "))
elif Backend.getIdentifiedDbms() in (DBMS.MAXDB, DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB):
2021-08-19 00:16:19 +03:00
query = _query % (kb.data.cachedTables[-1] if kb.data.cachedTables else " ")
elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD):
2021-08-19 00:16:19 +03:00
query = _query % index
elif Backend.getIdentifiedDbms() in (DBMS.HSQLDB, DBMS.INFORMIX, DBMS.FRONTBASE, DBMS.VIRTUOSO):
2021-08-19 00:16:19 +03:00
query = _query % (index, unsafeSQLIdentificatorNaming(db))
else:
2021-08-19 00:16:19 +03:00
query = _query % (unsafeSQLIdentificatorNaming(db), index)
2012-07-20 22:17:35 +04:00
table = unArrayizeValue(inject.getValue(query, union=False, error=False))
2012-07-20 22:17:35 +04:00
if not isNoneValue(table):
kb.hintValue = table
table = safeSQLIdentificatorNaming(table, True)
tables.append(table)
2019-05-29 16:52:33 +03:00
if tables:
kb.data.cachedTables[db] = tables
2012-07-20 22:17:35 +04:00
2018-05-22 00:44:21 +03:00
if conf.getComments:
for table in tables:
_ = queries[Backend.getIdentifiedDbms()].table_comment
if hasattr(_, "query"):
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))
comment = unArrayizeValue(inject.getValue(query, union=False, error=False))
if not isNoneValue(comment):
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)
2018-05-22 00:44:21 +03:00
else:
warnMsg = "on %s it is not " % Backend.getIdentifiedDbms()
warnMsg += "possible to get table comments"
singleTimeWarnMessage(warnMsg)
2018-05-22 00:44:21 +03:00
break
else:
warnMsg = "unable to retrieve the table names "
warnMsg += "for database '%s'" % unsafeSQLIdentificatorNaming(db)
logger.warning(warnMsg)
2012-07-20 22:17:35 +04:00
if isNoneValue(kb.data.cachedTables):
kb.data.cachedTables.clear()
if not kb.data.cachedTables:
errMsg = "unable to retrieve the table names for any database"
if bruteForce is None:
logger.error(errMsg)
return self.getTables(bruteForce=True)
elif not conf.search:
raise SqlmapNoneDataException(errMsg)
2012-07-20 22:17:35 +04:00
else:
for db, tables in kb.data.cachedTables.items():
kb.data.cachedTables[db] = sorted(tables) if tables else tables
2012-12-18 12:55:33 +04:00
if kb.data.cachedTables:
for db in kb.data.cachedTables:
2012-12-18 12:55:33 +04:00
kb.data.cachedTables[db] = list(set(kb.data.cachedTables[db]))
2012-07-20 22:17:35 +04:00
return kb.data.cachedTables
def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMode=False):
2012-07-20 22:17:35 +04:00
self.forceDbmsEnum()
if conf.db is None or conf.db == CURRENT_DB:
if conf.db is None:
2012-10-04 20:28:36 +04:00
warnMsg = "missing database parameter. sqlmap is going "
2012-07-20 22:17:35 +04:00
warnMsg += "to use the current database to enumerate "
warnMsg += "table(s) columns"
logger.warning(warnMsg)
2012-07-20 22:17:35 +04:00
conf.db = self.getCurrentDb()
if not conf.db:
errMsg = "unable to retrieve the current "
errMsg += "database name"
raise SqlmapNoneDataException(errMsg)
2012-07-20 22:17:35 +04:00
elif conf.db is not None:
2020-01-27 19:32:31 +03:00
if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES:
2012-07-20 22:17:35 +04:00
conf.db = conf.db.upper()
if ',' in conf.db:
2012-07-20 22:17:35 +04:00
errMsg = "only one database name is allowed when enumerating "
errMsg += "the tables' columns"
raise SqlmapMissingMandatoryOptionException(errMsg)
2012-07-20 22:17:35 +04:00
conf.db = safeSQLIdentificatorNaming(conf.db)
if conf.col:
2020-01-27 19:32:31 +03:00
if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES:
2012-07-20 22:17:35 +04:00
conf.col = conf.col.upper()
2014-01-13 13:05:49 +04:00
colList = conf.col.split(',')
2012-07-20 22:17:35 +04:00
else:
colList = []
2018-02-13 17:53:50 +03:00
if conf.exclude:
2019-11-04 14:53:29 +03:00
colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None]
2014-01-13 13:05:49 +04:00
2012-07-20 22:17:35 +04:00
for col in colList:
colList[colList.index(col)] = safeSQLIdentificatorNaming(col)
colList = [_ for _ in colList if _]
2012-07-20 22:17:35 +04:00
if conf.tbl:
2020-01-27 19:32:31 +03:00
if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES:
2012-07-20 22:17:35 +04:00
conf.tbl = conf.tbl.upper()
2017-04-18 16:56:24 +03:00
tblList = conf.tbl.split(',')
2012-07-20 22:17:35 +04:00
else:
self.getTables()
if len(kb.data.cachedTables) > 0:
if conf.db in kb.data.cachedTables:
tblList = kb.data.cachedTables[conf.db]
else:
2019-05-15 11:30:47 +03:00
tblList = list(six.itervalues(kb.data.cachedTables))
2012-07-20 22:17:35 +04:00
2019-05-15 11:30:47 +03:00
if tblList and isListLike(tblList[0]):
2012-07-20 22:17:35 +04:00
tblList = tblList[0]
tblList = list(tblList)
elif not conf.search:
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
2012-07-20 22:17:35 +04:00
2020-09-23 16:22:07 +03:00
if conf.exclude:
tblList = [_ for _ in tblList if re.search(conf.exclude, _, re.I) is None]
2019-03-29 04:28:16 +03:00
tblList = filterNone(safeSQLIdentificatorNaming(_, True) for _ in tblList)
2012-07-20 22:17:35 +04:00
if bruteForce is None:
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
2020-02-26 23:47:10 +03:00
warnMsg = "information_schema not available, "
warnMsg += "back-end DBMS is MySQL < 5.0"
logger.warning(warnMsg)
2012-07-20 22:17:35 +04:00
bruteForce = True
elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB, DBMS.RAIMA):
2020-02-26 23:47:10 +03:00
warnMsg = "cannot retrieve column names, "
2021-01-12 15:23:08 +03:00
warnMsg += "back-end DBMS is %s" % Backend.getIdentifiedDbms()
2021-01-12 15:21:51 +03:00
singleTimeWarnMessage(warnMsg)
2012-07-20 22:17:35 +04:00
bruteForce = True
2012-12-18 19:31:30 +04:00
if bruteForce:
2012-07-20 22:17:35 +04:00
resumeAvailable = False
for tbl in tblList:
for db, table, colName, colType in kb.brute.columns:
if db == conf.db and table == tbl:
resumeAvailable = True
break
if resumeAvailable and not (conf.freshQueries and not colList):
2012-07-20 22:17:35 +04:00
columns = {}
for column in colList:
columns[column] = None
for tbl in tblList:
for db, table, colName, colType in kb.brute.columns:
if db == conf.db and table == tbl:
columns[colName] = colType
if conf.db in kb.data.cachedColumns:
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] = columns
else:
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = {safeSQLIdentificatorNaming(tbl, True): columns}
return kb.data.cachedColumns
2021-01-12 15:21:51 +03:00
if kb.choices.columnExists is None:
message = "do you want to use common column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB) else "[y/N/q]")
kb.choices.columnExists = readInput(message, default='Y' if 'Y' in message else 'N').upper()
2012-07-20 22:17:35 +04:00
2021-01-12 15:21:51 +03:00
if kb.choices.columnExists == 'N':
if dumpMode and colList:
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = {safeSQLIdentificatorNaming(tbl, True): dict((_, None) for _ in colList)}
return kb.data.cachedColumns
else:
return None
elif kb.choices.columnExists == 'Q':
raise SqlmapUserQuitException
2012-07-20 22:17:35 +04:00
else:
return columnExists(paths.COMMON_COLUMNS)
rootQuery = queries[Backend.getIdentifiedDbms()].columns
condition = rootQuery.blind.condition if 'condition' in rootQuery.blind else None
2012-12-05 13:45:17 +04:00
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:
2012-07-20 22:17:35 +04:00
for tbl in tblList:
if conf.db is not None and len(kb.data.cachedColumns) > 0 \
and conf.db in kb.data.cachedColumns and tbl in \
kb.data.cachedColumns[conf.db]:
2021-03-11 13:11:29 +03:00
infoMsg = "fetched table columns from "
2012-07-20 22:17:35 +04:00
infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
logger.info(infoMsg)
return {conf.db: kb.data.cachedColumns[conf.db]}
infoMsg = "fetching columns "
2012-12-18 19:31:30 +04:00
condQuery = ""
2012-07-20 22:17:35 +04:00
if len(colList) > 0:
2012-12-18 19:31:30 +04:00
if colTuple:
_, colCondParam = colTuple
2015-09-22 13:03:47 +03:00
infoMsg += "LIKE '%s' " % ", ".join(unsafeSQLIdentificatorNaming(col) for col in sorted(colList))
2012-07-20 22:17:35 +04:00
else:
2012-12-18 19:31:30 +04:00
colCondParam = "='%s'"
2012-07-20 22:17:35 +04:00
infoMsg += "'%s' " % ", ".join(unsafeSQLIdentificatorNaming(col) for col in sorted(colList))
2012-12-18 19:31:30 +04:00
condQueryStr = "%%s%s" % colCondParam
condQuery = " AND (%s)" % " OR ".join(condQueryStr % (condition, unsafeSQLIdentificatorNaming(col)) for col in sorted(colList))
2012-07-20 22:17:35 +04:00
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.MONETDB, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CUBRID, DBMS.CACHE, DBMS.FRONTBASE, DBMS.VIRTUOSO, DBMS.CLICKHOUSE):
2012-07-20 22:17:35 +04:00
query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db))
query += condQuery
2019-06-21 11:15:36 +03:00
2021-07-05 00:07:55 +03:00
if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE):
2021-03-11 13:11:29 +03:00
query = re.sub("column_type", "data_type", query, flags=re.I)
2020-01-31 13:33:31 +03:00
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL):
2013-01-19 00:44:56 +04:00
query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper()))
2012-07-20 22:17:35 +04:00
query += condQuery
2019-06-21 11:15:36 +03:00
2012-07-20 22:17:35 +04:00
elif Backend.isDbms(DBMS.MSSQL):
query = rootQuery.inband.query % (conf.db, conf.db, conf.db, conf.db,
conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1])
query += condQuery.replace("[DB]", conf.db)
2019-06-21 11:15:36 +03:00
2013-01-19 02:10:10 +04:00
elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD):
2017-09-05 00:00:16 +03:00
query = rootQuery.inband.query % unsafeSQLIdentificatorNaming(tbl)
2013-06-24 18:03:08 +04:00
2019-06-21 11:15:36 +03:00
elif Backend.isDbms(DBMS.INFORMIX):
query = rootQuery.inband.query % (conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl))
query += condQuery
if dumpMode and colList:
values = [(_,) for _ in colList]
else:
infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl)
if METADB_SUFFIX not in conf.db:
infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
logger.info(infoMsg)
values = None
if values is None:
values = inject.getValue(query, blind=False, time=False)
if values and isinstance(values[0], six.string_types):
2018-08-22 18:58:00 +03:00
values = [values]
2012-07-20 22:17:35 +04:00
2013-01-11 14:17:41 +04:00
if Backend.isDbms(DBMS.MSSQL) and isNoneValue(values):
index, values = 1, []
2013-01-19 00:40:38 +04:00
2013-01-11 14:17:41 +04:00
while True:
2017-09-05 00:00:16 +03:00
query = rootQuery.inband.query2 % (conf.db, unsafeSQLIdentificatorNaming(tbl), index)
2013-01-11 14:17:41 +04:00
value = unArrayizeValue(inject.getValue(query, blind=False, time=False))
2013-01-19 00:40:38 +04:00
2013-01-11 14:17:41 +04:00
if isNoneValue(value) or value == " ":
break
else:
values.append((value,))
index += 1
2012-07-20 22:17:35 +04:00
if Backend.isDbms(DBMS.SQLITE):
2019-02-07 19:33:16 +03:00
if dumpMode and colList:
if conf.db not in kb.data.cachedColumns:
kb.data.cachedColumns[conf.db] = {}
2019-02-07 19:34:51 +03:00
kb.data.cachedColumns[conf.db][safeSQLIdentificatorNaming(conf.tbl, True)] = dict((_, None) for _ in colList)
2019-02-07 19:33:16 +03:00
else:
parseSqliteTableSchema(unArrayizeValue(values))
elif not isNoneValue(values):
2012-07-20 22:17:35 +04:00
table = {}
columns = {}
for columnData in values:
2012-07-20 22:17:35 +04:00
if not isNoneValue(columnData):
2019-05-09 14:14:42 +03:00
columnData = [unArrayizeValue(_) for _ in columnData]
2012-07-20 22:17:35 +04:00
name = safeSQLIdentificatorNaming(columnData[0])
if name:
2013-07-29 20:25:27 +04:00
if conf.getComments:
_ = queries[Backend.getIdentifiedDbms()].column_comment
if hasattr(_, "query"):
2020-01-27 19:32:31 +03:00
if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES:
2013-07-29 20:25:27 +04:00
query = _.query % (unsafeSQLIdentificatorNaming(conf.db.upper()), unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(name.upper()))
else:
query = _.query % (unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(name))
2016-04-19 14:13:37 +03:00
2013-07-29 20:25:27 +04:00
comment = unArrayizeValue(inject.getValue(query, blind=False, time=False))
2016-04-19 14:13:37 +03:00
if not isNoneValue(comment):
infoMsg = "retrieved comment '%s' for column '%s'" % (comment, name)
logger.info(infoMsg)
2013-07-29 20:25:27 +04:00
else:
2020-02-26 19:33:47 +03:00
warnMsg = "on %s it is not " % Backend.getIdentifiedDbms()
2013-07-29 20:25:27 +04:00
warnMsg += "possible to get column comments"
singleTimeWarnMessage(warnMsg)
2012-07-20 22:17:35 +04:00
if len(columnData) == 1:
2013-01-11 14:17:41 +04:00
columns[name] = None
2012-07-20 22:17:35 +04:00
else:
key = int(columnData[1]) if isinstance(columnData[1], six.string_types) and columnData[1].isdigit() else columnData[1]
if Backend.isDbms(DBMS.FIREBIRD):
2016-09-23 19:03:31 +03:00
columnData[1] = FIREBIRD_TYPES.get(key, columnData[1])
elif Backend.isDbms(DBMS.ALTIBASE):
columnData[1] = ALTIBASE_TYPES.get(key, columnData[1])
2016-09-23 19:03:31 +03:00
elif Backend.isDbms(DBMS.INFORMIX):
notNull = False
if isinstance(key, int) and key > 255:
key -= 256
notNull = True
columnData[1] = INFORMIX_TYPES.get(key, columnData[1])
if notNull:
columnData[1] = "%s NOT NULL" % columnData[1]
2012-07-20 22:17:35 +04:00
columns[name] = columnData[1]
if conf.db in kb.data.cachedColumns:
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] = columns
else:
table[safeSQLIdentificatorNaming(tbl, True)] = columns
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table
elif isInferenceAvailable() and not conf.direct:
for tbl in tblList:
if conf.db is not None and len(kb.data.cachedColumns) > 0 \
and conf.db in kb.data.cachedColumns and tbl in \
kb.data.cachedColumns[conf.db]:
2021-03-11 13:11:29 +03:00
infoMsg = "fetched table columns from "
2013-02-15 19:48:58 +04:00
infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
2012-07-20 22:17:35 +04:00
logger.info(infoMsg)
return {conf.db: kb.data.cachedColumns[conf.db]}
infoMsg = "fetching columns "
2012-12-18 19:31:30 +04:00
condQuery = ""
2012-07-20 22:17:35 +04:00
if len(colList) > 0:
2012-12-18 19:31:30 +04:00
if colTuple:
_, colCondParam = colTuple
2015-09-22 13:03:47 +03:00
infoMsg += "LIKE '%s' " % ", ".join(unsafeSQLIdentificatorNaming(col) for col in sorted(colList))
2012-07-20 22:17:35 +04:00
else:
2012-12-18 19:31:30 +04:00
colCondParam = "='%s'"
2012-07-20 22:17:35 +04:00
infoMsg += "'%s' " % ", ".join(unsafeSQLIdentificatorNaming(col) for col in sorted(colList))
2012-12-18 19:31:30 +04:00
condQueryStr = "%%s%s" % colCondParam
condQuery = " AND (%s)" % " OR ".join(condQueryStr % (condition, unsafeSQLIdentificatorNaming(col)) for col in sorted(colList))
2012-07-20 22:17:35 +04:00
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.MONETDB, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CUBRID, DBMS.CACHE, DBMS.FRONTBASE, DBMS.VIRTUOSO, DBMS.CLICKHOUSE):
2012-07-20 22:17:35 +04:00
query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db))
query += condQuery
2020-01-31 13:33:31 +03:00
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL):
2013-01-19 00:44:56 +04:00
query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper()))
2012-07-20 22:17:35 +04:00
query += condQuery
elif Backend.isDbms(DBMS.MSSQL):
query = rootQuery.blind.count % (conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1])
2012-07-20 22:17:35 +04:00
query += condQuery.replace("[DB]", conf.db)
elif Backend.isDbms(DBMS.FIREBIRD):
2017-09-05 00:00:16 +03:00
query = rootQuery.blind.count % unsafeSQLIdentificatorNaming(tbl)
2012-07-20 22:17:35 +04:00
query += condQuery
elif Backend.isDbms(DBMS.INFORMIX):
2017-09-05 00:00:16 +03:00
query = rootQuery.blind.count % (conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl))
query += condQuery
2012-07-20 22:17:35 +04:00
elif Backend.isDbms(DBMS.SQLITE):
2019-02-07 19:33:16 +03:00
if dumpMode and colList:
if conf.db not in kb.data.cachedColumns:
kb.data.cachedColumns[conf.db] = {}
2019-02-07 19:34:51 +03:00
kb.data.cachedColumns[conf.db][safeSQLIdentificatorNaming(conf.tbl, True)] = dict((_, None) for _ in colList)
2019-02-07 19:33:16 +03:00
else:
query = rootQuery.blind.query % unsafeSQLIdentificatorNaming(tbl)
value = unArrayizeValue(inject.getValue(query, union=False, error=False))
parseSqliteTableSchema(unArrayizeValue(value))
2012-07-20 22:17:35 +04:00
return kb.data.cachedColumns
table = {}
columns = {}
if dumpMode and colList:
count = 0
for value in colList:
columns[safeSQLIdentificatorNaming(value)] = None
else:
infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl)
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)
if not isNumPosStrValue(count):
if Backend.isDbms(DBMS.MSSQL):
count, index, values = 0, 1, []
while True:
2017-09-05 00:00:16 +03:00
query = rootQuery.blind.query3 % (conf.db, unsafeSQLIdentificatorNaming(tbl), index)
value = unArrayizeValue(inject.getValue(query, union=False, error=False))
2019-05-29 16:52:33 +03:00
if isNoneValue(value) or value == " ":
break
else:
columns[safeSQLIdentificatorNaming(value)] = None
index += 1
2013-01-11 14:17:41 +04:00
if not columns:
errMsg = "unable to retrieve the %scolumns " % ("number of " if not Backend.isDbms(DBMS.MSSQL) else "")
errMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl)
if METADB_SUFFIX not in conf.db:
errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
logger.error(errMsg)
continue
2013-01-11 14:17:41 +04:00
2012-09-07 19:06:38 +04:00
for index in getLimitRange(count):
2021-02-15 16:07:04 +03:00
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CUBRID, DBMS.CACHE, DBMS.FRONTBASE, DBMS.VIRTUOSO):
2012-07-20 22:17:35 +04:00
query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db))
query += condQuery
field = None
2018-10-16 15:47:09 +03:00
elif Backend.isDbms(DBMS.H2):
query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db))
query = query.replace(" ORDER BY ", "%s ORDER BY " % condQuery)
field = None
2020-01-31 23:24:20 +03:00
elif Backend.isDbms(DBMS.MIMERSQL):
query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper()))
query = query.replace(" ORDER BY ", "%s ORDER BY " % condQuery)
field = None
elif Backend.getIdentifiedDbms() in (DBMS.MONETDB, DBMS.CLICKHOUSE):
2020-01-17 19:14:41 +03:00
query = safeStringFormat(rootQuery.blind.query, (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db), index))
field = None
2020-01-31 23:24:20 +03:00
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE):
2013-01-19 00:44:56 +04:00
query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper()))
2012-07-20 22:17:35 +04:00
query += condQuery
field = None
elif Backend.isDbms(DBMS.MSSQL):
2012-09-07 19:06:38 +04:00
query = rootQuery.blind.query.replace("'%s'", "'%s'" % unsafeSQLIdentificatorNaming(tbl).split(".")[-1]).replace("%s", conf.db).replace("%d", str(index))
2012-07-20 22:17:35 +04:00
query += condQuery.replace("[DB]", conf.db)
field = condition.replace("[DB]", conf.db)
elif Backend.isDbms(DBMS.FIREBIRD):
2017-09-05 00:00:16 +03:00
query = rootQuery.blind.query % unsafeSQLIdentificatorNaming(tbl)
2012-07-20 22:17:35 +04:00
query += condQuery
field = None
elif Backend.isDbms(DBMS.INFORMIX):
2017-09-05 00:00:16 +03:00
query = rootQuery.blind.query % (index, conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl))
query += condQuery
field = condition
2012-07-20 22:17:35 +04:00
2012-09-07 19:06:38 +04:00
query = agent.limitQuery(index, query, field, field)
column = unArrayizeValue(inject.getValue(query, union=False, error=False))
2012-07-20 22:17:35 +04:00
if not isNoneValue(column):
2013-07-29 20:25:27 +04:00
if conf.getComments:
_ = queries[Backend.getIdentifiedDbms()].column_comment
if hasattr(_, "query"):
2020-01-27 19:32:31 +03:00
if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES:
2013-07-29 20:25:27 +04:00
query = _.query % (unsafeSQLIdentificatorNaming(conf.db.upper()), unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(column.upper()))
else:
query = _.query % (unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(column))
2016-04-19 14:13:37 +03:00
2013-07-29 20:25:27 +04:00
comment = unArrayizeValue(inject.getValue(query, union=False, error=False))
2016-04-19 14:13:37 +03:00
if not isNoneValue(comment):
infoMsg = "retrieved comment '%s' for column '%s'" % (comment, column)
logger.info(infoMsg)
2013-07-29 20:25:27 +04:00
else:
2020-02-26 19:33:47 +03:00
warnMsg = "on %s it is not " % Backend.getIdentifiedDbms()
2013-07-29 20:25:27 +04:00
warnMsg += "possible to get column comments"
singleTimeWarnMessage(warnMsg)
2012-07-20 22:17:35 +04:00
if not onlyColNames:
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.FRONTBASE, DBMS.VIRTUOSO, DBMS.CLICKHOUSE):
2012-07-20 22:17:35 +04:00
query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl), column, unsafeSQLIdentificatorNaming(conf.db))
2020-01-31 13:33:31 +03:00
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL):
2013-01-19 00:44:56 +04:00
query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl.upper()), column, unsafeSQLIdentificatorNaming(conf.db.upper()))
2012-07-20 22:17:35 +04:00
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])
2012-07-20 22:17:35 +04:00
elif Backend.isDbms(DBMS.FIREBIRD):
2017-09-05 00:00:16 +03:00
query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl), column)
2016-09-23 19:03:31 +03:00
elif Backend.isDbms(DBMS.INFORMIX):
2017-09-05 00:00:16 +03:00
query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl), column)
2020-01-17 19:14:41 +03:00
elif Backend.isDbms(DBMS.MONETDB):
query = rootQuery.blind.query2 % (column, unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db))
2012-07-20 22:17:35 +04:00
colType = unArrayizeValue(inject.getValue(query, union=False, error=False))
key = int(colType) if hasattr(colType, "isdigit") and colType.isdigit() else colType
2019-05-29 16:52:33 +03:00
2012-07-20 22:17:35 +04:00
if Backend.isDbms(DBMS.FIREBIRD):
2016-09-23 19:03:31 +03:00
colType = FIREBIRD_TYPES.get(key, colType)
elif Backend.isDbms(DBMS.INFORMIX):
notNull = False
if isinstance(key, int) and key > 255:
key -= 256
notNull = True
colType = INFORMIX_TYPES.get(key, colType)
if notNull:
colType = "%s NOT NULL" % colType
2012-07-20 22:17:35 +04:00
column = safeSQLIdentificatorNaming(column)
columns[column] = colType
else:
column = safeSQLIdentificatorNaming(column)
columns[column] = None
if columns:
if conf.db in kb.data.cachedColumns:
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] = columns
else:
table[safeSQLIdentificatorNaming(tbl, True)] = columns
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table
if not kb.data.cachedColumns:
2012-12-18 20:42:03 +04:00
warnMsg = "unable to retrieve column names for "
2013-02-15 19:48:58 +04:00
warnMsg += ("table '%s' " % unsafeSQLIdentificatorNaming(unArrayizeValue(tblList))) if len(tblList) == 1 else "any table "
if METADB_SUFFIX not in conf.db:
warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
logger.warning(warnMsg)
2012-07-20 22:17:35 +04:00
if bruteForce is None:
return self.getColumns(onlyColNames=onlyColNames, colTuple=colTuple, bruteForce=True)
return kb.data.cachedColumns
@stackedmethod
2012-07-20 22:17:35 +04:00
def getSchema(self):
infoMsg = "enumerating database management system schema"
logger.info(infoMsg)
2015-07-18 18:01:34 +03:00
try:
pushValue(conf.db)
pushValue(conf.tbl)
pushValue(conf.col)
2012-07-20 22:17:35 +04:00
2015-07-18 18:01:34 +03:00
kb.data.cachedTables = {}
kb.data.cachedColumns = {}
2012-07-20 22:17:35 +04:00
2015-07-18 18:01:34 +03:00
self.getTables()
2012-07-20 22:17:35 +04:00
2015-07-18 18:01:34 +03:00
infoMsg = "fetched tables: "
2019-12-12 16:10:02 +03:00
infoMsg += ", ".join(["%s" % ", ".join("'%s%s%s'" % (unsafeSQLIdentificatorNaming(db), ".." if Backend.isDbms(DBMS.MSSQL) or Backend.isDbms(DBMS.SYBASE) else '.', unsafeSQLIdentificatorNaming(_)) for _ in tbl) for db, tbl in kb.data.cachedTables.items()])
2015-07-18 18:01:34 +03:00
logger.info(infoMsg)
2012-07-20 22:17:35 +04:00
2015-07-18 18:01:34 +03:00
for db, tables in kb.data.cachedTables.items():
for tbl in tables:
conf.db = db
conf.tbl = tbl
self.getColumns()
finally:
conf.col = popValue()
conf.tbl = popValue()
conf.db = popValue()
2012-07-20 22:17:35 +04:00
return kb.data.cachedColumns
def _tableGetCount(self, db, table):
2014-12-30 12:04:41 +03:00
if not db or not table:
return None
2020-01-27 19:32:31 +03:00
if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES:
2013-01-18 02:13:59 +04:00
db = db.upper()
table = table.upper()
2012-07-20 22:17:35 +04:00
2020-02-26 19:33:47 +03:00
if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MCKOI, DBMS.EXTREMEDB):
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))
2019-02-15 18:54:43 +03:00
query = agent.whereQuery(query)
2012-07-20 22:17:35 +04:00
count = inject.getValue(query, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
if isNumPosStrValue(count):
if safeSQLIdentificatorNaming(db) not in kb.data.cachedCounts:
kb.data.cachedCounts[safeSQLIdentificatorNaming(db)] = {}
if int(count) in kb.data.cachedCounts[safeSQLIdentificatorNaming(db)]:
kb.data.cachedCounts[safeSQLIdentificatorNaming(db)][int(count)].append(safeSQLIdentificatorNaming(table, True))
else:
kb.data.cachedCounts[safeSQLIdentificatorNaming(db)][int(count)] = [safeSQLIdentificatorNaming(table, True)]
def getCount(self):
if not conf.tbl:
warnMsg = "missing table parameter, sqlmap will retrieve "
warnMsg += "the number of entries for all database "
warnMsg += "management system databases' tables"
logger.warning(warnMsg)
2012-07-20 22:17:35 +04:00
elif "." in conf.tbl:
if not conf.db:
2015-09-09 15:46:06 +03:00
conf.db, conf.tbl = conf.tbl.split('.', 1)
2012-07-20 22:17:35 +04:00
2020-02-26 19:33:47 +03:00
if conf.tbl is not None and conf.db is None and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MCKOI, DBMS.EXTREMEDB):
2012-10-04 20:28:36 +04:00
warnMsg = "missing database parameter. sqlmap is going to "
2012-07-20 22:17:35 +04:00
warnMsg += "use the current database to retrieve the "
warnMsg += "number of entries for table '%s'" % unsafeSQLIdentificatorNaming(conf.tbl)
logger.warning(warnMsg)
2012-07-20 22:17:35 +04:00
conf.db = self.getCurrentDb()
self.forceDbmsEnum()
if conf.tbl:
2017-04-18 16:56:24 +03:00
for table in conf.tbl.split(','):
self._tableGetCount(conf.db, table)
2012-07-20 22:17:35 +04:00
else:
self.getTables()
for db, tables in kb.data.cachedTables.items():
for table in tables:
self._tableGetCount(db, table)
2012-07-20 22:17:35 +04:00
return kb.data.cachedCounts
2019-05-29 16:52:33 +03:00
def getStatements(self):
infoMsg = "fetching SQL statements"
logger.info(infoMsg)
rootQuery = queries[Backend.getIdentifiedDbms()].statements
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:
2021-07-05 00:07:55 +03:00
if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE):
query = rootQuery.inband.query2
else:
query = rootQuery.inband.query
2019-05-29 16:52:33 +03:00
while True:
values = inject.getValue(query, blind=False, time=False)
if not isNoneValue(values):
kb.data.cachedStatements = []
for value in arrayizeValue(values):
value = (unArrayizeValue(value) or "").strip()
if not isNoneValue(value):
kb.data.cachedStatements.append(value.strip())
elif Backend.isDbms(DBMS.PGSQL) and "current_query" not in query:
query = query.replace("query", "current_query")
continue
break
if not kb.data.cachedStatements and isInferenceAvailable() and not conf.direct:
infoMsg = "fetching number of statements"
logger.info(infoMsg)
query = rootQuery.blind.count
2021-07-05 00:07:55 +03:00
if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE):
2021-03-11 13:11:29 +03:00
query = re.sub("INFORMATION_SCHEMA", "DATA_DICTIONARY", query, flags=re.I)
2019-05-29 16:52:33 +03:00
count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
if count == 0:
return kb.data.cachedStatements
elif not isNumPosStrValue(count):
errMsg = "unable to retrieve the number of statements"
raise SqlmapNoneDataException(errMsg)
plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES
2019-05-29 16:52:33 +03:00
indexRange = getLimitRange(count, plusOne=plusOne)
for index in indexRange:
value = None
if Backend.getIdentifiedDbms() in (DBMS.MYSQL,): # case with multiple processes
query = rootQuery.blind.query3 % index
identifier = unArrayizeValue(inject.getValue(query, union=False, error=False, expected=EXPECTED.INT))
if not isNoneValue(identifier):
query = rootQuery.blind.query2 % identifier
value = unArrayizeValue(inject.getValue(query, union=False, error=False, expected=EXPECTED.INT))
if isNoneValue(value):
query = rootQuery.blind.query % index
2021-07-05 00:07:55 +03:00
if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE):
2021-03-11 13:11:29 +03:00
query = re.sub("INFORMATION_SCHEMA", "DATA_DICTIONARY", query, flags=re.I)
2019-05-29 16:52:33 +03:00
value = unArrayizeValue(inject.getValue(query, union=False, error=False))
if not isNoneValue(value):
kb.data.cachedStatements.append(value)
if not kb.data.cachedStatements:
errMsg = "unable to retrieve the statements"
logger.error(errMsg)
else:
kb.data.cachedStatements = [_.replace(REFLECTED_VALUE_MARKER, "<payload>") for _ in kb.data.cachedStatements]
2019-06-01 13:38:37 +03:00
return kb.data.cachedStatements