Minor refactoring

This commit is contained in:
Miroslav Štampar 2026-06-30 20:45:46 +02:00
parent e0269acc0d
commit f932a3f30f
6 changed files with 30 additions and 139 deletions

View file

@ -181,7 +181,7 @@ f8de57606325456928e46ae2896f5f8bbec9ad18b1c644b492a566fa992216f6 lib/core/decor
5387168e5dfedd94ae22af7bb255f27d6baaca50b24179c6b98f4f325f5cc7b4 lib/core/exception.py
1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/core/__init__.py
914a13ee21fd610a6153a37cbe50830fcbd1324c7ebc1e7fc206d5e598b0f7ad lib/core/log.py
33ed53b263fa766a808be6797dd812822bb115d3b9db6e3a34763f500f5359e8 lib/core/optiondict.py
5a576f802f1298d0aa357e766ae6502fa53cacbbe0b1d328b7410a8b20a885b2 lib/core/optiondict.py
e033b20a0f7821797a10f4bf4235723f38c7db551c611fbb713faa621b123c4a lib/core/option.py
21b2b1745107c211fc7593923a3da7a808d40763c00091c28de5f7c129bcf3bc lib/core/patch.py
49c0fa7e3814dfda610d665ee02b12df299b28bc0b6773815b4395514ddf8dec lib/core/profiling.py
@ -189,7 +189,7 @@ e033b20a0f7821797a10f4bf4235723f38c7db551c611fbb713faa621b123c4a lib/core/optio
9bf174058f15d14e24e94f9aaf42df045119d3617c6c54bd2f3af79b462f331d lib/core/replication.py
0b8c38a01bb01f843d94a6c5f2075ee47520d0c4aa799cecea9c3e2c5a4a23a6 lib/core/revision.py
888daba83fd4a34e9503fe21f01fef4cc730e5cde871b1d40e15d4cbc847d56c lib/core/session.py
2498555483d50cf55f24dcb82b3253816a1ad6c3325b17e502d5063f2c9cbc87 lib/core/settings.py
098e5d86a0da05d4be5f5ed5371083954be2369abce57fda4bd906d12e1f8870 lib/core/settings.py
c7804223319e18eb0b8e2cbf0a8b6896d1cefb7b0b1a2e9f1cf826a8a3b56750 lib/core/shell.py
a2e98a94b231432736d6b304fc75525c8b5fdb4768c418387c5b4c1a610dad64 lib/core/subprocessng.py
19f1e3c5e3ba703d28d510cd7a9ab8284d5fbe9df5ce7e77c86e5931571364b7 lib/core/target.py
@ -200,7 +200,7 @@ b9aacb840310173202f79c2ba125b0243003ee6b44c92eca50424f2bdfc83c02 lib/core/unesc
2400e465fa4d13e4c32795910878c71ff212e4361b46428d57ce43983f5e997c lib/core/wordlist.py
1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/__init__.py
54bfd31ebded3ffa5848df1c644f196eb704116517c7a3d860b5d081e984d821 lib/parse/banner.py
316cdcb3d8d839dab639ed7eb4935780375d49c93371edbd6224976cbb968c2e lib/parse/cmdline.py
403ebb5b54531cf907a30ed439fc881cf3cbae68c3a4ec600c75312e5f6b9001 lib/parse/cmdline.py
02d82e4069bd98c52755417f8b8e306d79945672656ac24f1a45e7a6eff4b158 lib/parse/configfile.py
c5b258be7485089fac9d9cd179960e774fbd85e62836dc67cce76cc028bb6aeb lib/parse/handler.py
5c9a9caee948843d5537745640cc7b98d70a0412cc0949f59d4ebe8b2907c06c lib/parse/headers.py
@ -247,7 +247,7 @@ c3e5cf7e5e35ae5fd86b63a515b37e6f06e61c70d2690252f2ee8373aa16637e lib/techniques
44401cad3e39ae9fb899ed5d0e2fdd0879561de05c3117f17f3b0db54f4e3724 lib/techniques/nosql/__init__.py
bde75d41ac3e5747b96d2af4c33922573158cb43b48714a28490d6720dd85d89 lib/techniques/nosql/inject.py
1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/techniques/ssti/__init__.py
8eaf90c2fa517a4577467ac0d7534a927c23931b946b27e88e63ae022f794a1c lib/techniques/ssti/inject.py
14637b64878248e5965887b07aa68e62615dac88e2ffc6c3a581430bdd4e309e lib/techniques/ssti/inject.py
1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/techniques/union/__init__.py
ceec65f8cb7c3254c4671351c837418c76ac5bc55ccbc40779f67231b54d7085 lib/techniques/union/test.py
c65766f71e285fc85cdf58e7448c4c1d015af2a9dbb44fa3b665a9f13362fbcc lib/techniques/union/use.py
@ -643,7 +643,7 @@ cec98d72992c0799229a780fa7f0d7f3fb01ec2d708187ce0e4a05c8612f291b tests/test_saf
a1c6cda1e5b483f61e6a4f8ddd0b06a15ddaa3fd2119bfb9dbd9cc970d7a751d tests/test_settings_regex.py
29d0278e3718b0fee422d3f6bb85ca02560138d48cd76f9fe1f35ac19d96071b tests/test_sgmllib.py
d3d991331096e16e5019de3d652e9fff92c09bd9f97c50b1c2c3ceb0ed49b17e tests/test_sqlparse.py
4a9409a070770cc6300ed2b0c954254273479252fa602ffd19d78917f895756c tests/test_ssti.py
412a61053c2531cc0380b34dfd01d52bd118f6a6473728c069c467054c7e3c8e tests/test_ssti.py
8bcbf1091134dd0a62f6201f8b3645ed87b5ff2f7ba40a87231a29dac412591f tests/test_strings.py
8f1c5f0f337ecd26d35c5551060034e0aa33a62cce5385fc1227fdc485f6383e tests/test_tamper.py
67472bd71c20782cc0f738e2c2e674c29d6985669e14d15b69baef7d0e33de62 tests/test_target_parsing.py

View file

@ -173,8 +173,6 @@ optDict = {
"lastChar": "integer",
"sqlQuery": "string",
"sqlShell": "boolean",
"sstiQuery": "string",
"sstiShell": "boolean",
"sqlFile": "string",
},

View file

@ -20,7 +20,7 @@ from lib.core.enums import OS
from thirdparty import six
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
VERSION = "1.10.6.197"
VERSION = "1.10.6.198"
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)

View file

@ -133,7 +133,7 @@ def cmdLineParser(argv=None):
help="Parse target(s) from Burp or WebScarab proxy log file")
target.add_argument("-m", dest="bulkFile",
help="Scan multiple targets given in a textual file ")
help="Scan multiple targets given in a textual file")
target.add_argument("-r", dest="requestFile",
help="Load HTTP request from a file")
@ -335,7 +335,7 @@ def cmdLineParser(argv=None):
help="Skip testing for given parameter(s)")
injection.add_argument("--skip-static", dest="skipStatic", action="store_true",
help="Skip testing parameters that not appear to be dynamic")
help="Skip testing parameters that do not appear to be dynamic")
injection.add_argument("--param-exclude", dest="paramExclude",
help="Regexp to exclude parameters from testing (e.g. \"ses\")")
@ -442,21 +442,6 @@ def cmdLineParser(argv=None):
techniques.add_argument("--second-req", dest="secondReq",
help="Load second-order HTTP request from file")
techniques.add_argument("--graphql", dest="graphql", action="store_true",
help="Test for GraphQL injection")
techniques.add_argument("--ldap", dest="ldap", action="store_true",
help="Test for LDAP injection")
techniques.add_argument("--nosql", dest="nosql", action="store_true",
help="Test for NoSQL injection")
techniques.add_argument("--xpath", dest="xpath", action="store_true",
help="Test for XPath injection")
techniques.add_argument("--ssti", dest="ssti", action="store_true",
help="Test for server-side template injection")
# Fingerprint options
fingerprint = parser.add_argument_group("Fingerprint", "These options can be used to perform a back-end database management system version fingerprint")
@ -515,7 +500,7 @@ def cmdLineParser(argv=None):
help="Dump DBMS database table entries")
enumeration.add_argument("--dump-all", dest="dumpAll", action="store_true",
help="Dump all DBMS databases tables entries")
help="Dump entries of all DBMS database tables")
enumeration.add_argument("--search", dest="search", action="store_true",
help="Search column(s), table(s) and/or database name(s)")
@ -571,12 +556,6 @@ def cmdLineParser(argv=None):
enumeration.add_argument("--sql-shell", dest="sqlShell", action="store_true",
help="Prompt for an interactive SQL shell")
enumeration.add_argument("--ssti-query", dest="sstiQuery",
help="SSTI expression to evaluate in-band on the vulnerable parameter")
enumeration.add_argument("--ssti-shell", dest="sstiShell", action="store_true",
help="Prompt for an interactive SSTI expression shell")
enumeration.add_argument("--sql-file", dest="sqlFile",
help="Execute SQL statements from given file(s)")
@ -626,11 +605,10 @@ def cmdLineParser(argv=None):
help="Prompt for an OOB shell, Meterpreter or VNC")
takeover.add_argument("--os-smbrelay", dest="osSmb", action="store_true",
help="One click prompt for an OOB shell, Meterpreter or VNC")
help="One-click prompt for an OOB shell, Meterpreter or VNC")
takeover.add_argument("--os-bof", dest="osBof", action="store_true",
help="Stored procedure buffer overflow "
"exploitation")
help="Stored procedure buffer overflow exploitation")
takeover.add_argument("--priv-esc", dest="privEsc", action="store_true",
help="Database process user privilege escalation")
@ -788,6 +766,24 @@ def cmdLineParser(argv=None):
general.add_argument("--web-root", dest="webRoot",
help="Web server document root directory (e.g. \"/var/www\")")
# Non-SQL injection options
nonsql = parser.add_argument_group("Non-SQL injection", "These options can be used to test for non-SQL injection types")
nonsql.add_argument("--graphql", dest="graphql", action="store_true",
help="Test for GraphQL injection")
nonsql.add_argument("--ldap", dest="ldap", action="store_true",
help="Test for LDAP injection")
nonsql.add_argument("--nosql", dest="nosql", action="store_true",
help="Test for NoSQL injection")
nonsql.add_argument("--xpath", dest="xpath", action="store_true",
help="Test for XPath injection")
nonsql.add_argument("--ssti", dest="ssti", action="store_true",
help="Test for server-side template injection")
# Miscellaneous options
miscellaneous = parser.add_argument_group("Miscellaneous", "These options do not fit into any other category")

