Skip to content

fix(933180): remove exponential backtracking in variable-function noise suffix#4669

Merged
fzipi merged 3 commits into
mainfrom
fix/933180-redos-suffix
Jun 21, 2026
Merged

fix(933180): remove exponential backtracking in variable-function noise suffix#4669
fzipi merged 3 commits into
mainfrom
fix/933180-redos-suffix

Conversation

@fzipi

@fzipi fzipi commented Jun 18, 2026

Copy link
Copy Markdown
Member

what

de-ambiguates the "noise" suffix in rule 933180 (PHP variable function calls) so it no longer exhibits exponential backtracking. the noise group between the variable name and the call (...) previously used free .+/.* in several branches, all of which overlapped the bare \s branch:

  • array access \[.+\]\[(?:[^\[\]]|\[[^\]]*\])*\] (no free .+; one level of nesting via two disjoint branches)
  • curly braces {.+}\{[^}]*\}
  • block comments /\*.*\*/ → the unrolling-the-loop form /\*[^*]*\*+(?:[^/*][^*]*\*+)*/
  • line comments //.* / #.* → newline-anchored //[^\n\r]*[\n\r] / #[^\n\r]*[\n\r]
  • ${expr} prefix \s*{.+}\s*\{[^}]*\}

adds four tests: two positives exercising the de-ambiguated comment branches in isolation, and two negatives locking in the behavior change (a #/// line comment with no trailing newline is not a function call, so it no longer matches).

why

with . matching newlines (DOTALL, as ModSecurity runs @rx), the \s branch and the comment/bracket bodies could all consume the same whitespace inside the *-quantified noise group, so an input without a real call backtracks exponentially. measured on Python re (DOTALL): the old pattern timed out by n≈20 ($a + \t#e×n); the new pattern is flat (0.23 ms at n=4000). PCRE2 and RE2 already tame the old form (PCRE2 via auto-possessification / required-literal study, RE2 has no backtracking), so this is a moderate-severity hardening — the same tier and technique as the 933160/933161 fix — not a runtime DoS on CRS's supported engines.

the nested-bracket form is deliberately chosen so $__[$_[1]](7) (one level of array nesting) keeps matching; a naive \[[^\]]*\] would have broken it. arbitrary-depth nesting is not matchable without RE2-incompatible recursion, which is a documented limit.

PL1, so the rule is always active; behavior is unchanged for every existing test (full REQUEST-933 suite passes, 406/406).

refs

ai disclosure

  • tools used: Claude (Opus 4.8)
  • assisted with: de-ambiguating the regex-assembly noise branches, drafting the four new go-ftw tests, regeneration via crs-toolchain, PR description drafting
  • review performed: confirmed flat behavior under DOTALL on Python re (n up to 4000) and verified PCRE2 does not exceed its backtracking limit with pcre2test (match_limit=1,000,000); ran the full REQUEST-933 go-ftw suite on modsec2-apache (406/406) and confirmed all 40 933180 tests pass including the nested-bracket and multiline-comment cases; ran crs-linter 1.0.0 (clean); manually verified all existing positive payloads still match and that the line-comment behavior change is correct

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

📊 Quantitative test results for language: eng, year: 2023, size: 10K, paranoia level: 1:
🚀 Quantitative testing did not detect new false positives

Comment thread regex-assembly/933180.ra Outdated
Comment thread regex-assembly/933180.ra Outdated
Comment thread regex-assembly/933180.ra Outdated
fzipi added a commit that referenced this pull request Jun 19, 2026
Address review on #4669: use + instead of * for the array-access and
curly-brace noise branches so empty [] / {} are not matched. This
restores the non-empty semantics of the original .+ patterns; the *
form had accidentally broadened them. Differential vs the original rule
shows zero true-positive loss; REQUEST-933 416/416 pass.
@fzipi
fzipi requested a review from theseion June 19, 2026 17:00
@fzipi
fzipi requested a review from theseion June 20, 2026 13:20
fzipi added 3 commits June 20, 2026 10:20
Address review on #4669: use + instead of * for the array-access and
curly-brace noise branches so empty [] / {} are not matched. This
restores the non-empty semantics of the original .+ patterns; the *
form had accidentally broadened them. Differential vs the original rule
shows zero true-positive loss; REQUEST-933 416/416 pass.
@fzipi
fzipi force-pushed the fix/933180-redos-suffix branch from d640f38 to 948f6ab Compare June 20, 2026 13:20
@fzipi
fzipi added this pull request to the merge queue Jun 21, 2026
Merged via the queue into main with commit 3828246 Jun 21, 2026
8 checks passed
@fzipi
fzipi deleted the fix/933180-redos-suffix branch June 21, 2026 12:01
fzipi added a commit that referenced this pull request Jun 22, 2026
…se suffix (#4669)

* fix(933180): remove exponential backtracking in variable-function noise suffix

* fix(933180): require non-empty array/brace bodies in noise suffix

Address review on #4669: use + instead of * for the array-access and
curly-brace noise branches so empty [] / {} are not matched. This
restores the non-empty semantics of the original .+ patterns; the *
form had accidentally broadened them. Differential vs the original rule
shows zero true-positive loss; REQUEST-933 416/416 pass.

* test(933180): verify empty array/brace bodies are not matched
@fzipi fzipi mentioned this pull request Jul 1, 2026
fzipi added a commit that referenced this pull request Jul 2, 2026
* fix: false positive with parameter name `.history` (#4614)

(cherry picked from commit 0885958)

* fix(942200): reduce false positives on payloads with comments (#4608)

* fix(942200): reduce false positives on payloads with comments

* add known fp test case

* improve test comments

* fix: wrong test

* add known fp test case

* fix typo

* fix typo

(cherry picked from commit 2c64b25)

* fix(unix): exclude `pg` command from pl-1 (#4613)

(cherry picked from commit 82195a4)

* fix(920100): drop HTTP/0.9 GET support from request line validation (#4621)

* fix(920100): drop HTTP/0.9 GET support from request line validation

remove the GET-without-protocol alternative from the regex assembly.
HTTP/0.9 is obsolete per RFC 9110 and the dedicated alternative was
the only one that did not require a protocol suffix in the request
line. GET requests with proper HTTP/1.x or HTTP/2 protocol versions
remain covered by the generic method alternative.

* Apply suggestions from code review

Co-authored-by: Max Leske <[email protected]>

---------

Co-authored-by: Max Leske <[email protected]>
(cherry picked from commit f8402a9)

* fix(920240, 920400): don't rely on content-type header (#4639)

(cherry picked from commit 2f1afb7)

* fix: remove exponential backtracking in 933160/933161 comment suffix (#4666)

* fix: remove exponential backtracking in 933160/933161 comment suffix

The suffix that skips whitespace and PHP comments between a function name
and the opening parenthesis used an ambiguous alternation where the
whitespace branch and the comment bodies both matched the same characters
under a `*` quantifier. On a backtracking engine a payload that never
reaches `(` backtracks exponentially.

De-ambiguate the alternation while keeping RE2 compatibility (no atomic
groups):
- block comments use the unrolled form instead of `/\*.*\*/`
- line comments (`#`, `//`) must terminate at a line break, because a line
  comment before `(` is only reachable across a newline; otherwise the `(`
  is inside the comment and there is no function call

Add positive tests for the comment/whitespace evasions (block, line with
newline, multi-line block, vertical tab, mixed) and negatives to 933161.

* docs: explain unrolled comment form in 933160/933161 .ra

* Update regex-assembly/933161.ra

Co-authored-by: Max Leske <[email protected]>

---------

Co-authored-by: Max Leske <[email protected]>
(cherry picked from commit c1df902)

* fix(941140): remove exponential backtracking in CSS url(javascript) detection (#4670)

* fix(941140): bound CSS declaration value to remove exponential backtracking

Move the inline pattern to regex-assembly/941140.ra and replace the free
'.+' in the declaration list with '[^;]+', removing the ambiguity that
caused exponential backtracking on backtracking engines (PCRE2/RE2 were
unaffected). Detection is unchanged (differential-tested).

* docs(941140): clarify whitespace handling and regression-test intent

Address review feedback on #4670:
- note that t:removeWhitespace strips whitespace before the regex runs, so
  the character classes intentionally do not account for it
- reword test_id 13 desc: it is a no-match canary for the unbounded-value
  shape (PCRE2 tames both old and new patterns), not a latency assertion

(cherry picked from commit fe59387)

* fix(933180): remove exponential backtracking in variable-function noise suffix (#4669)

* fix(933180): remove exponential backtracking in variable-function noise suffix

* fix(933180): require non-empty array/brace bodies in noise suffix

Address review on #4669: use + instead of * for the array-access and
curly-brace noise branches so empty [] / {} are not matched. This
restores the non-empty semantics of the original .+ patterns; the *
form had accidentally broadened them. Differential vs the original rule
shows zero true-positive loss; REQUEST-933 416/416 pass.

* test(933180): verify empty array/brace bodies are not matched

(cherry picked from commit 3828246)

* feat(crs-setup): add default actions for phases 3-5 (#4675)

(cherry picked from commit 09c1a11)

* fix: 4RI-250413 (#4672)

* fix: 4RI-250413

* fix: tests

* chore(formatting): auto fixes from pre-commit hooks

for more information, see https://pre-commit.ci

* fix: typo

* fix: tests

* apply code review suggestions

* apply code review suggestions

* chore(formatting): auto fixes from pre-commit hooks

for more information, see https://pre-commit.ci

* chore(942360): update regex with better format

Signed-off-by: Felipe Zipitria <[email protected]>

* fix(942362): add json. prefix support for JSON ARGS_NAMES

Rule 942362 uses a ^[\W\d]+ anchor that silently fails on libModSecurity3
because JSON payloads prepend json. (starting with j, a letter) to ARGS_NAMES.
Apply the same fix used in 942360/942361: ^(?:json\.)?[\W\d]+.

* Apply suggestions from code review

Co-authored-by: Max Leske <[email protected]>

* apply code review suggestions

* Update regex-assembly/942360.ra

Co-authored-by: Max Leske <[email protected]>

---------

Signed-off-by: Felipe Zipitria <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Felipe Zipitria <[email protected]>
Co-authored-by: Max Leske <[email protected]>
(cherry picked from commit 6e9ff3c)

* chore: release v4.25.1

* fix: correct indentation and action order in commented 900510 example

* docs: note libmodsecurity v3.0.16 requirement in CHANGES

* chore(tests): bump modsecurity-crs nginx image to libmodsecurity v3.0.16

* fix(901180): split XML attribute inspection gate to fix unused TX variable lint error

* fix: renumber duplicated test

Signed-off-by: Felipe Zipitria <[email protected]>

* fix: restore dropped json. prefix fixes and fix stale nginx CONNECT override

Cherry-picking PR #4672 (4RI-250413) took the LTS side wholesale for
rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf and
rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf, then only regenerated
the rule IDs backed by a regex-assembly (.ra) file. Two hand-written
inline regexes with no .ra file (932171, 942361) were silently dropped
in that process and never got the `(?:json\.)?` ARGS_NAMES prefix fix,
so 942361-9 failed to detect the SQLi payload on nginx/libmodsecurity3.

Also updates the nginx-overrides.yaml entry for 920100 test 4: the
libmodsecurity v3.0.16 image bump also picked up nginx 1.30.3 (up from
1.28.2), which now rejects CONNECT with 405 instead of 400.

* docs: note ModSecurity v2 limitation for XML attribute opt-out in CHANGES

---------

Signed-off-by: Felipe Zipitria <[email protected]>
Co-authored-by: Esad Cetiner <[email protected]>
Co-authored-by: Max Leske <[email protected]>
Co-authored-by: Prateek saini <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:lts-4.25.1 PR that must be backported to LTS release 4.25.1 🧙 regex-assembly release:fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants