XML External Entity (XXE) Attacks
An XML External Entity (XXE) attack abuses an XML parser’s willingness to resolve external entities — URIs declared inside a !DOCTYPE / !ENTITY block that the parser fetches and substitutes into the document. Because those URIs are basically just includes, an attacker who controls XML input (most commonly an API that accepts XML bodies) can use them to read local files, hit internal services (SSRF), or in rare cases execute code.
XML/DTD quick primer
A Document Type Definition (DTD) declares the shape of an XML document. A typical DTD:
<!DOCTYPE note [
<!ELEMENT note (to, from, heading, body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>This defines a document like:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>foo</to>
<from>bar</from>
<heading>baz</heading>
<body>etc.</body>
</note>Key pieces:
!DOCTYPEdefines the document type and the root element.!ELEMENTdefines additional elements; a!DOCTYPEdeclaration must contain at least one!ELEMENTwith the same name as the root.!ENTITYdefines entities like>— basically shortcuts for other data.#PCDATAis “parsable character data” (an XML-encoded string).- The
SYSTEMkeyword means “this URI/file is hosted by the current system” and can appear in both!DOCTYPEand!ENTITYdeclarations.
The crucial insight: the bracketed section of an inline DTD is concatenated with any external DTD the parser loads. That means we can append our own declarations onto the end of whatever !DOCTYPE the server expects — and those declarations win.
Basic file-read payload
Combine an inline !DOCTYPE with a SYSTEM entity pointing at file:// and the parser will happily read any file the webserver process can access:
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY read SYSTEM "file:///etc/passwd">
]>
<root>&read;</root>Note the added !DOCTYPE does not have to match the server’s expected document type — the definitions are concatenated, not validated for consistency. Don’t waste time crafting a “matching” !DOCTYPE; any garbage root name will do.
This is conceptually the same move as SQL or command injection, just aimed at the XML parser instead of the application code or shell.
RCE via expect:// in PHP
If the target is running PHP, and the PHP expect module is loaded, and XML inputs aren’t properly sanitized, then defining a SYSTEM entity whose URI uses the expect:// wrapper yields remote code execution:
<?xml version="1.0"?>
<!DOCTYPE root [<!ENTITY xxerce SYSTEM "expect://id">]>
<root>&xxerce;</root>The expect:// wrapper passes the rest of the URI to a shell via the expect extension, so the output of id is substituted into the parsed document. Don’t expect to run into this often — the combination of PHP + expect module + unsanitized XML parser is rare — but it’s a memorable escalation when it works. Related: local-file-inclusion-attacks for the more common PHP file-read paths.
Blind / out-of-band XXE
When the application doesn’t reflect the parsed entity back in the response, XXE goes “blind.” Exfiltration still works by nesting entities so the parser itself makes an outbound request to an attacker-controlled host — a workflow Burp Suite’s Collaborator is built for. Parameter entities (<!ENTITY % ...> inside the DTD) are the usual vehicle here.
Defenses
- Disable DTD processing entirely in the XML parser if the application doesn’t need it (
FEATURE_SECURE_PROCESSING,disallow-doctype-decl, or the parser-specific equivalent). - If DTDs are required, disable external entities and external parameter entities specifically.
- Prefer JSON or other non-XML formats for APIs where possible — removes the attack surface altogether.
- Patch XML libraries: many (libxml2, Java’s default JAXP, .NET ≥ 4.5.2) ship with safer defaults now, but older versions resolve external entities silently.
Sources
- WSTG — Testing for XML Injection
- XML external entity (XXE) injection — PortSwigger Web Security Academy
- 2016 — Exploitation: XML External Entity (XXE) Injection — Depth Security
- XMLWriter — Entity declaration in XML — dead link
Related: php, local-file-inclusion-attacks, burp-suite