mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2026-07-09 18:13:18 +00:00
Couple of optimizations
This commit is contained in:
parent
154c7e333e
commit
6e459d66f2
5 changed files with 168 additions and 130 deletions
|
|
@ -28,10 +28,10 @@ from lib.request.inject import checkBooleanExpression
|
|||
# OTHER valid rows, which sqlmap's fuzzy page comparison conflates with the anchor row, producing
|
||||
# false positives. See PROVE_DESIGN.md.)
|
||||
#
|
||||
# Truth table measured on a live OWASP-CRS platform across 16 engines (MySQL/MySQL5, MariaDB/TiDB,
|
||||
# PostgreSQL, CockroachDB, CrateDB, Microsoft SQL Server, SQLite, Firebird, ClickHouse, H2, HSQLDB,
|
||||
# Derby, MonetDB, IRIS, Trino); only the zero-false-positive rules are kept (see _classify). With
|
||||
# anchor value 2:
|
||||
# Signatures were measured against every SQL engine on a live OWASP-CRS platform (MySQL/MySQL5,
|
||||
# MariaDB/TiDB, PostgreSQL, CockroachDB, CrateDB, Microsoft SQL Server, SQLite, Firebird, ClickHouse,
|
||||
# H2, HSQLDB, Derby, MonetDB, IRIS, Trino) and encoded as an exact-signature WHITELIST in _classify()
|
||||
# (only measured signatures classify; anything else -> None). With anchor value 2:
|
||||
#
|
||||
# * 2^0=2 -> '^' is bitwise XOR (MySQL/MSSQL/MonetDB: 2^0=2) vs exponentiation (PostgreSQL: 2^0=1)
|
||||
# vs no such operator (SQLite/Oracle/... -> error, so false)
|
||||
|
|
@ -52,57 +52,69 @@ DIALECT_PROBES = (
|
|||
("shift", "1<<2=4"),
|
||||
)
|
||||
|
||||
# Canary for the trustworthiness gate: a syntactically-invalid expression (a trailing operator) that
|
||||
# a real SQL back-end can only read as FALSE - the appended clause is a parse error, the query fails,
|
||||
# no row. A false-positive / noise channel (a WAF, a reflection, or a backend that ignores the
|
||||
# injected tail and reads every probe the same) reads it as TRUE, which is proof the boolean oracle
|
||||
# is trash, so the heuristic returns None (a true negative) rather than a bogus DBMS from a
|
||||
# meaningless signature. It uses a trailing-operator form, distinct from the '<n> <m>' no-operator
|
||||
# form already exercised by sqlmap's earlier false-positive check, so it adds new information.
|
||||
DIALECT_CANARY = "2+"
|
||||
|
||||
# Exact operator-dialect signature -> back-end DBMS. Strict WHITELIST re-derived from the live
|
||||
# measurement above: ONLY these signatures classify; any other - an engine not measured here, or a
|
||||
# false-positive / noise channel - returns None. This deliberately replaces earlier partial-condition
|
||||
# rules, which would confidently mis-map physically-impossible signatures onto a DBMS (e.g. the
|
||||
# all-true 'reads everything as true' noise, where '^' would be XOR and exponentiation at once).
|
||||
_SIGNATURE_DBMS = {
|
||||
# xor pgpow intdiv bitor shift
|
||||
(True, False, False, True, True): DBMS.MYSQL, # MySQL / MariaDB / TiDB
|
||||
(False, True, True, True, True): DBMS.PGSQL, # PostgreSQL
|
||||
(False, True, False, True, True): DBMS.PGSQL, # CockroachDB (pgwire; has '<<' -> shift True)
|
||||
(False, True, True, True, False): DBMS.PGSQL, # CrateDB
|
||||
(True, False, True, True, False): DBMS.MSSQL, # Microsoft SQL Server (no bit-shift)
|
||||
(True, False, True, True, True): DBMS.MONETDB, # MonetDB (as MSSQL but has '<<')
|
||||
(False, False, True, True, True): DBMS.SQLITE, # SQLite
|
||||
}
|
||||
|
||||
def _classify(signature):
|
||||
"""
|
||||
Maps a measured (xor, pgpow, intdiv, bitor) operator-dialect signature to a back-end
|
||||
DBMS, or returns None when the signature does not *uniquely* identify a major DBMS (so
|
||||
detection proceeds unchanged - the heuristic never wrong-foots the scan).
|
||||
Maps an exact operator-dialect signature (xor, pgpow, intdiv, bitor, shift) to a back-end DBMS
|
||||
through a strict whitelist of live-measured signatures, or returns None when the signature is not
|
||||
a known DBMS fingerprint - an engine not measured, or a noise / false-positive channel - so
|
||||
detection proceeds unchanged and the heuristic never wrong-foots the scan.
|
||||
|
||||
Rules below are the subset of the measured 11-engine truth table that maps with zero
|
||||
false positives. Engines whose operator profile is not distinctive enough (Oracle's
|
||||
all-false signature, which a minimal engine like ClickHouse/H2/Firebird/HSQLDB/Derby or
|
||||
a fully WAF-blocked channel also produces) deliberately fall through to None:
|
||||
|
||||
>>> _classify((True, False, False, True, True)) # MySQL / MariaDB / TiDB
|
||||
>>> _classify((True, False, False, True, True)) # MySQL / MariaDB / TiDB
|
||||
'MySQL'
|
||||
>>> _classify((True, False, True, True, False)) # Microsoft SQL Server (no bit-shift)
|
||||
>>> _classify((False, True, True, True, True)) # PostgreSQL
|
||||
'PostgreSQL'
|
||||
>>> _classify((False, True, False, True, True)) # CockroachDB -> PostgreSQL family
|
||||
'PostgreSQL'
|
||||
>>> _classify((False, True, True, True, False)) # CrateDB -> PostgreSQL family
|
||||
'PostgreSQL'
|
||||
>>> _classify((True, False, True, True, False)) # Microsoft SQL Server (no bit-shift)
|
||||
'Microsoft SQL Server'
|
||||
>>> _classify((True, False, True, True, True)) # MonetDB (same xor/intdiv as MSSQL, but has '<<')
|
||||
>>> _classify((True, False, True, True, True)) # MonetDB (as MSSQL but has '<<')
|
||||
'MonetDB'
|
||||
>>> _classify((False, True, True, True, False)) # PostgreSQL
|
||||
'PostgreSQL'
|
||||
>>> _classify((False, True, False, True, False)) # CockroachDB (pgwire) -> PostgreSQL family
|
||||
'PostgreSQL'
|
||||
>>> _classify((False, False, True, True, True)) # SQLite
|
||||
>>> _classify((False, False, True, True, True)) # SQLite
|
||||
'SQLite'
|
||||
>>> _classify((False, False, True, False, False)) is None # Firebird/HSQLDB/Derby/H2/Trino -> no prior
|
||||
>>> _classify((True, True, True, True, True)) is None # 'reads everything true' noise -> None
|
||||
True
|
||||
>>> _classify((False, False, False, False, False)) is None # all-false (Oracle/ClickHouse/IRIS/blocked) -> no prior
|
||||
>>> _classify((False, False, False, False, False)) is None # all-false (Oracle/ClickHouse/IRIS/blocked) -> None
|
||||
True
|
||||
>>> _classify((False, False, True, False, False)) is None # Firebird/H2/HSQLDB/Derby/Trino -> not distinctive
|
||||
True
|
||||
"""
|
||||
|
||||
xor, pgpow, intdiv, bitor, shift = signature
|
||||
|
||||
if pgpow: # '^' is exponentiation -> PostgreSQL family
|
||||
return DBMS.PGSQL
|
||||
if xor and intdiv: # '^' is XOR AND integer division -> SQL Server ...
|
||||
# ... except MonetDB shares this exact signature; it alone has a working bit-shift operator
|
||||
# ('1<<2=4'), SQL Server has none -> split the collision (measured zero-FP across 16 engines).
|
||||
return DBMS.MONETDB if shift else DBMS.MSSQL
|
||||
if xor and not intdiv: # '^' is XOR AND real division -> MySQL family
|
||||
return DBMS.MYSQL
|
||||
if not xor and intdiv and bitor: # no '^', integer division, bitwise '|' -> SQLite
|
||||
return DBMS.SQLITE
|
||||
|
||||
return None
|
||||
return _SIGNATURE_DBMS.get(tuple(bool(_) for _ in signature))
|
||||
|
||||
def dialectCheckDbms(injection):
|
||||
"""
|
||||
Keyword-free back-end DBMS heuristic via operator-dialect differentials, evaluated through the
|
||||
given (boolean-capable) injection. Complements heuristicCheckDbms() - which is skipped when the
|
||||
WAF/IPS is dropping requests and otherwise relies on SELECT/quote payloads - because every probe
|
||||
here is built from operator semantics alone. Returns the DBMS name or None; an ambiguous or
|
||||
WAF-blocked channel yields None, leaving the scan unchanged.
|
||||
here is built from operator semantics alone. Returns the DBMS name or None; an ambiguous,
|
||||
WAF-blocked or false-positive channel yields None, leaving the scan unchanged.
|
||||
"""
|
||||
|
||||
retVal = None
|
||||
|
|
@ -114,9 +126,12 @@ def dialectCheckDbms(injection):
|
|||
kb.injection = injection
|
||||
|
||||
try:
|
||||
# channel sanity: a tautology must read TRUE and a contradiction FALSE, otherwise the
|
||||
# boolean oracle is unreliable and the all-false signature (Oracle-like) would be meaningless
|
||||
if checkBooleanExpression("2=2") and not checkBooleanExpression("2=3"):
|
||||
# Trustworthiness gate: a real boolean oracle reads a tautology TRUE, a contradiction FALSE,
|
||||
# and a syntactically-invalid canary FALSE (the appended clause is a parse error -> the query
|
||||
# fails). A false-positive / noise channel reads them all alike - the canary as TRUE - which
|
||||
# is proof the oracle is trash, so classification is skipped (a true negative) instead of
|
||||
# emitting a bogus DBMS from a meaningless signature.
|
||||
if checkBooleanExpression("2=2") and not checkBooleanExpression("2=3") and not checkBooleanExpression(DIALECT_CANARY):
|
||||
signature = tuple(bool(checkBooleanExpression(expr)) for _, expr in DIALECT_PROBES)
|
||||
retVal = _classify(signature)
|
||||
finally:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue