GitHub Advisoryadvisory · Go

GHSA-6r3q-mjv7-xr8m Coraza: Silent argument drop at ArgumentLimit allows bypass of ARGS-targeted rules via parameter flooding

HIGH Published 2026-10-06 Modified 2026-10-06 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

Weakness · CWE (2)

  • CWE-693 Protection Mechanism Failure weakness
  • CWE-770 Allocation of Resources Without Limits or Throttling weakness

References