fix(waf): stop promoting 944110/944120 chain starters (round 3)
Owner ruling (same mode contract as 920440: no accidental enforcement from
bare words). 944110 and 944120 are CRS chains whose first link matches
`runtime|processbuilder` / `getproperty|forclosure|filewriter|...`
anywhere in the request; the range promotion 944100-944300 put
ctl:ruleEngine=On on those starters, so any request containing the word
ran with full anomaly enforcement, in detect_only too.
Promotion is now 944100 and 944130-944300. Live (haproxy-manager-base
:wafbody-r3 + coraza-spoa :wafbody-r3), both modes: `?q=runtime&<SQLi>`
and `?q=getproperty&<SQLi>` 403 -> 200 on detect_only; Log4Shell in the
query (plain and ${lower:j} obfuscated), User-Agent and a JSON body still
403.
test-waf-body-inspection.py: 21 tests (+2: no CRS chain starter
920440/944110/944120 is covered by a promotion; the Log4Shell ids keep
theirs). Red on round 2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
234029c249
commit
2da2020305
2 files changed
+33
-1
No files matched your search
@@ -47,7 +47,17 @@ SecRuleUpdateActionById 933170-933200 "ctl:ruleEngine=On"
|
|||||||
# vulnerable, but blocking at the edge keeps logs clean and protects
|
# vulnerable, but blocking at the edge keeps logs clean and protects
|
||||||
# any future Java workloads.
|
# any future Java workloads.
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
SecRuleUpdateActionById 944100-944300 "ctl:ruleEngine=On"
|
# 944110 and 944120 are NOT promoted (changed 2026-10-09, 2026.10.2). Both are
|
||||||
|
# CHAINS whose first link matches bare words anywhere in the request --
|
||||||
|
# `runtime|processbuilder` and `getproperty|forclosure|filewriter|...` -- and
|
||||||
|
# Coraza runs an appended ctl:ruleEngine=On on that first link even when the
|
||||||
|
# chain then fails. Measured (Coraza v3.7.0 + CRS 4.25.0): in detect_only,
|
||||||
|
# `?q=runtime&id=1' or '1'='1` was 403 while the SQLi alone was 200. Same
|
||||||
|
# chain-starter defect as 920440. They still add to the anomaly score.
|
||||||
|
# Log4Shell (944150/944151/944152, ...) stays promoted. WHP mirrors this split
|
||||||
|
# in coraza_baked_promotion_ranges() (web-files/libs/coraza_blocking_set.php).
|
||||||
|
SecRuleUpdateActionById 944100 "ctl:ruleEngine=On"
|
||||||
|
SecRuleUpdateActionById 944130-944300 "ctl:ruleEngine=On"
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# 920440 — URL file extension restricted by policy
|
# 920440 — URL file extension restricted by policy
|
||||||
|
|||||||
@@ -240,6 +240,28 @@ class BakedPromotions(unittest.TestCase):
|
|||||||
if re.match(r'SecRuleUpdateActionById\s+(\S*-)?920440\b', l)],
|
if re.match(r'SecRuleUpdateActionById\s+(\S*-)?920440\b', l)],
|
||||||
'920440 promoted via SecRuleUpdateActionById again')
|
'920440 promoted via SecRuleUpdateActionById again')
|
||||||
|
|
||||||
|
def test_no_chain_starter_is_promoted(self):
|
||||||
|
"""Chain-starter rules in the promoted ranges (CRS 4.25.0): 920440,
|
||||||
|
944110, 944120. None may be covered by a SecRuleUpdateActionById."""
|
||||||
|
chain_starters = (920440, 944110, 944120)
|
||||||
|
promoted = []
|
||||||
|
for l in self.lines:
|
||||||
|
m = re.match(r'SecRuleUpdateActionById\s+(\d+)(?:-(\d+))?\s', l)
|
||||||
|
if m:
|
||||||
|
lo = int(m.group(1))
|
||||||
|
promoted.append((lo, int(m.group(2) or lo)))
|
||||||
|
self.assertTrue(promoted, 'no promotions parsed -- the test is blind')
|
||||||
|
for rid in chain_starters:
|
||||||
|
self.assertFalse([r for r in promoted if r[0] <= rid <= r[1]],
|
||||||
|
f'{rid} is a CRS chain starter and must not be promoted')
|
||||||
|
|
||||||
|
def test_log4shell_range_still_promoted(self):
|
||||||
|
for rid in (944100, 944130, 944150, 944151, 944152, 944300):
|
||||||
|
self.assertTrue([1 for l in self.lines
|
||||||
|
if (m := re.match(r'SecRuleUpdateActionById\s+(\d+)(?:-(\d+))?\s', l))
|
||||||
|
and int(m.group(1)) <= rid <= int(m.group(2) or m.group(1))],
|
||||||
|
f'{rid} lost its promotion')
|
||||||
|
|
||||||
def test_920440_flip_keys_on_the_restricted_extension(self):
|
def test_920440_flip_keys_on_the_restricted_extension(self):
|
||||||
i = self.lines.index('SecRule TX:EXTENSION "@within %{tx.restricted_extensions}" \\')
|
i = self.lines.index('SecRule TX:EXTENSION "@within %{tx.restricted_extensions}" \\')
|
||||||
self.assertEqual(self.lines[i + 1],
|
self.assertEqual(self.lines[i + 1],
|
||||||
|
|||||||
Reference in new issue
Block a user