sqlmap/tests/test_wafbypass.py

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()