XML External Entity (XXE): How a Parser Reads /etc/passwd For You
An XML parser that resolves external entities will fetch any file or URL an attacker names. Here is how XXE turns a document upload into local file disclosure and SSRF, and the parser settings that stop it cold.
Server-side requests & parsersOverview<!ENTITY x SYSTEM "file:///etc/passwd">
On this page
XML is more powerful than most developers realize. Beyond elements and attributes, the format lets a document define entities — and external entities can point at files on disk or URLs on the network. XML external entity (XXE) injection abuses a parser that resolves those entities: by declaring one that references a local file or an internal address, an attacker makes the server read sensitive files, reach internal services, and exfiltrate data. The root cause is depressingly simple — many XML libraries enable external-entity and DTD processing by default.
This post shows how a single crafted document turns a routine XML endpoint into a file-disclosure and SSRF primitive, and how to configure the parser so it never happens.
How it happens
Anywhere an application accepts XML — SOAP services, SAML, document uploads, sitemaps, SVG, config import — it must parse it. The vulnerability appears when the parser is left at its default settings, which often resolve DOCTYPE-declared entities. Consider a Java endpoint that reads an uploaded XML order:
This looks ordinary, and for well-formed orders it works fine. But DocumentBuilderFactory does not disable external entities by default. The parser will dereference any entity the document declares — including one that points outside the document entirely.
A concrete attack
Instead of a normal order, the attacker uploads an XML body with a DOCTYPE that defines an external entity pointing at a local file:
<?xmlversion="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
When the parser hits &xxe;, it resolves the entity by reading file:///etc/passwd and substitutes the file's contents into the element. Whatever the application does next with that element — echo it back, store it, include it in an error — now carries the file. The response body comes back containing:
The same technique swaps the target for an internal URL to turn the parser into a server-side request forger:
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">
]>
<foo>&xxe;</foo>
Now the server fetches cloud metadata on the attacker's behalf — credentials and all.
The attacker did not break the parser. They used a standard XML feature exactly as specified — the parser was simply configured to trust the document about which files and URLs to read.
What an attacker gains
XXE is a versatile primitive that opens several doors at once:
Local file disclosure — read /etc/passwd, source code, configuration files and application secrets directly off the server's disk.
Server-side request forgery — make the server hit internal-only services and cloud metadata endpoints (like 169.254.169.254) that are unreachable from outside.
Out-of-band exfiltration — with parameter entities and an attacker-hosted DTD, steal data even when the response shows nothing (blind XXE).
Denial of service — entity-expansion bombs can exhaust memory and CPU.
On many stacks, file disclosure alone hands over the keys: a leaked database password or cloud credential is enough to compromise far more than the XML endpoint.
The fix: disable DTDs and external entities in the parser
XXE is one of the rare classes with a clean, definitive fix: turn off the dangerous features at the parser. Since the application almost never needs DOCTYPEs or external entities, disable them outright. In Java:
With disallow-doctype-decl set, the earlier payload is rejected before any entity is resolved — there is nothing to dereference. The same principle applies on every platform:
Disable DTD processing and external-entity resolution in whichever XML library you use; most expose a single flag or a secure-processing mode.
Prefer simpler data formats like JSON where you control the contract — they have no equivalent of external entities.
Keep XML libraries patched, since defaults and bypasses change between versions.
Module: XXE. How it probes: Submits external-entity and parameter-entity payloads to every XML-accepting endpoint the crawl finds, including SOAP and WSDL surfaces. How it confirms: File-read evidence in the response, or an out-of-band entity fetch when the parser answers silently. A finding moves from Possible to Confirmed only after this check. Reported as CWE-611 · ATT&CK T1190 · SARIF 2.1.0.
How SelfSec proves this one
PossibleConfirmed
Module
XXE
How it probes
Submits external-entity and parameter-entity payloads to every XML-accepting endpoint the crawl finds, including SOAP and WSDL surfaces.
How it confirms
File-read evidence in the response, or an out-of-band entity fetch when the parser answers silently.
Reported asCWE-611 · ATT&CK T1190 · SARIF 2.1.0
A reflected response alone is not enough to promote a XML External Entity finding. The classical engine has to confirm the behavior before it reaches the report. See the detection engine
Reading about the class is one thing. SelfSec tests for it against an application you are authorized to assess, and only calls it real when independent evidence agrees.