mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2026-06-28 04:20:58 +00:00
81 lines
3.4 KiB
Python
81 lines
3.4 KiB
Python
#!/usr/bin/env python
|
|
|
|
"""
|
|
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
|
|
See the file 'LICENSE' for copying permission
|
|
|
|
T1 - automatic WAF-bypass tamper selection (lib/utils/wafbypass.py). These cover the pure,
|
|
offline pieces: the identYwaf blind-signature decoder (which provocation vectors a known WAF
|
|
blocks), the data-ranked / DBMS-filtered / identYwaf-pruned candidate ordering, and the runtime
|
|
tamper loader. The end-to-end "adopt a tamper that restores detection" behaviour is exercised by
|
|
the --auto-tamper vuln-test case (lib/core/testing.py) against the vulnserver WAF emulator.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import unittest
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
from _testutils import bootstrap
|
|
bootstrap()
|
|
|
|
from lib.utils.wafbypass import candidateTampers, identYwafBlockedVectors, loadTamper
|
|
|
|
|
|
class TestIdentYwafDecoder(unittest.TestCase):
|
|
def test_known_waf_decodes_to_blocked_vectors(self):
|
|
# cloudflare has bundled blind signatures -> a non-trivial set of blocked vector indices,
|
|
# all within range of the 45 provocation vectors
|
|
blocked = identYwafBlockedVectors("cloudflare")
|
|
self.assertTrue(len(blocked) > 5)
|
|
self.assertTrue(all(isinstance(_, int) and 0 <= _ < 45 for _ in blocked))
|
|
|
|
def test_unknown_waf_is_empty(self):
|
|
self.assertEqual(identYwafBlockedVectors("definitely-not-a-real-waf"), set())
|
|
self.assertEqual(identYwafBlockedVectors(None), set())
|
|
|
|
|
|
class TestCandidateRanking(unittest.TestCase):
|
|
def test_structural_first(self):
|
|
cands = candidateTampers()
|
|
# the empirically strongest structural substitutions lead, ahead of camouflage
|
|
self.assertEqual(cands[0], "equaltolike")
|
|
self.assertIn("between", cands[:3])
|
|
self.assertLess(cands.index("between"), cands.index("space2comment"))
|
|
|
|
def test_no_dbms_prefiltering(self):
|
|
# DBMS compatibility is verified at runtime (detection re-run through the tamper), not here,
|
|
# so the full candidate set is offered regardless of any guessed back-end DBMS
|
|
cands = candidateTampers()
|
|
self.assertIn("versionedkeywords", cands)
|
|
self.assertIn("space2hash", cands)
|
|
self.assertIn("between", cands)
|
|
|
|
def test_identYwaf_prior_prunes_camouflage(self):
|
|
# a WAF whose profile blocks comment-obfuscated vectors should have comment-insertion
|
|
# camouflage pruned (it cannot help there), while structural candidates survive
|
|
base = candidateTampers()
|
|
pruned = candidateTampers(identifiedWafs=["cloudflare"])
|
|
self.assertIn("equaltolike", pruned)
|
|
self.assertNotIn("space2comment", pruned)
|
|
self.assertLessEqual(len(pruned), len(base))
|
|
|
|
|
|
class TestLoadTamper(unittest.TestCase):
|
|
def test_loads_and_applies(self):
|
|
fn = loadTamper("between")
|
|
self.assertTrue(callable(fn))
|
|
self.assertEqual(fn.__name__, "between")
|
|
# the loaded function is the real tamper transform
|
|
self.assertEqual(fn(payload="1 AND A>B"), "1 AND A NOT BETWEEN 0 AND B")
|
|
|
|
def test_missing_returns_none_or_raises(self):
|
|
# a non-existent script must not silently yield a bogus callable
|
|
try:
|
|
self.assertIsNone(loadTamper("no_such_tamper_script_xyz"))
|
|
except Exception:
|
|
pass # an import error is also acceptable; what matters is no fake function
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|