View file

@ -59,12 +59,6 @@ def _arithmeticPayload(fmt, a, b):
return fmt.replace("%d", str(a), 1).replace("%d", str(b), 1)
def _expressionPayload(fmt, value):
# Same rationale as _arithmeticPayload(): literal %s substitution so '%'-delimited engines
# (notably ERB) can wrap expressions instead of crashing on fmt % value.
return fmt.replace("%s", value, 1)
def _degroup(text):
# Strip digit-group (thousands) separators so an arithmetic result still matches when the
# engine formats large numbers with grouping (e.g. FreeMarker renders 234*567 as "132,678").
@ -642,7 +636,7 @@ def sstiScan():
place, parameter, engine, evidence = slot
from lib.core.common import readInput
wantsTakeover = any(conf.get(_) for _ in ("osCmd", "osShell", "sstiQuery", "sstiShell"))
wantsTakeover = any(conf.get(_) for _ in ("osCmd", "osShell"))
# If the user did not ask for exploitation, confirm (benignly) whether OS command
# execution is reachable and, if so, advise the relevant switches.
@ -651,20 +645,6 @@ def sstiScan():
"you are advised to try '--os-shell' (interactive) or "
"'--os-cmd=<command>' (single command)" % engine.name)
# --ssti-query: user-provided expression evaluated in-band
if conf.get("sstiQuery"):
_evalExpression(place, parameter, engine, conf.sstiQuery)
# --ssti-shell: interactive expression evaluation loop (interactive even under --batch,
# like sqlmap's SQL --sql-shell/--os-shell, which read straight from the terminal)
if conf.get("sstiShell"):
logger.info("calling SSTI shell. Enter expressions (e.g. 7*7) or 'exit'/'quit' to leave")
while True:
expr = readInput("ssti-shell> ", checkBatch=False)
if not expr or expr.strip().lower() in ("exit", "quit"):
break
_evalExpression(place, parameter, engine, expr.strip())
# --os-cmd / --os-shell: RCE via SSTI (reuses existing SQL takeover flags)
if conf.get("osCmd") or conf.get("osShell"):
if not _canTakeover(engine, evidence):
@ -692,56 +672,6 @@ def _escapeSingleQuoted(value):
return value.replace("\\", "\\\\").replace("'", "\\'")
def _evalExpression(place, parameter, engine, expr):
"""Wrap expr in the engine's expression format, extract result between
random markers for deterministic output, fall back to baseline diff."""
if not engine.expressionFmt:
logger.error("expression evaluation not supported for engine '%s'" % engine.name)
return
original = _originalValue(place, parameter) or ""
startMarker = randomStr(length=8, lowercase=True)
endMarker = randomStr(length=8, lowercase=True)
# Three-part payload: marker, expression, marker -- each in its own template tag
# so the expression is evaluated independently of the markers
payload = original + _expressionPayload(engine.expressionFmt, "'%s'" % startMarker)
payload += " " + _expressionPayload(engine.expressionFmt, expr)
payload += " " + _expressionPayload(engine.expressionFmt, "'%s'" % endMarker)
page = _send(place, parameter, payload)
if not page:
logger.warning("no response for SSTI expression '%s'" % expr)
return
text = getUnicode(page)
result = None
# Extract content between the random markers
if startMarker in text and endMarker in text:
start = text.index(startMarker) + len(startMarker)
end = text.index(endMarker, start)
result = text[start:end].strip()
# Fallback: diff against baseline
if not result:
baseline = _send(place, parameter, original)
if baseline:
sm = difflib.SequenceMatcher(None, getUnicode(baseline), text)
parts = []
for tag, i1, i2, j1, j2 in sm.get_opcodes():
if tag in ("insert", "replace"):
parts.append(text[j1:j2])
if parts:
result = "".join(parts).strip()
if result:
conf.dumper.singleString("SSTI expression result: %s" % result)
else:
logger.warning("could not extract expression result from response")
def _canTakeover(engine, evidence):
"""Require exact engine fingerprint (not a family guess) and confirmed
proof before attempting OS command execution."""

