Merge branch 'master' into hana-pr

# Conflicts:
#	data/txt/sha256sums.txt
#	lib/core/common.py
#	lib/core/settings.py
This commit is contained in:
Miroslav Štampar 2026-07-09 16:48:30 +02:00
commit 1465d55a47
239 changed files with 57035 additions and 4345 deletions

View file

@ -68,7 +68,7 @@ class Fingerprint(GenericFingerprint):
infoMsg = "testing %s" % DBMS.MONETDB
logger.info(infoMsg)
result = inject.checkBooleanExpression("isaurl(NULL)=false")
result = inject.checkBooleanExpression("isaurl(NULL) IS NULL")
if result:
infoMsg = "confirming %s" % DBMS.MONETDB

View file

@ -93,7 +93,7 @@ class Enumeration(GenericEnumeration):
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:
for db in dbs:
if conf.excludeSysDbs and db in self.excludeDbsList:
if conf.excludeSysDbs and unsafeSQLIdentificatorNaming(db) in self.excludeDbsList:
infoMsg = "skipping system database '%s'" % db
singleTimeLogMessage(infoMsg)
continue
@ -116,7 +116,7 @@ class Enumeration(GenericEnumeration):
if not kb.data.cachedTables and isInferenceAvailable() and not conf.direct:
for db in dbs:
if conf.excludeSysDbs and db in self.excludeDbsList:
if conf.excludeSysDbs and unsafeSQLIdentificatorNaming(db) in self.excludeDbsList:
infoMsg = "skipping system database '%s'" % db
singleTimeLogMessage(infoMsg)
continue
@ -206,7 +206,7 @@ class Enumeration(GenericEnumeration):
for db in foundTbls.keys():
db = safeSQLIdentificatorNaming(db)
if conf.excludeSysDbs and db in self.excludeDbsList:
if conf.excludeSysDbs and unsafeSQLIdentificatorNaming(db) in self.excludeDbsList:
infoMsg = "skipping system database '%s'" % db
singleTimeLogMessage(infoMsg)
continue
@ -343,7 +343,7 @@ class Enumeration(GenericEnumeration):
for db in (_ for _ in dbs if _):
db = safeSQLIdentificatorNaming(db)
if conf.excludeSysDbs and db in self.excludeDbsList:
if conf.excludeSysDbs and unsafeSQLIdentificatorNaming(db) in self.excludeDbsList:
continue
if conf.exclude and re.search(conf.exclude, db, re.I) is not None:

View file

@ -61,7 +61,7 @@ class Takeover(GenericTakeover):
break
if not addrs:
errMsg = "sqlmap can not exploit the stored procedure buffer "
errMsg = "sqlmap cannot exploit the stored procedure buffer "
errMsg += "overflow because it does not have a valid return "
errMsg += "code for the underlying operating system (Windows "
errMsg += "%s Service Pack %d)" % (Backend.getOsVersion(), Backend.getOsServicePack())

View file

