# Coraza: Silent argument drop at ArgumentLimit allows bypass of ARGS-targeted rules via parameter flooding

Coraza silently drops request arguments past SecArgumentsLimit with randomized map order, letting parameter flooding hide payloads from ARGS-targeted WAF rules.

- Payout: Research
- Severity: high
- Program: Coraza
- Found by: WalTeR-RE
- Disclosed: 2026-10-06
- CVE: CVE-2026-41510
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-6r3q-mjv7-xr8m
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-6r3q-mjv7-xr8m
- Tags: area:web domain:web impact:dos impact:integrity kind:research platform:ghsa sev:high tech:go technique:param-pollution technique:source-review vuln:business-logic vuln:dos vuln:misconfiguration

**Target:** Coraza WAF engine (Go library) argument collection and body processors
**Root cause:** AddGetRequestArgument/AddPostRequestArgument/AddPathRequestArgument silently return once the per-collection count hits WAF.ArgumentLimit (default 1000) without setting any error or flag, and ExtractGetArguments iterates a Go map with randomized order so which argument is dropped is non-deterministic. The urlencoded body processor writes ARGS_POST directly, bypassing the cap entirely.
**Steps:**
1. Configure Coraza with `SecRuleEngine On` and `SecRule ARGS "@contains ATTACK_HERE_XYZ" "id:9001,phase:2,deny,status:403"`.
2. Baseline: `curl 'http://127.0.0.1:8090/?evil=ATTACK_HERE_XYZ'` returns 403.
3. Send a URI with 9999 filler parameters plus the malicious parameter at a random offset (10000 total args).
4. Observe ~94% of requests return HTTP 200 because the payload argument was silently discarded before phase:2 evaluation.
**How found:** Code review of transaction.go argument-adding paths revealed silent returns with no error/flag; comparing against ModSecurity v3 showed the same engine bug but a compensating default rule (id:200007, `&ARGS @ge 1000`) that Coraza's recommended config lacks, making it exploitable out-of-the-box. Empirical bypass-rate table matched the theoretical model (N-limit)/N.
**Exploitation:** Attacker floods the query string with filler parameters so total argument count exceeds SecArgumentsLimit; the malicious argument is probabilistically dropped before rule evaluation, evading ARGS/ARGS_GET/ARGS_NAMES rules including CRS SQLi/XSS/RCE/LFI families. Failed attempts cost nothing, so retries yield near-certain evasion. POST urlencoded bodies bypass the cap entirely, also enabling memory DoS.
**Escalation:** Chained with the JSON body processor gap (GHSA-3ww9-vw83-9w5x) and byte-budget amplification in array-length writes, turning the bypass into memory-exhaustion DoS; repeated-key values (`a=1&a=1...`) never tripped the distinct-key Len() check, producing ~60MB heap growth from 1M repeats.
**Insight:** When auditing WAF engines, check whether limit-enforcement paths set a transaction-visible flag or error; a silent drop with no observable signal is a bypass primitive. Also verify that body processors route through the same capped add functions rather than writing collections directly, and that limit checks count total values not distinct keys.

## Proof of concept (copied exactly from the original report)

**curl baseline and 10000-arg flooding bypass trial**

```
$ curl -s -o /dev/null -w '%{http_code}\n' 'http://127.0.0.1:8090/?evil=ATTACK_HERE_XYZ'
403

Now pad the URI with 9999 filler parameters and one malicious parameter placed at a random offset. Running 50 such trials against a real HTTP listener with real `curl`:

=== 10000 args (attacker adds 9999 filler parameters) ===
total_args=10000 trials=50 BLOCKED=3 BYPASS=47 (94.0%)
```

**readJSON byte-budget amplification PoC**

```
const keyLen = 20000
const depth = 200
body := `{"` + strings.Repeat("a", keyLen) + `":` + strings.Repeat("[", depth) + strings.Repeat("]", depth) + `}`
res, truncated, err := readJSON(body, 1024, 1000)
```

**Repeated-key argument limit bypass (a=1 x1M)**

```
`a=1&a=1&a=1...` never tripped the limit no matter how large it grew — confirmed empirically: 1,000,000 repeats of `a=1&` via the already-"protected" GET-argument path produced ~60MB of unbounded heap growth despite `SecArgumentsLimit 1000`
```

