Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessSend truncated multipart/form-data request
-
DeliveryParser hits io.ErrUnexpectedEOF, swallowed silently
-
ExploitMULTIPART_STRICT_ERROR stays 0, rules 200002/200003 don't fire
-
ExecutionRequest passes WAF as benign
-
ImpactBackend's permissive parser extracts smuggled payload
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A05:2025 - Injection
ASVS 5.0
V2 - Validation and Business Logic Input validation, allowlisting
|
| Exploitation Conditions | Requires Coraza v3.4.0-3.7.0 deployed inline as a WAF with SecRuleEngine On and SecRequestBodyAccess On, AND the operator relying on the recommended coraza.conf rules 200002 (REQBODY_ERROR) / 200003 (MULTIPART_STRICT_ERROR) as the multipart parser-disagreement defense. The trigger is a multipart/form-data request whose body ends prematurely so the Go reader raises io.ErrUnexpectedEOF - e.g. a missing closing boundary or a mid-part cutoff (name="x"\r\n\r\nabc). No authentication is needed (PR:N) and exploitation works against the default recommended configuration. LIMITING FACTORS: the bypass only neutralizes a defense-in-depth rule, so actual compromise additionally requires a backend multipart parser that disagrees with Go's and extracts the smuggled content; deployments not using rule 200003, or those intentionally running SecRequestBodyLimitAction ProcessPartial, are affected differently and may accept truncated bodies by design. |
| Impact | This is a defense-in-depth control bypass in the OWASP Coraza WAF engine, and the signals point to a real-but-moderate priority rather than an urgent emergency. The CVSS 3.1 vector (AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:N, base 5.8) is internally consistent with the description: a remote, unauthenticated attacker sends a crafted multipart body over the network with low complexity and no user interaction, and the scope change (S:C) plus integrity-only impact (I:L) correctly reflect that the harm lands on the protected backend, not on Coraza itself. No EPSS score was provided (MISSING), the CVE is NOT in CISA KEV, and there is no evidence of active exploitation; however, the public GHSA advisory documents a concrete proof-of-concept (three curl cases showing HTTP 200 where 400 is expected), so working reproduction steps are public. The key nuance that lowers real-world severity: this flaw does not directly compromise anything on its own - it silently clears the MULTIPART_STRICT_ERROR flag so recommended rules 200002/200003 fail to fire, which only matters if (a) the operator relies on those recommended rules as their multipart-evasion defense and (b) a downstream parser (PHP/Node/Java/etc.) actually disagrees with Go's mime/multipart reader and extracts the smuggled content. So a CVSS of 5.8 with a documented PoC but no KEV/EPSS signal is best read as 'targeted, conditional WAF-evasion enabler' rather than mass-exploitable RCE. |
| Exploit ScenarioDerived | An attacker submits a multipart/form-data POST whose body is deliberately truncated - for example two parts with no closing boundary, or a mid-part cutoff - so Coraza's parser hits io.ErrUnexpectedEOF. Because v3.4.0-3.7.0 swallow that error without setting MULTIPART_STRICT_ERROR, recommended rules 200003 and 200002 never fire and the request is passed through (HTTP 200), letting a smuggled field or payload reach a backend whose more permissive multipart parser still extracts it. A public PoC in the GHSA advisory demonstrates the bypass with real curl requests and confirms transaction.variables.multipartStrictError == 0. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Oct 06, 2026
Defense-in-depth bypass in Coraza WAF 3.4.0 through 3.7.0 causes the multipart body processor to silently swallow io.ErrUnexpectedEOF, so a truncated multipart/form-data request neither sets MULTIPART_STRICT_ERROR nor returns a request-body error, and the two canonical defensive rules shipped in coraza.conf-recommended (200003 MULTIPART_STRICT_ERROR and 200002 REQBODY_ERROR) fail open. Remote unauthenticated attackers (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:N, 5.8) can send a body that omits the closing boundary or cuts off mid-part and have it processed as if well-formed, neutralizing the parser-disagreement catch-all and enabling multipart smuggling or CRS evasion chains against backends whose multipart parser disagrees with Go's mime/multipart reader. No active exploitation is confirmed (not in CISA KEV), though the vendor advisory publishes a working reproduction proof-of-concept and the vendor-released fix is v3.8.0.
RemediationAI
Vendor-released patch: upgrade to Coraza v3.8.0 (https://github.com/corazawaf/coraza/releases/tag/v3.8.0), which contains commit f94c81bec209f658120c418448d0590a549b71df adding the missing v.MultipartStrictError().(*collections.Single).Set("1") calls at all three sites in internal/bodyprocessors/multipart.go and regression tests for truncated bodies; there is no configuration-only fix that restores the invariant. If immediate upgrade is impossible, the most direct compensating control is to back-port that two-line-per-site change into the vendored module and rebuild, since the patch preserves PR #1453's ProcessPartial semantics (trade-off: you must maintain a local fork until the upgrade lands), or temporarily pin to a pre-3.4.0 release where io.ErrUnexpectedEOF returns err and rule 200002 fires (trade-off: loses SecRequestBodyLimitAction ProcessPartial support and older feature sets). Operators relying solely on the recommended rules 200002/200003 should add an independent multipart framing validation layer in front of or behind Coraza - for example a reverse proxy that verifies the closing boundary and Content-Length/body consistency, or backend-side strict multipart validation - because rule 200003 is the only defense-in-depth rule for multipart in coraza.conf-recommended and offers no secondary signal once bypassed. Deployments intentionally running SecRequestBodyLimitAction ProcessPartial, which accept truncated bodies by design, should pair the engine fix by scoping rule 200003 or downgrading it to detection-only for the legitimate truncation case; note that doing so re-widens exposure unless the truncation is correlated with an actual configured request-body-limit hit. Consult the advisory at https://github.com/corazawaf/coraza/security/advisories/GHSA-r3rm-qphw-hh76 for the authoritative fix details.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 4 packages in go, euvd
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
Coraza is a Go-based WAF engine and the reference OWASP Core Rule Set implementation for Go (CPE pkg:go/github.com_corazawaf_coraza_v3); the flaw sits in internal/bodyprocessors/multipart.go, the component that reads multipart/form-data bodies into ARGS via Go's mime/multipart reader. CWE-20 (Improper Input Validation): three error branches (filesystem-backed file branch around lines 71-77, TinyGo discard branch around lines 82-88, and the field branch around lines 102-113) special-case io.ErrUnexpectedEOF only to suppress the fatal return, setting seenUnexpectedEOF or breaking out of the loop without setting the multipart-strict-error transaction variable. The processor therefore returns nil for any prematurely terminated body, leaving MULTIPART_STRICT_ERROR at its initial value 0 and never propagating a request-body error. This is a regression introduced by PR #1453 (commit 3347961b, merged 2026-03-06, first shipped in v3.4.0), which legitimately needed to tolerate truncation for SecRequestBodyLimitAction ProcessPartial so partially received parts still surface; it over-corrected by silencing the operator-visible strict-error flag rather than keeping the non-fatal break/flag pair. Every other error path in the function correctly sets MULTIPART_STRICT_ERROR before returning, confirming this is an inconsistency rather than a systemic parser failure, and the upstream fix commit f94c81be adds exactly three one-line flag setters plus regression tests asserting the flag is raised for truncated payloads. The impact class is parser-inconsistency evasion: rule 200003 is the only recommended catch-all for cases where Coraza's Go parser and the backend parser (PHP, Node, Java, libmodsecurity) disagree about field boundaries.
Know when Go gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:N
Remote unauthenticated crafted multipart request (AV:N/PR:N/UI:N) with deterministic crafting (AC:L); only integrity of the protected downstream system is affected (S:C, I:L), with no confidentiality or availability impact.
Primary rating from Vendor (https://github.com/corazawaf/coraza).
CVSS VectorVendor: https://github.com/corazawaf/coraza
DescriptionCVE.org
Root Cause
File: internal/bodyprocessors/multipart.go (since commit 3347961b, PR #1453 *"feat: ignore unexpected EOF in MIME multipart request body processor"*, merged 2026-03-06, first shipped in v3.4.0).
The multipart body processor treats io.ErrUnexpectedEOF as a benign condition. Three sites are affected; all mishandle the error the same way.
File branch, filesystem-backed (lines 71-77)
sz, err := io.Copy(temp, p)
if err != nil {
if !errors.Is(err, io.ErrUnexpectedEOF) {
v.MultipartStrictError().(*collections.Single).Set("1")
return err
}
seenUnexpectedEOF = true // <-- flag never set for UnexpectedEOF
}File branch, TinyGo path (lines 82-88)
sz, err := io.Copy(io.Discard, p)
if err != nil {
if !errors.Is(err, io.ErrUnexpectedEOF) {
v.MultipartStrictError().(*collections.Single).Set("1")
return err
}
seenUnexpectedEOF = true // <-- same gap
}Field branch (lines 102-113)
data, err := io.ReadAll(p)
if err != nil {
if !errors.Is(err, io.ErrUnexpectedEOF) {
v.MultipartStrictError().(*collections.Single).Set("1")
return err
}
}
...
if errors.Is(err, io.ErrUnexpectedEOF) {
break // <-- exits loop with no flag set
}The function then returns nil at line 116 for any body that ended prematurely. Consequences:
MULTIPART_STRICT_ERRORstays at its initial value0.REQBODY_ERRORis not propagated either (sinceProcessRequestreturnsnil).- Neither of the two canonical defensive rules shipped in
coraza.conf-recommendedfires:
SecRule REQBODY_ERROR "!@eq 0" "id:200002,phase:2,deny,status:400,..."
SecRule MULTIPART_STRICT_ERROR "!@eq 0" "id:200003,phase:2,deny,status:400,..."All other error branches in the same function (lines 27, 48, 66, 84, 104) correctly set MULTIPART_STRICT_ERROR before returning; this is an inconsistency introduced in #1453, not a systemic issue.
Context - why the error is swallowed
PR #1453 was introduced to support SecRequestBodyLimitAction ProcessPartial: when a body is cut off because it hit the configured request-body limit, the parser should still surface the parts it did receive. The PR legitimately needs to avoid return err on ErrUnexpectedEOF. But it also silenced the strict-error flag, which is the wrong compromise - the flag is exactly how operators observe that something was incomplete. The fix should keep the non-fatal break and still set MULTIPART_STRICT_ERROR, letting the operator decide (via rule 200003 or their own policy) whether partial processing is acceptable.
Impact
Rule 200003 is Coraza's blanket defense against multipart parser-inconsistency evasions - attack classes where the body is crafted so Coraza's Go mime/multipart reader and the backend's multipart parser (PHP, Node, Java, legacy libmodsecurity, etc.) disagree about where fields start or end. The rule fails-closed on *any* malformed body, so the operator does not need to enumerate every parser-disagreement trick. That defense is now void for any evasion that also truncates the body.
Examples of what becomes reachable:
- Smuggling a second field past the truncation boundary that the backend's more permissive parser still extracts.
- Hiding payload bytes after a deliberately malformed
Content-Dispositionheader that the Go parser refuses but the backend accepts. - Generic CRS evasion chains that depend on rule 200003 as a catch-all.
The fix is tiny and low-risk. The impact is disproportionately large because rule 200003 is the *only* defense-in-depth rule for multipart in the recommended config - there is no secondary signal.
Proof of Concept
Start a Coraza-wrapped HTTP server shipping the canonical defensive rules from coraza.conf-recommended:
SecRuleEngine On
SecRequestBodyAccess On
SecRule REQBODY_ERROR "!@eq 0" \
"id:200002,phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.'"
SecRule MULTIPART_STRICT_ERROR "!@eq 0" \
"id:200003,phase:2,t:none,log,deny,status:400,msg:'Multipart strict validation failed.'"Three real-curl requests against the listener:
| Body | Expected (with rule 200003) | Observed |
|
|---|---|---|---| | 1 | Well-formed field1=benign + closing boundary | HTTP 200 | HTTP 200 (baseline) | | 2 | Two parts, no closing boundary (...MALFORMED_NO_TRAILING_BOUNDARY) | HTTP 400 | HTTP 200 | | 3 | Mid-part cutoff: name="x"\r\n\r\nabc (no \r\n, no boundary) | HTTP 400 | HTTP 200 | Server-side match log is empty for cases 2 and 3: neither rule 200002 nor rule 200003 fires. Example (case 2):
--PoCBoundary12345\r\n
Content-Disposition: form-data; name="field1"\r\n\r\n
benign\r\n
--PoCBoundary12345\r\n
Content-Disposition: form-data; name="truncated"\r\n\r\n
MALFORMED_NO_TRAILING_BOUNDARY→ HTTP 200, no audit record, transaction.variables.multipartStrictError == 0.
Mitigation
A two-line change per site in internal/bodyprocessors/multipart.go:
if errors.Is(err, io.ErrUnexpectedEOF) {
v.MultipartStrictError().(*collections.Single).Set("1")
seenUnexpectedEOF = true // keep existing break semantics
}Apply at the three sites (lines 71-77, 82-88, 102-113 in the current code). No change to the return err control flow is needed - the fix only adds the flag-setter alongside the existing seenUnexpectedEOF = true / break path. PR #1453's ProcessPartial goal is preserved.
Operators running in ProcessPartial mode who intentionally allow truncated bodies should pair this with a config-level change (downgrade rule 200003 to detection-only, or scope it with a secondary check on whether SecRequestBodyLimit was actually hit). The engine change above is safe by default - it restores the invariant that malformed multipart always raises MULTIPART_STRICT_ERROR.
Affected versions
Introduced in PR #1453 (commit 3347961b, merged 2026-03-06). First released in v3.4.0 and still present on main at 599ae64a.
Affected: >= 3.4.0, <= 3.7.0.
Releases prior to v3.4.0 returned err on io.ErrUnexpectedEOF and so REQBODY_ERROR would propagate via rule 200002 even if MULTIPART_STRICT_ERROR was not set - a different (arguably stricter) behavior that did not exhibit this bypass.
References
internal/bodyprocessors/multipart.golines 70-113coraza.conf-recommendedruleid:200003(MULTIPART_STRICT_ERROR)- PR #1453 - introduction of
ErrUnexpectedEOFswallowing coraza.conf-recommendedruleid:200002(REQBODY_ERROR) - also not raised becauseProcessRequestreturnsnil
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Denial of service against HTTP/2 server implementations allows remote unauthenticated attackers to exhaust server resour
The net/http package in Go through 1.6 does not attempt to address RFC 3875 section 4.1.18 namespace conflicts and there
Critical certificate validation bypass in Go crypto/tls during session resumption. If ClientCAs or RootCAs fields are mu
net/url in Go before 1.11.13 and 1.12.x before 1.12.8 mishandles malformed hosts in URLs, leading to an authorization by
Casdoor through 4.4.0 exposes its instance-wide built-in JWT signing private key unmasked through the /api/get-certs and
Host header injection in Nezha 2.2.3's OAuth2 redirect endpoint (/api/v1/oauth2/{provider}) allows an unauthenticated re
Session cross-wiring in SUSE NeuVector before 5.6.1 allows an unauthenticated remote attacker to obtain another user's a
Unauthenticated path traversal in Uber Kraken through 0.1.29 allows remote attackers to read files outside the configure
The "go get" implementation in Go 1.9.4, when the -insecure command-line option is used, does not validate the import pa
Denial of service in Temporal Server allows an authenticated caller holding at least namespace read permission to crash
Account takeover in Klever-Go's KleverUpdateAccountPermission built-in (all versions prior to 1.7.20) allows an attacker
Maliciously crafted on-chain account names processed by Klever-Go prior to 1.7.20 are spliced into Elasticsearch _bulk r
EUVD-2026-97828
GHSA-r3rm-qphw-hh76
NVD
CVE.org
EPSS
Exploit-DB