@ -16,6 +16,8 @@ class Syntax(GenericSyntax):
True
>>> Syntax.escape(u"SELECT 'abcd\xebfgh' FROM foobar") == "SELECT CHR(97)||CHR(98)||CHR(99)||CHR(100)||NCHR(235)||CHR(102)||CHR(103)||CHR(104) FROM foobar"
True
>>> Syntax.escape("SELECT 'a''b' FROM DUAL") == "SELECT CHR(97)||CHR(39)||CHR(98) FROM DUAL"
True
"""
def escaper(value):

View file

@ -11,6 +11,7 @@ from lib.core.common import randomInt
from lib.core.compat import xrange
from lib.core.data import kb
from lib.core.data import logger
from lib.core.enums import CHARSET_TYPE
from lib.core.exception import SqlmapUnsupportedFeatureException
from lib.core.settings import LOBLKSIZE
from lib.request import inject
@ -32,6 +33,15 @@ class Filesystem(GenericFilesystem):
return self.udfEvalCmd(cmd=remoteFile, udfName="sys_fileread")
def nonStackedReadFile(self, remoteFile):
if not kb.bruteMode:
infoMsg = "fetching file: '%s'" % remoteFile
logger.info(infoMsg)
# a superuser (or a member of the pg_read_server_files role on PostgreSQL >= 11) can read
# files in-band via pg_read_binary_file(), so file reading does not require stacked queries
return inject.getValue("ENCODE(PG_READ_BINARY_FILE('%s'),'hex')" % remoteFile, charsetType=CHARSET_TYPE.HEXADECIMAL)
def unionWriteFile(self, localFile, remoteFile, fileType=None, forceCheck=False):
errMsg = "PostgreSQL does not support file upload with UNION "
errMsg += "query SQL injection technique"

View file

@ -9,15 +9,8 @@ from lib.core.data import logger
from plugins.generic.enumeration import Enumeration as GenericEnumeration
class Enumeration(GenericEnumeration):
def getBanner(self):
warnMsg = "on Presto it is not possible to get the banner"
logger.warning(warnMsg)
return None
def getCurrentDb(self):
warnMsg = "on Presto it is not possible to get name of the current database (schema)"
logger.warning(warnMsg)
# NOTE: getBanner()/getCurrentDb() are intentionally NOT overridden - modern Presto/Trino expose
# version() and current_schema (wired in queries.xml), so the generic implementations work.
def isDba(self, user=None):
warnMsg = "on Presto it is not possible to test if current user is DBA"

View file

@ -7,10 +7,14 @@ See the file 'LICENSE' for copying permission
from lib.core.common import Backend
from lib.core.common import Format
from lib.core.common import hashDBRetrieve
from lib.core.common import hashDBWrite
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.enums import FORK
from lib.core.enums import HASHDB_KEYS
from lib.core.session import setDbms
from lib.core.settings import PRESTO_ALIASES
from lib.request import inject
@ -21,6 +25,18 @@ class Fingerprint(GenericFingerprint):
GenericFingerprint.__init__(self, DBMS.PRESTO)
def getFingerprint(self):
fork = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK)
if fork is None:
# Trino (the PrestoSQL fork) exposes functions PrestoDB never added (e.g. SOUNDEX),
# so a NULL-based probe on one of them distinguishes the fork from the original.
if inject.checkBooleanExpression("SOUNDEX(NULL) IS NULL"):
fork = FORK.TRINO
else:
fork = ""
hashDBWrite(HASHDB_KEYS.DBMS_FORK, fork)
value = ""
wsOsFp = Format.getOs("web server", kb.headersFp)
@ -37,6 +53,8 @@ class Fingerprint(GenericFingerprint):
if not conf.extensiveFp:
value += DBMS.PRESTO
if fork:
value += " (%s fork)" % fork
return value
actVer = Format.getDbms()
@ -55,6 +73,9 @@ class Fingerprint(GenericFingerprint):
if htmlErrorFp:
value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)
if fork:
value += "\n%sfork fingerprint: %s" % (blank, fork)
return value
def checkDbms(self):

View file

@ -70,6 +70,7 @@ class Databases(object):
kb.data.cachedCounts = {}
kb.data.dumpedTable = {}
kb.data.cachedStatements = []
kb.data.cachedProcedures = []
def getCurrentDb(self):
infoMsg = "fetching current database"
@ -304,7 +305,7 @@ class Databases(object):
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)
query += " IN (%s)" % ','.join("'%s'" % unsafeSQLIdentificatorNaming(db) for db in sorted(dbs) if unsafeSQLIdentificatorNaming(db) not in self.excludeDbsList)
else:
query += " IN (%s)" % ','.join("'%s'" % unsafeSQLIdentificatorNaming(db) for db in sorted(dbs))
@ -356,7 +357,7 @@ class Databases(object):
if not kb.data.cachedTables and isInferenceAvailable() and not conf.direct:
for db in dbs:
if conf.excludeSysDbs and db in self.excludeDbsList:
if conf.excludeSysDbs and unsafeSQLIdentificatorNaming(db) in self.excludeDbsList:
infoMsg = "skipping system database '%s'" % unsafeSQLIdentificatorNaming(db)
logger.info(infoMsg)
continue
@ -400,26 +401,40 @@ class Databases(object):
plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES
indexRange = getLimitRange(count, plusOne=plusOne)
for index in indexRange:
if Backend.isDbms(DBMS.SYBASE):
query = _query % (db, (kb.data.cachedTables[-1] if kb.data.cachedTables else " "))
elif Backend.getIdentifiedDbms() in (DBMS.MAXDB, DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB):
query = _query % (kb.data.cachedTables[-1] if kb.data.cachedTables else " ")
elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD):
query = _query % index
elif Backend.getIdentifiedDbms() in (DBMS.HSQLDB, DBMS.INFORMIX, DBMS.FRONTBASE, DBMS.VIRTUOSO):
query = _query % (index, unsafeSQLIdentificatorNaming(db))
elif Backend.getIdentifiedDbms() in (DBMS.SPANNER,):
query = _query % (unsafeSQLIdentificatorNaming(db), unsafeSQLIdentificatorNaming(db), index)
else:
query = _query % (unsafeSQLIdentificatorNaming(db), index)
# Value-parallel, prediction-assisted name enumeration for the DBMSes using the
# generic "<db>, <index>" blind template. Retrieves whole names concurrently (one per
# worker, each decoded sequentially so wordlist prediction applies). Used with '--threads'
# and under '--eta' (one whole-job bar/ETA) per valueParallelEligible(); plain single-thread
# stays on the classic getValue loop, which still gets predictive inference via getPartRun.
# The special templates stay serial.
genericTemplate = Backend.getIdentifiedDbms() not in (DBMS.SYBASE, DBMS.MAXDB, DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB, DBMS.SQLITE, DBMS.FIREBIRD, DBMS.HSQLDB, DBMS.INFORMIX, DBMS.FRONTBASE, DBMS.VIRTUOSO, DBMS.SPANNER)
table = unArrayizeValue(inject.getValue(query, union=False, error=False))
if genericTemplate and inject.valueParallelEligible():
for table in (inject._threadedInferenceValues(lambda index: _query % (unsafeSQLIdentificatorNaming(db), index), indexRange, context="Tables") or []):
if not isNoneValue(table):
kb.hintValue = table
tables.append(safeSQLIdentificatorNaming(table, True))
else:
for index in indexRange:
if Backend.isDbms(DBMS.SYBASE):
query = _query % (db, (kb.data.cachedTables[-1] if kb.data.cachedTables else " "))
elif Backend.getIdentifiedDbms() in (DBMS.MAXDB, DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB):
query = _query % (kb.data.cachedTables[-1] if kb.data.cachedTables else " ")
elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD):
query = _query % index
elif Backend.getIdentifiedDbms() in (DBMS.HSQLDB, DBMS.INFORMIX, DBMS.FRONTBASE, DBMS.VIRTUOSO):
query = _query % (index, unsafeSQLIdentificatorNaming(db))
elif Backend.getIdentifiedDbms() in (DBMS.SPANNER,):
query = _query % (unsafeSQLIdentificatorNaming(db), unsafeSQLIdentificatorNaming(db), index)
else:
query = _query % (unsafeSQLIdentificatorNaming(db), index)
if not isNoneValue(table):
kb.hintValue = table
table = safeSQLIdentificatorNaming(table, True)
tables.append(table)
table = unArrayizeValue(inject.getValue(query, union=False, error=False))
if not isNoneValue(table):
kb.hintValue = table
table = safeSQLIdentificatorNaming(table, True)
tables.append(table)
if tables:
kb.data.cachedTables[db] = tables
@ -840,7 +855,7 @@ class Databases(object):
logger.error(errMsg)
continue
for index in getLimitRange(count):
def columnNameQuery(index):
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CUBRID, DBMS.CACHE, DBMS.FRONTBASE, DBMS.VIRTUOSO):
query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db))
query += condQuery
@ -879,8 +894,24 @@ class Databases(object):
query += condQuery
field = condition
query = agent.limitQuery(index, query, field, field)
column = unArrayizeValue(inject.getValue(query, union=False, error=False))
return agent.limitQuery(index, query, field, field)
indexList = list(getLimitRange(count))
# Value-parallel column-NAME enumeration: the same axis/mechanism as getTables (one name per
# worker, decoded sequentially - no length probe, predictive inference applies, names stream
# live, and under '--eta' a single whole-job bar/ETA is drawn). Eligibility is shared via
# valueParallelEligible(); serial fallback only when also fetching per-column comments (those
# need an extra per-column query).
columnNames = None
if not conf.getComments and inject.valueParallelEligible():
columnNames = inject._threadedInferenceValues(columnNameQuery, indexList, context="Columns")
for position, index in enumerate(indexList):
if columnNames is not None:
column = unArrayizeValue(columnNames[position])
else:
column = unArrayizeValue(inject.getValue(columnNameQuery(index), union=False, error=False))
if not isNoneValue(column):
if conf.getComments:
@ -1051,6 +1082,11 @@ class Databases(object):
rootQuery = queries[Backend.getIdentifiedDbms()].statements
if "inband" not in rootQuery and "blind" not in rootQuery:
warnMsg = "on %s it is not possible to enumerate the SQL statements" % Backend.getIdentifiedDbms()
logger.warning(warnMsg)
return kb.data.cachedStatements
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:
if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE):
query = rootQuery.inband.query2
@ -1122,3 +1158,62 @@ class Databases(object):
kb.data.cachedStatements = [_.replace(REFLECTED_VALUE_MARKER, "<payload>") for _ in kb.data.cachedStatements]
return kb.data.cachedStatements
def getProcedures(self):
infoMsg = "fetching stored procedures"
logger.info(infoMsg)
rootQuery = queries[Backend.getIdentifiedDbms()].procedures
# Generic-first by design: a DBMS is supported iff it declares a <procedures> query block in
# queries.xml (INFORMATION_SCHEMA.ROUTINES / pg_proc / sys.sql_modules / ALL_SOURCE / RDB$PROCEDURES).
# Engines without stored procedures (or without a declared block) fall through with a clean
# warning - same model as getStatements() (uneven coverage is the established convention).
if "inband" not in rootQuery and "blind" not in rootQuery:
warnMsg = "on %s it is not possible to enumerate the stored procedures" % Backend.getIdentifiedDbms()
logger.warning(warnMsg)
return kb.data.cachedProcedures
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:
query = rootQuery.inband.query
values = inject.getValue(query, blind=False, time=False)
if not isNoneValue(values):
kb.data.cachedProcedures = []
for value in arrayizeValue(values):
value = (unArrayizeValue(value) or "").strip()
if not isNoneValue(value):
kb.data.cachedProcedures.append(value.strip())
if not kb.data.cachedProcedures and isInferenceAvailable() and not conf.direct:
infoMsg = "fetching number of stored procedures"
logger.info(infoMsg)
count = inject.getValue(rootQuery.blind.count, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
if count == 0:
return kb.data.cachedProcedures
elif not isNumPosStrValue(count):
errMsg = "unable to retrieve the number of stored procedures"
raise SqlmapNoneDataException(errMsg)
# every <procedures> blind query uses 0-based paging (MySQL "LIMIT %d,1", PostgreSQL/MSSQL/Oracle
# "OFFSET %d"), so the index range stays 0-based here regardless of PLUS_ONE_DBMSES (unlike
# getStatements(), whose MSSQL/Oracle idioms are 1-based)
indexRange = getLimitRange(count)
for index in indexRange:
query = rootQuery.blind.query % index
value = unArrayizeValue(inject.getValue(query, union=False, error=False))
if not isNoneValue(value):
kb.data.cachedProcedures.append((value or "").strip())
if not kb.data.cachedProcedures:
errMsg = "unable to retrieve the stored procedures"
logger.error(errMsg)
else:
kb.data.cachedProcedures = [_.replace(REFLECTED_VALUE_MARKER, "<payload>") for _ in kb.data.cachedProcedures]
return kb.data.cachedProcedures

View file

@ -42,12 +42,15 @@ from lib.core.exception import SqlmapNoneDataException
from lib.core.exception import SqlmapUnsupportedFeatureException
from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD
from lib.core.settings import CURRENT_DB
from lib.core.settings import KEYSET_MIN_ROWS
from lib.core.settings import METADB_SUFFIX
from lib.core.settings import NULL
from lib.core.settings import PLUS_ONE_DBMSES
from lib.core.settings import UPPER_CASE_DBMSES
from lib.request import inject
from lib.utils.hash import attackDumpedTable
from lib.utils.keysetdump import keysetDumpTable
from lib.utils.keysetdump import resolveKeysetCursor
from lib.utils.pivotdumptable import pivotDumpTable
from thirdparty import six
from thirdparty.six.moves import zip as _zip
@ -309,6 +312,9 @@ class Entries(object):
count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
# keyset (seek) pagination: forced with --keyset, automatic for large tables, off with --no-keyset
keysetCursor = resolveKeysetCursor(tbl, colList) if (not conf.noKeyset and isNumPosStrValue(count) and (conf.keyset or int(count) >= KEYSET_MIN_ROWS)) else None
lengths = {}
entries = {}
@ -332,6 +338,19 @@ class Entries(object):
continue
elif keysetCursor:
infoMsg = "using keyset (seek) pagination on column(s) '%s' " % ', '.join(keysetCursor)
infoMsg += "for table '%s'" % unsafeSQLIdentificatorNaming(tbl)
logger.info(infoMsg)
try:
entries, lengths = keysetDumpTable(tbl, colList, count, keysetCursor)
except KeyboardInterrupt:
kb.dumpKeyboardInterrupt = True
clearConsoleLine()
warnMsg = "Ctrl+C detected in dumping phase"
logger.warning(warnMsg)
elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL, DBMS.INFORMIX, DBMS.MCKOI, DBMS.RAIMA):
if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.RAIMA):
table = tbl
@ -399,41 +418,74 @@ class Entries(object):
debugMsg += "dumped as it appears to be empty"
logger.debug(debugMsg)
def cellQuery(column, index):
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.CLICKHOUSE, DBMS.SNOWFLAKE, DBMS.SPANNER):
query = rootQuery.blind.query % (agent.preprocessField(tbl, column), conf.db, conf.tbl, prioritySortColumns(colList)[0], index)
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.getIdentifiedDbms() in (DBMS.MIMERSQL,):
query = rootQuery.blind.query % (agent.preprocessField(tbl, column), tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())), prioritySortColumns(colList)[0], index)
elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.EXTREMEDB):
query = rootQuery.blind.query % (agent.preprocessField(tbl, column), tbl, index)
elif Backend.isDbms(DBMS.FIREBIRD):
query = rootQuery.blind.query % (index, agent.preprocessField(tbl, column), tbl)
elif Backend.getIdentifiedDbms() in (DBMS.INFORMIX, DBMS.VIRTUOSO):
query = rootQuery.blind.query % (index, agent.preprocessField(tbl, column), conf.db, tbl, prioritySortColumns(colList)[0])
elif Backend.isDbms(DBMS.FRONTBASE):
query = rootQuery.blind.query % (index, agent.preprocessField(tbl, column), conf.db, tbl)
else:
query = rootQuery.blind.query % (agent.preprocessField(tbl, column), conf.db, tbl, index)
return agent.whereQuery(query)
try:
for index in indexRange:
# Value-parallel dumping: one whole cell per worker, decoded sequentially, so there
# is NO per-cell LENGTH() probe (the position-parallel path needs one to split a
# value's characters across threads) and the per-column Huffman model + low-cardinality
# guessing engage under concurrency. Used for the boolean channel with '--threads', and
# for the HTTP/2 timeless oracle (its per-thread connections make concurrency safe and
# deterministic). Also used under '--eta' - including single-threaded time-based, the
# slowest channel - to drive one whole-job progress bar/ETA (how long the dump takes)
# instead of a per-cell counter. Eligibility (channel x threads/eta) is valueParallelEligible();
# '--dns-domain' keeps the classic loop (its OOB fast path bypasses bisection).
if not conf.dnsDomain and inject.valueParallelEligible():
# One value-parallel pass over every (non-empty) cell, so there is a single
# thread pool and values stream live as they complete - out of order, exactly
# like the error/union dumps - instead of a silent progress counter.
nonEmpty = [_ for _ in colList if _ not in emptyColumns]
tasks = [(column, index) for column in nonEmpty for index in indexRange]
retrieved = inject._threadedInferenceValues(lambda pair: cellQuery(pair[0], pair[1]), tasks, charsetType=None, dump=True) if tasks else []
retrieved = retrieved if retrieved is not None else [None] * len(tasks)
offset = 0
for column in colList:
value = ""
entries[column] = BigArray()
lengths.setdefault(column, 0)
if column not in lengths:
lengths[column] = 0
if column not in entries:
entries[column] = BigArray()
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.CLICKHOUSE, DBMS.SNOWFLAKE, DBMS.SPANNER):
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, 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.getIdentifiedDbms() in (DBMS.MIMERSQL,):
query = rootQuery.blind.query % (agent.preprocessField(tbl, column), tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())), sorted(colList, key=len)[0], index)
elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.EXTREMEDB):
query = rootQuery.blind.query % (agent.preprocessField(tbl, column), tbl, index)
elif Backend.isDbms(DBMS.FIREBIRD):
query = rootQuery.blind.query % (index, agent.preprocessField(tbl, column), tbl)
elif Backend.getIdentifiedDbms() in (DBMS.INFORMIX, DBMS.VIRTUOSO):
query = rootQuery.blind.query % (index, agent.preprocessField(tbl, column), conf.db, tbl, sorted(colList, key=len)[0])
elif Backend.isDbms(DBMS.FRONTBASE):
query = rootQuery.blind.query % (index, agent.preprocessField(tbl, column), conf.db, tbl)
if column in emptyColumns:
values = [NULL] * len(indexRange)
else:
query = rootQuery.blind.query % (agent.preprocessField(tbl, column), conf.db, tbl, index)
values = retrieved[offset:offset + len(indexRange)]
offset += len(indexRange)
query = agent.whereQuery(query)
for value in values:
value = '' if value is None else value
lengths[column] = max(lengths[column], getConsoleLength(DUMP_REPLACEMENTS.get(getUnicode(value), getUnicode(value))))
entries[column].append(value)
else:
for index in indexRange:
for column in colList:
if column not in lengths:
lengths[column] = 0
value = NULL if column in emptyColumns else inject.getValue(query, union=False, error=False, dump=True)
value = '' if value is None else value
if column not in entries:
entries[column] = BigArray()
lengths[column] = max(lengths[column], getConsoleLength(DUMP_REPLACEMENTS.get(getUnicode(value), getUnicode(value))))
entries[column].append(value)
value = NULL if column in emptyColumns else inject.getValue(cellQuery(column, index), union=False, error=False, dump=True)
value = '' if value is None else value
lengths[column] = max(lengths[column], getConsoleLength(DUMP_REPLACEMENTS.get(getUnicode(value), getUnicode(value))))
entries[column].append(value)
except KeyboardInterrupt:
kb.dumpKeyboardInterrupt = True

View file

@ -229,7 +229,7 @@ class Filesystem(object):
logger.debug(debugMsg)
fileContent = self.stackedReadFile(remoteFile)
elif Backend.isDbms(DBMS.MYSQL):
elif Backend.isDbms(DBMS.MYSQL) or Backend.isDbms(DBMS.PGSQL):
debugMsg = "going to try to read the file with non-stacked query "
debugMsg += "SQL injection technique"
logger.debug(debugMsg)

View file

@ -26,18 +26,21 @@ class Syntax(object):
retVal = expression
if quote:
for item in re.findall(r"'[^']*'+", expression):
original = item[1:-1]
if original:
# Match a full SQL string literal, honouring the '' (doubled single quote) escape - e.g.
# 'a''b' is ONE literal whose value is a'b, not 'a'' followed by a dangling b'. The old
# r"'[^']*'+" split on the inner '' and left the tail bare, corrupting the encoded payload.
for item in re.findall(r"'(?:[^']|'')*'", expression):
value = item[1:-1].replace("''", "'") # inner content with '' collapsed to the real quote
if value:
if Backend.isDbms(DBMS.SQLITE) and "X%s" % item in expression:
continue
if re.search(r"\[(SLEEPTIME|RAND)", original) is None: # e.g. '[SLEEPTIME]' marker
replacement = escaper(original) if not conf.noEscape else original
if re.search(r"\[(SLEEPTIME|RAND)", value) is None: # e.g. '[SLEEPTIME]' marker
replacement = escaper(value) if not conf.noEscape else value
if replacement != original:
if replacement != value:
retVal = retVal.replace(item, replacement)
elif len(original) != len(getBytes(original)) and "n'%s'" % original not in retVal and Backend.getDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.ORACLE, DBMS.MSSQL):
retVal = retVal.replace("'%s'" % original, "n'%s'" % original)
elif len(value) != len(getBytes(value)) and "n%s" % item not in retVal and Backend.getDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.ORACLE, DBMS.MSSQL):
retVal = retVal.replace(item, "n%s" % item)
else:
retVal = escaper(expression)

View file

@ -457,7 +457,7 @@ class Users(object):
# In MySQL >= 5.0 and Oracle we get the list
# of privileges as string
elif Backend.isDbms(DBMS.ORACLE) or (Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema) or Backend.getIdentifiedDbms() in (DBMS.VERTICA, DBMS.MIMERSQL, DBMS.CUBRID, DBMS.SNOWFLAKE):
elif Backend.isDbms(DBMS.ORACLE) or (Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema) or Backend.getIdentifiedDbms() in (DBMS.VERTICA, DBMS.MIMERSQL, DBMS.CUBRID, DBMS.SNOWFLAKE, DBMS.CLICKHOUSE, DBMS.CRATEDB, DBMS.ALTIBASE):
privileges.add(privilege)
# In MySQL < 5.0 we get Y if the privilege is
@ -668,8 +668,8 @@ class Users(object):
return (kb.data.cachedUsersPrivileges, areAdmins)
def getRoles(self, query2=False):
warnMsg = "on %s the concept of roles does not " % Backend.getIdentifiedDbms()
warnMsg += "exist. sqlmap will enumerate privileges instead"
warnMsg = "enumeration of roles is not supported on %s; " % Backend.getIdentifiedDbms()
warnMsg += "sqlmap will enumerate privileges instead"
logger.warning(warnMsg)
return self.getPrivileges(query2)