Coraza v3 (Go WAF) silently drops QUERY_STRING and ARGS_GET when a URI contains raw control bytes that Go's URL parser rejects, so typical OWASP CRS GET-side rules never fire on those requests. The practical exposure is narrow: stock net/http deployments reject such URIs with a 400 before Coraza sees them, so only integrations that pass raw URIs directly (coraza-spoa behind HAProxy, coraza-proxy-wasm on Envoy, custom FFI/WASM hosts) are affected, which is why the score is only 4.0 (Medium) with high attack complexity. With 6,147 downstream dependents, no EPSS data, no public exploit, no Nuclei template and no KEV listing, this is not a breaking-alert event, but any WAF bypass quietly removes a defensive layer in front of AI APIs and model gateways. Upgrade to github.com/corazawaf/coraza/v3 3.8.0, and until then add a fail-closed rule that denies requests with a URI parse error or control bytes, and keep rules on REQUEST_URI_RAW, which is populated correctly.
What is the risk?
Medium (CVSS 4.0, AV:N/AC:H/S:C/I:L). Exploitation is trivial once the attacker can reach a non-net/http integration, since they only append a control byte such as NUL, CR, LF or tab to the URI. Exposure is limited to HAProxy SPOA, Envoy WASM and custom embeddings. The WAF only hides the payload, so the real impact depends on whether the protected application is vulnerable to the hidden payload. No known exploitation, no EPSS score, no public exploit and not in KEV.
How does the attack unfold?
What systems are affected?
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| Anthropic Python | go | >= 3.0.0, < 3.8.0 | 3.8.0 |
Do you use Anthropic Python? You're affected.
How severe is it?
What is the attack surface?
What should I do?
1 step1) Upgrade to github.com/corazawaf/coraza/v3 3.8.0 (and matching coraza-spoa / coraza-proxy-wasm builds that vendor it). 2) Until patched, add a fail-closed rule that denies requests when URLENCODED_ERROR is set at phase 1, or when REQUEST_URI_RAW matches control bytes (\x00-\x1F, \x7F), for example a regex on REQUEST_URI_RAW. 3) Block control characters in URIs at the HAProxy or Envoy layer before they reach the WAF. 4) Duplicate critical GET-side signatures against REQUEST_URI_RAW. 5) Detection: log and alert on HTTP requests with control bytes in the request line and on URLENCODED_ERROR hits in WAF audit logs. 6) Inventory which deployments use non-net/http Coraza integrations.
How is it classified?
Which compliance frameworks are affected?
This CVE is relevant to:
Frequently Asked Questions
What is GHSA-x26q-wvhg-fh4m?
Coraza v3 (Go WAF) silently drops QUERY_STRING and ARGS_GET when a URI contains raw control bytes that Go's URL parser rejects, so typical OWASP CRS GET-side rules never fire on those requests. The practical exposure is narrow: stock net/http deployments reject such URIs with a 400 before Coraza sees them, so only integrations that pass raw URIs directly (coraza-spoa behind HAProxy, coraza-proxy-wasm on Envoy, custom FFI/WASM hosts) are affected, which is why the score is only 4.0 (Medium) with high attack complexity. With 6,147 downstream dependents, no EPSS data, no public exploit, no Nuclei template and no KEV listing, this is not a breaking-alert event, but any WAF bypass quietly removes a defensive layer in front of AI APIs and model gateways. Upgrade to github.com/corazawaf/coraza/v3 3.8.0, and until then add a fail-closed rule that denies requests with a URI parse error or control bytes, and keep rules on REQUEST_URI_RAW, which is populated correctly.
Is GHSA-x26q-wvhg-fh4m actively exploited?
No confirmed active exploitation of GHSA-x26q-wvhg-fh4m has been reported, but organizations should still patch proactively.
How to fix GHSA-x26q-wvhg-fh4m?
1) Upgrade to github.com/corazawaf/coraza/v3 3.8.0 (and matching coraza-spoa / coraza-proxy-wasm builds that vendor it). 2) Until patched, add a fail-closed rule that denies requests when URLENCODED_ERROR is set at phase 1, or when REQUEST_URI_RAW matches control bytes (\x00-\x1F, \x7F), for example a regex on REQUEST_URI_RAW. 3) Block control characters in URIs at the HAProxy or Envoy layer before they reach the WAF. 4) Duplicate critical GET-side signatures against REQUEST_URI_RAW. 5) Detection: log and alert on HTTP requests with control bytes in the request line and on URLENCODED_ERROR hits in WAF audit logs. 6) Inventory which deployments use non-net/http Coraza integrations.
What systems are affected by GHSA-x26q-wvhg-fh4m?
This vulnerability affects the following AI/ML architecture patterns: Model serving behind WAF, LLM API gateways, Agent backends behind HAProxy/Envoy, ML web UIs and dashboards.
What is the CVSS score for GHSA-x26q-wvhg-fh4m?
GHSA-x26q-wvhg-fh4m has a CVSS v3.1 base score of 4.0 (MEDIUM).
What is the AI security impact?
Affected AI Architectures
MITRE ATLAS Techniques
AML.T0049Exploit Public-Facing ApplicationAML.T0107Exploitation for Defense EvasionCompliance Controls Affected
What are the technical details?
Original Advisory
## Root Cause File: `internal/corazawaf/transaction.go`, lines 834–866. ```go parsedURL, err := url.ParseRequestURI(uri) query := "" if err != nil { tx.variables.urlencodedError.Set(err.Error()) path = uri tx.variables.requestURI.Set(uri) /* tx.Variables.VARIABLE_URI_PARSE_ERROR.Set("1") posRawQuery := strings.Index(uri, "?") if posRawQuery != -1 { tx.ExtractArguments("GET", uri[posRawQuery+1:]) path = uri[:posRawQuery] query = uri[posRawQuery+1:] } else { path = uri } tx.Variables.RequestUri.Set(uri) */ } else { tx.ExtractGetArguments(parsedURL.RawQuery) // only path that populates ARGS_GET tx.variables.requestURI.Set(parsedURL.String()) path = parsedURL.Path query = parsedURL.RawQuery } ... tx.variables.queryString.Set(query) ``` When `url.ParseRequestURI(uri)` returns an error — which Go's stdlib does for any URI containing raw control bytes (`\x00`, `\n`, `\r`, `\t`, other `0x00–0x1F`, `0x7F`) — the error branch silently produces an empty `QUERY_STRING` and an empty `ARGS_GET` collection. The fallback logic that should split on `?` and populate the GET arguments from the raw tail is already present in the source as a commented-out block, referencing a `VARIABLE_URI_PARSE_ERROR` variable that was never wired up. Consequences on the error branch: - `ARGS_GET` / `ARGS_GET_NAMES` / `ARGS` (union) are **empty** — `ExtractGetArguments` is never called. - `QUERY_STRING` is **empty** (initial `query := ""` at line 835 persists through to `queryString.Set(query)` at line 866). - `REQUEST_FILENAME` / `REQUEST_BASENAME` contain the entire URI including any `?…` query suffix (because `path = uri` at line 838 bypasses the parse, and the subsequent `strings.LastIndexAny(path, "/\\")` runs over the raw URI). - `URLENCODED_ERROR` is set to the Go error message. That variable is *also* set by the urlencoded body processor on body-decode failures, so an operator cannot distinguish "malformed URI" from "malformed request body" without string-matching the error text. - `REQUEST_URI_RAW` (set unconditionally at line 822, before the parse) **is** populated correctly. Any rule targeting `ARGS_GET`, `ARGS`, `ARGS_NAMES`, `ARGS_GET_NAMES`, or `QUERY_STRING` — which is the default target set for the vast majority of OWASP CRS GET-side signature rules — does not fire against attacker content that reaches Coraza via a URI Go's `net/url` rejects. ## Reachability This issue **does not affect the standard `coraza/v3/http` + `net/http` integration**. Go's `http.ReadRequest` calls `url.ParseRequestURI` first and rejects malformed URIs with `400 Bad Request` before `ProcessURI` is invoked. Verified experimentally against a Coraza-wrapped `net/http` server — a raw request with a control-byte-laced URI produced `HTTP 400`, and the handler was never reached. The bug is reachable when an integration forwards raw URI bytes to `tx.ProcessURI` directly, bypassing Go's HTTP parser: - **`coraza-spoa`** — HAProxy SPOP agent. Receives URI from HAProxy, which permits bytes `net/http` rejects. - **`coraza-proxy-wasm`** — Envoy WASM filter. Passes the `:path` pseudo-header from Envoy. - Custom FFI/WASM hosts and any embedder calling `tx.ProcessURI(rawURI, method, httpVersion)` with bytes not pre-validated by Go's URL parser. This gates the attack to Attack Complexity:High — a standard Go HTTP deployment is not exposed. ## Proof of Concept Direct-API reproduction (simulating the non-net/http integration path): ```go waf, _ := coraza.NewWAF(coraza.NewWAFConfig().WithDirectives(` SecRuleEngine On SecRule ARGS_GET "@contains ATTACK_HERE_XYZ" "id:9001,phase:1,deny,status:403" SecRule QUERY_STRING "@contains ATTACK_HERE_XYZ" "id:9002,phase:1,deny,status:403" `)) for _, uri := range []string{ "/search?q=ATTACK_HERE_XYZ", // baseline "/search?q=ATTACK_HERE_XYZ\x00&y=1", // NUL byte "/search?q=ATTACK_HERE_XYZ\ninjected: header", // bare LF "/search?q=ATTACK_HERE_XYZ\rhdr: x", // bare CR "/search?q=ATTACK_HERE_XYZ\tx=1", // tab } { tx := waf.NewTransaction() tx.ProcessURI(uri, "GET", "HTTP/1.1") it := tx.ProcessRequestHeaders() // inspect tx.Variables().QueryString().Get() and tx.Variables().ArgsGet().FindAll() tx.Close() } ``` Observed: | URI | `QUERY_STRING` | `ARGS_GET` | interrupted? | |---|---|---|---| | `/search?q=ATTACK_HERE_XYZ` | `q=ATTACK_HERE_XYZ` | 1 entry | **yes (403)** | | `/search?q=ATTACK_HERE_XYZ\x00&y=1` | `""` | 0 entries | **no — BYPASS** | | `/search?q=ATTACK_HERE_XYZ\ninjected: header` | `""` | 0 entries | **no — BYPASS** | | `/search?q=ATTACK_HERE_XYZ\rhdr: x` | `""` | 0 entries | **no — BYPASS** | | `/search?q=ATTACK_HERE_XYZ\tx=1` | `""` | 0 entries | **no — BYPASS** | `REQUEST_URI_RAW` is populated correctly in every case (line 822 sets it before the parse), so a rule written against `REQUEST_URI_RAW` still catches the attack. CRS and most operator-written rules target `ARGS_GET` / `ARGS` / `QUERY_STRING` — those do not fire. HTTP-layer reachability check (stock `net/http`): ``` $ printf 'GET /?q=ATTACK_HERE_XYZ\x00&y=1 HTTP/1.1\r\nHost: x\r\n\r\n' | nc 127.0.0.1 8092 HTTP/1.1 400 Bad Request ``` Confirms the exposure is limited to non-net/http integrations. ## Mitigation Recommended fixes, in order: ### 1. Re-enable the existing fallback and wire up `URI_PARSE_ERROR` The code to fix this is already present as a commented-out block at `transaction.go:840–851`. Re-enable it, promote the referenced `VARIABLE_URI_PARSE_ERROR` to a real transaction variable, and populate `ARGS_GET` / `QUERY_STRING` from the raw `?…` tail: ```go if err != nil { tx.variables.urlencodedError.Set(err.Error()) tx.variables.uriParseError.Set("1") // new variable tx.variables.requestURI.Set(uri) if i := strings.Index(uri, "?"); i != -1 { path = uri[:i] query = uri[i+1:] tx.ExtractGetArguments(query) // populate ARGS_GET } else { path = uri } } else { ... } ``` ### 2. Ship a companion rule in `coraza.conf-recommended` ```conf SecRule URI_PARSE_ERROR "@eq 1" \ "id:'200010',phase:1,t:none,log,deny,status:400,msg:'URI failed to parse'" ``` This gives operators a fail-closed default (analogous to rule 200003 for multipart strict error and rule 200002 for body-parse error), so non-net/http integrations at least stop the request regardless of downstream rule coverage. ### 3. Do not overload `URLENCODED_ERROR` The current code uses `URLENCODED_ERROR` for URI parse failures. That variable is also set by the urlencoded body processor on body-decode errors; operators cannot distinguish the two causes without string-matching the error text, and any rule they add will fire on both classes of failure. A dedicated `URI_PARSE_ERROR` variable (per the commented-out TODO) is the right shape. ## Affected versions All releases on the v3 branch (`>= 3.0.0, <= 3.7.0`); the silent-drop behavior has been present since the first v3 release. Only deployments using non-net/http integrations (coraza-spoa, coraza-proxy-wasm, custom FFI) are exposed in practice. ## References - `internal/corazawaf/transaction.go` lines 834–866 (ProcessURI error branch) - `internal/corazawaf/transaction.go` line 822 (`REQUEST_URI_RAW` is populated before the parse, which is why `REQUEST_URI_RAW`-targeted rules still catch the attack) - Commented-out fallback at lines 840–851 referencing `VARIABLE_URI_PARSE_ERROR` - CWE-20 — Improper Input Validation - CWE-436 — Interpretation Conflict ### Severity (revised 2026-10-02) `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:L/A:N` (4.0, Medium). Attack Complexity stays High: the bypass only applies to integrations that pass Coraza a raw URI that Go's URL parser rejects, which `net/http` does not. The previous vector scored Integrity High (6.8); it is scored here like Coraza's other inspection bypasses. Impact metrics follow the convention used across Coraza's WAF-bypass advisories: the vulnerable component is Coraza, but the impact lands on the protected application, so Scope is Changed. The bypass hides a payload from inspection; the application still has to be vulnerable to it, so Integrity is Low and Confidentiality is not scored separately. _AI involvement in this section: Claude Opus 5.5 (Anthropic), via Claude Code, re-derived the CVSS vector from the project's triage guidance (AGENTS.md, "CVSS preconditions get verified, not copied from the report") and drafted this text. A human maintainer (fzipi) chose the `S:C/I:L` impact convention and directed this update._
Exploitation Scenario
An attacker targets an LLM gateway or model-serving API protected by Coraza running as an HAProxy SPOA agent. They send a GET request whose query string carries an injection payload (for example a path traversal or SQLi string aimed at a vulnerable backend endpoint) followed by a NUL or tab byte. HAProxy forwards the URI, Coraza fails to parse it and leaves ARGS_GET and QUERY_STRING empty, so CRS rules do not fire, and the request reaches the backend uninspected where the payload exploits an independent application flaw.
Weaknesses (CWE)
CWE-20 — Improper Input Validation: The product receives input or data, but it does not validate or incorrectly validates that the input has the properties that are required to process the data safely and correctly.
- [Architecture and Design] Consider using language-theoretic security (LangSec) techniques that characterize inputs using a formal language and build "recognizers" for that language. This effectively requires parsing to be a distinct layer that effectively enforces a boundary between raw input and internal data representations, instead of allowing parser code to be scattered throughout the program, where it could be subject to errors or inconsistencies that create weaknesses. [REF-1109] [REF-1110] [REF-1111]
- [Architecture and Design] Use an input validation framework such as Struts or the OWASP ESAPI Validation API. Note that using a framework does not automatically address all input validation problems; be mindful of weaknesses that could arise from misusing the framework itself (CWE-1173).
Source: MITRE CWE corpus.
CVSS Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:L/A:NReferences
Timeline
Related Vulnerabilities
CVE-2026-9460310.0Podman: checkpoint images override sandbox on run
Same package: anthropicCVE-2026-544498.8LangBot: RCE via arbitrary STDIO MCP command
Same package: anthropicCVE-2026-277758.8Gitea: cached permission check allows repo takeover
Same package: anthropicCVE-2026-554298.7Coder: cross-workspace agent hijack via app ID reuse
Same package: anthropicCVE-2026-75748.7Claude Desktop: VM integrity bypass enables RCE
Same package: anthropic