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>
135 lines
8.0 KiB
Plaintext
135 lines
8.0 KiB
Plaintext
# WHP day-one enforce overrides for coraza-spoa.
|
||
#
|
||
# Global mode in config.yaml is SecRuleEngine DetectionOnly. The rule ID
|
||
# ranges below are promoted to enforcement individually, chosen for very
|
||
# low false-positive rate on the kinds of customer traffic seen on WHP
|
||
# (WordPress, WooCommerce, Divi page builders).
|
||
#
|
||
# When bumping the upstream coraza-spoa pin (and thus the bundled CRS):
|
||
# 1. Skim the CRS CHANGELOG for new/changed rules in these ID ranges.
|
||
# 2. Verify they're still high-confidence before promoting the new image.
|
||
# 3. Smoke-test in staging detect-only mode for 24h before flipping enforce.
|
||
#
|
||
# Per-customer false-positive tuning lives in a future per-customer
|
||
# override mechanism; v1 is server-wide.
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 930120 — LFI: explicit traversal to sensitive system files
|
||
# (/etc/passwd, /proc/self/, /.ssh/, /etc/shadow, /etc/group, etc.)
|
||
# Unambiguous probe pattern; no legitimate site path leads here.
|
||
# Note: 930xxx as a whole includes broader traversal patterns that can FP
|
||
# on legitimate relative-path file browsers — keep those detect-only.
|
||
# ---------------------------------------------------------------------------
|
||
SecRuleUpdateActionById 930120 "ctl:ruleEngine=On"
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 932100-932160 — RCE: Unix shell command injection
|
||
# Patterns like `; cat /etc/passwd`, `|whoami`, backtick `\`uname\``,
|
||
# $(...) substitution, &&/|| chaining with shell builtins.
|
||
# Don't appear in normal POST bodies, URL params, or headers. Targeting
|
||
# these is unambiguous attempted command execution.
|
||
# ---------------------------------------------------------------------------
|
||
SecRuleUpdateActionById 932100-932160 "ctl:ruleEngine=On"
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 933170-933200 — PHP Webshell access patterns
|
||
# Direct requests to known webshell paths: c99.php, r57.php, b374k.php,
|
||
# wso.php, alfa.php, mini.php, etc. Almost universally reconnaissance
|
||
# scanning for post-exploitation. Even legitimate WordPress installs
|
||
# never serve these paths.
|
||
# ---------------------------------------------------------------------------
|
||
SecRuleUpdateActionById 933170-933200 "ctl:ruleEngine=On"
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 944100-944300 — Log4Shell / JNDI injection
|
||
# `${jndi:ldap://}`, `${jndi:rmi://}`, and obfuscated variants thereof
|
||
# in headers, query strings, or bodies. Even our PHP/Node stack isn't
|
||
# vulnerable, but blocking at the edge keeps logs clean and protects
|
||
# any future Java workloads.
|
||
# ---------------------------------------------------------------------------
|
||
# 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
|
||
# Catches probes for backup / config / dump files: .bak, .old, .save,
|
||
# .swp, .sql, .dist, .backup. Promoted to enforce after empirical
|
||
# observation on whp01 (2026-05-12, first ~30 min of detect-only):
|
||
# 124 events, all backup-file recon — `/wp-config.php.old`,
|
||
# `/db_backup.sql`, `/.env.save`, `/releases.sql`, etc. — from a
|
||
# single GCP-hosted scanner. Zero false positives observed; standard
|
||
# WP/WooCommerce/Divi/HPR URLs do not end in these extensions.
|
||
# ---------------------------------------------------------------------------
|
||
# HOW IT IS PROMOTED (changed 2026-10-09, haproxy-manager-base 2026.10.2).
|
||
# This used to be `SecRuleUpdateActionById 920440 "ctl:ruleEngine=On"`, which
|
||
# was WRONG: 920440 is a CHAIN whose first link is
|
||
# `REQUEST_BASENAME "@rx \.([^.]+)$"` -- it matches EVERY URL with a file
|
||
# extension -- and Coraza runs actions appended to a chain starter when that
|
||
# first link matches, even though the chain then fails. Measured (Coraza
|
||
# v3.7.0 + CRS 4.25.0, in-process and live): every *.php / *.css / ...
|
||
# request ran with the engine On, so the whole CRS anomaly score (949110) and
|
||
# 200002 enforced there -- in detect_only mode too, since SecRuleEngine
|
||
# DetectionOnly cannot override a runtime ctl.
|
||
#
|
||
# Now: 920440 itself is untouched (it still sets tx.extension in its first
|
||
# link and adds the anomaly score in its second), and this separate phase-1
|
||
# rule, which runs after it, flips the engine only when the extension really
|
||
# is in tx.restricted_extensions -- the original intent. Removing 920440
|
||
# (break-glass, a per-host exception) also removes tx.extension, so this rule
|
||
# goes quiet with it. Id 992920440 is outside WHP's generated range
|
||
# (990000000-991999999).
|
||
SecRule TX:EXTENSION "@within %{tx.restricted_extensions}" \
|
||
"id:992920440,phase:1,pass,nolog,t:none,t:lowercase,ctl:ruleEngine=On"
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 930130 — Restricted File Access Attempt
|
||
# Catches dotfile / VCS / config-disclosure probes: .env (and .env.local /
|
||
# .env.bak / .env.save variants), .git/config, config.php at root or under
|
||
# /admin /backend, etc. Distinct from 930120 (system file paths like
|
||
# /etc/passwd); this targets application secret files.
|
||
#
|
||
# Promoted to enforce on the same observation pass that justified 920440:
|
||
# 117 events split across joshuaknapp.net (136), cgdannyb.com (51),
|
||
# onlinesupplements.net (23) — all `.env`-class disclosure probes.
|
||
# Zero false positives observed. Notably, HPR's `/ccdn.php?filename=...`
|
||
# audio delivery path does NOT trigger this rule — verified empirically.
|
||
# ---------------------------------------------------------------------------
|
||
SecRuleUpdateActionById 930130 "ctl:ruleEngine=On"
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Rule families intentionally kept at DETECT-ONLY for v1 — high FP rate
|
||
# on customer mix. Promote individually after observation:
|
||
#
|
||
# 913xxx (Scanner UAs)— matches legitimate ActivityPub federation
|
||
# (Mastodon's "...Bot" UA) and SiteLockSpider (a
|
||
# paid customer-security service some sites use).
|
||
# Observed on whp01 burn-in 2026-05-13:
|
||
# 20/185 hits = ~11% FP rate on HPR + greggfranklin
|
||
# + suchascream. Detection adds anomaly score
|
||
# either way; enforce upside is low.
|
||
# 941xxx (XSS) — Divi rich-text editor saves, TinyMCE submissions
|
||
# 942xxx (SQLi) — WP admin queries reflected in params
|
||
# 920xxx (other) — most 920xxx rules; 920440 specifically promoted above
|
||
# 933150 — PHP injection FP on WooCommerce checkout
|
||
# (`session_start` literal appearing in billing form data)
|
||
# 950xxx-953xxx — Data leakage / backup-file disclosure (mixed FP)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# RESERVED RULE-ID RANGE: 990000000 – 990999999
|
||
# WHP's coraza_rule_manager generates per-host-exception rules in this range
|
||
# (rule ID = 990000000 + target_rule_id). Do NOT add new rules in this range
|
||
# from any other source. When bumping the coraza-spoa pin, check the CRS
|
||
# changelog for new rules with 9-digit IDs (rare but possible) and re-namespace
|
||
# if collision risk emerges.
|
||
# ---------------------------------------------------------------------------
|