GitHub Advisoryadvisory · Go
GHSA-6r3q-mjv7-xr8m Coraza: Silent argument drop at ArgumentLimit allows bypass of ARGS-targeted rules via parameter flooding
HIGH
Official page ↗
- Aliases
- CVE-2026-41510
- Ecosystem
- Go
- Packages
- github.com/corazawaf/coraza/v3
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:L
- Weaknesses
- CWE-693, CWE-770
- Reviewed
- 2026-10-06
Affected packages
• Go github.com/corazawaf/coraza/v3: >= 3.0.0, < 3.8.1 → fixed in 3.8.1
Description
Root Cause
File: `internal/corazawaf/transaction.go`, lines 770–808 (since commit 2fd87b89, PR #812, 2023-06-14)
```go
func (tx *Transaction) AddGetRequestArgument(key string, value string) {
if tx.checkArgumentLimit(tx.variables.argsGet) {
tx.debugLogger.Warn().Msg("skipping get request argument, over limit")
return
}
tx.variables.argsGet.Add(key, value)
}
func (tx *Transaction) checkArgumentLimit(c *collections.NamedCollection) bool {
return c.Len() >= tx.WAF.ArgumentLimit
}
```
`AddGetRequestArgument`, `AddPostRequestArgument`, and `AddPathRequestArgument` silently `return` once the per-collection argument count reaches `WAF.ArgumentLimit` (default `1000`, see `internal/corazawaf/waf.go:346`). No error variable is set, no transaction flag is raised, and no rule can observe that a drop occurred.
Worse, `ExtractGetArguments` (`transaction.go:761`) iterates the `map[string][]string` returned by `urlutil.ParseQuery`:
```go
func (tx *Transaction) ExtractGetArguments(uri string) {
data := urlutil.ParseQuery(uri, '&')
for k, vs := range data { // Go map iteration order is randomized
for _, v := range vs {
tx.AddGetRequestArgument(k, v)
}
}
}
```
Because Go randomizes map iteration order, which of the caller-supplied arguments survive the limit is non-deterministic. An attacker can pad the URI with filler arguments; any one of them — including the malicious payload — may be the one silently discarded, and therefore invisible to every `SecRule` targeting `ARGS`, `ARGS_GET`, or `ARGS_NAMES`.
Secondary finding — POST urlencoded processor bypasses the cap entirely
`internal/bodyprocessors/urlencoded.go:29` populates `ARGS_POST` without invoking `checkArgumentLimit`:
```go
values := urlutil.ParseQuery(b, '&')
argsCol := v.ArgsPost()
for k, vs := range values {
argsCol.Set(k, vs) // direct write, no limit check
}
```
So `AddPostRequestArgument`'s cap is effectively dead code for real urlencoded bodies. `ARGS_POST` grows unbounded — both a bypass surface and a memory-DoS surface.
Impact
Any `SecRule` or CRS rule that inspects `ARGS`, `ARGS_GET`, `ARGS_NAMES`, `ARGS_GET_NAMES`, or `ARGS_PATH` can be evaded by inflating the request's argument count past `SecArgumentsLimit` (default `1000`). The bypass probability per request scales with overflow:
| Total args in request | Observed bypass rate of ARGS rule |
|---|---|
| 1000 (at limit) | 0 / 50 (0.0%) |
| 1001 (1 over) | 0 / 2000 (m_argumentsLimit.m_set
&& m_variableArgs.size() >= m_rules->m_argumentsLimit.m_value) {
ms_dbg(4, "Skipping request argument, over limit (...)")
return false; // return value is ignored at the GET callsite
}
...
}
```
ModSecurity is in fact *more deterministic* than Coraza — its query-string parser (`extractArguments`, `transaction.cc:254`) splits with an ordered `ssplit`, so it is the *tail* of the query that silently drops. An attacker places the payload first and pads the tail; no retry loop needed.
However, ModSecurity's default configuration papers over the engine bug. `modsecurity.conf-recommended` ships:
```conf
SecArgumentsLimit 1000
If SecArgumentsLimit has been set, you probably want to reject any
request body that has only been partly parsed. The value used in this
rule should match what was used with SecArgumentsLimit
SecRule &ARGS "@ge 1000" \
"id:'200007',phase:2,t:none,log,deny,status:400,msg:'Failed to fully parse request body due to large argument count',severity:2"
```
Because `addArgument` caps the single `m_variableArgs` collection at exactly the limit, `&ARGS == limit` iff the limit was hit — rule 200007 converts silent-drop into explicit `HTTP 400`. ModSecurity also has a complementary `REQBODY_ERROR` path: its JSON processor cancels parsing on `addArgument` failure, and rule 200002 denies on `REQBODY_ERROR` (verified by `test/test-cases/regression/secargumentslimit.json`, test 2/2).
Coraza's `coraza.conf-recommended` ships no equivalent rule. That is what makes the bug exploitable out-of-the-box in Coraza and not in ModSecurity.
| | Silent-drop at engine | Compensating default rule | Exploitable out-of-the-box |
|---|---|---|---|
| ModSecurity v3 | yes | yes (`id:200007`, `&ARGS @ge 1000`) | no — denies at limit |
| Coraza v3 | yes | no | yes |
Mitigation
Recommended fixes, in the order they should be applied. Config-layer (#1) closes the default-install exposure quickly; engine-layer (#2, #3) is the durable fix.
1. Ship compensating rules in `coraza.conf-recommended` (config-layer, immediate)
Port the ModSecurity guard, but keyed per-collection — Coraza caps `ARGS_GET`, `ARGS_POST`, and `ARGS_PATH` independently, unlike ModSecurity's unified `m_variableArgs`. A single `&ARGS @ge 1000` check on the concatenated collection would false-positive at e.g. GET=500 + POST=500 (no drops occurred but aggregate == 1000):
```conf
SecRule &ARGS_GET "@ge 1000" \
"id:200007,phase:2,t:none,log,deny,status:400,msg:'ARGS_GET over SecArgumentsLimit; request partially parsed'"
SecRule &ARGS_POST "@ge 1000" \
"id:200008,phase:2,t:none,log,deny,status:400,msg:'ARGS_POST over SecArgumentsLimit; request partially parsed'"
SecRule &ARGS_PATH "@ge 1000" \
"id:200009,phase:2,t:none,log,deny,status:400,msg:'ARGS_PATH over SecArgumentsLimit; request partially parsed'"
```
Both `&VAR` (variable count, `internal/seclang/rule_parser.go:40`) and `@ge` (`internal/operators/testdata/ge.json`) are supported. Thresholds must track `SecArgumentsLimit` if the operator overrides it.
2. Expose a transaction-visible flag (engine-layer, durable)
Introduce an `ARGUMENTS_LIMIT_REACHED` collection variable, analogous to `MULTIPART_STRICT_ERROR` and `URLENCODED_ERROR`, set to `1` by `AddGetRequestArgument` / `AddPostRequestArgument` / `AddPathRequestArgument` whenever they drop. Replace the config rules above with a single engine-backed check:
```conf
SecRule ARGUMENTS_LIMIT_REACHED "@eq 1" \
"id:200006,phase:1,t:none,log,deny,status:413,msg:'Argument limit reached; request rejected'"
```
This protects operators with hand-rolled configurations, not just those who use the recommended file.
3. Close the POST urlencoded body-processor gap
`internal/bodyprocessors/urlencoded.go` should ro…
Related
Alias · CVE (1)
- CVE-2026-41510 OWASP Coraza WAF is a golang modsecurity compatible web application firewall library. vulnerability
References
- https://github.com/corazawaf/coraza/security/advisories/GHSA-6r3q-mjv7-xr8m web
- https://github.com/corazawaf/coraza/commit/146c2f79f39ad16f13787e7d67ad400f8d8cb9a3 web
- https://github.com/corazawaf/coraza/commit/814e1898e083d2ff2ceb644382d0da17e930f93f web
- https://github.com/corazawaf/coraza/commit/b98359bb7e7606c95100dcc7ad490d2d624e6dea web
- https://github.com/corazawaf/coraza package
- https://github.com/corazawaf/coraza/releases/tag/v3.8.1 web