View file

@ -275,39 +275,6 @@ class TestCrossEngineDisambiguation(unittest.TestCase):
self.assertIn("Twig", engine.name)
class TestExpressionEvaluation(unittest.TestCase):
def setUp(self):
self.original_send = ssti._send
def tearDown(self):
ssti._send = self.original_send
def test_eval_uses_expressionFmt(self):
engine = ssti._ENGINE_TABLE[0] # Jinja2: expressionFmt = "{{ %s }}"
results = []
def mock(place, parameter, value):
results.append(value)
return "Hello __marker__ 49 __marker2__"
ssti._send = mock
ssti._evalExpression("GET", "q", engine, "7*7")
# Payload must use expressionFmt, not raw delimiter concatenation
self.assertIn("{{ ", results[0])
self.assertIn(" }}", results[0])
def test_eval_falls_back_when_no_expressionFmt(self):
engine = [e for e in ssti._ENGINE_TABLE if e.name == "Handlebars"][0]
self.assertEqual(engine.expressionFmt, "")
def mock(place, parameter, value):
return "irrelevant"
ssti._send = mock
# Should not raise; just logs error
ssti._evalExpression("GET", "q", engine, "7*7")
class TestBooleanUniqueness(unittest.TestCase):
def test_jinja2_boolean_unique_among_curlies(self):
jinja2 = ssti._ENGINE_TABLE[0]