Server-side
XXE: The Complete Practical Guide to XML External Entity Injection
What XXE is, how to exploit it (file read, blind OOB exfil, XXE-to-SSRF, file upload), and how to prevent it. With free labs to practice on.
Published

XXE is what happens when an application hands attacker-controlled XML to a parser that still honors a decades-old feature: external entities. Those let a document point at a local file or a URL, and a parser that resolves them becomes your file reader and your network client. The root cause is not a single bad line of code, it is a dangerous default left on in many XML libraries. The payoff is real: local file disclosure, SSRF into the internal network and cloud metadata, and occasionally remote code execution. Want to feel it rather than read about it? Sign up free and try it on a live target.
What is XXE?
The mental model in one sentence: the XML parser becomes the attacker's file and URL fetcher. You send XML that declares an entity pointing at something the server can reach, and the parser dutifully resolves it and often reflects the result back to you. The application never meant to expose that file or that internal service. The parser did it on your behalf.
A 60-second XML/DTD refresher
You only need a little XML to read every payload in this guide:
- An element is a tag like
<data>value</data>. - A DOCTYPE at the top of a document can define a Document Type Definition (DTD), the grammar for the XML, and it is also where entities are declared.
- An entity is a named placeholder the parser expands. An internal entity holds a literal value. An external entity uses the
SYSTEMkeyword and a URL, and that is the dangerous one. - A general entity is referenced as
&name;in the document body. A parameter entity is written%name;and used inside the DTD itself, which matters for the blind and out-of-band techniques.
The one dangerous ingredient is SYSTEM plus a URL, because that is what tells the parser to go fetch something external.
Where XXE hides
Look for anything that parses XML, which is more than you would expect:
- SOAP and legacy API request bodies.
- SAML authentication assertions.
- Document and image uploads:
.docx,.xlsx, and.svgare XML under the hood. - RSS, sitemap, and feed imports.
- Config, template, and data-file parsers.
- Any endpoint you can flip to
Content-Type: application/xml, even if it normally takes JSON.
If the server reads XML, it is a candidate.
In-band file read
The simplest XXE declares an entity pointing at a local file and places it where the response echoes back:
<?xml version="1.0"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<data>&xxe;</data>If &xxe; lands in a reflected field, the file's contents come back in the response. In-band file read covers choosing the right field, which files are worth reading, and why some files break the parse.
Blind XXE (out-of-band)
When nothing is reflected, you go out-of-band: host an external DTD on a server you control and use nested parameter entities to smuggle a file's contents into a DNS or HTTP callback, or into an error message. Blind XXE via external DTD walks the parameter-entity chain and the error-based variant for when outbound requests are blocked.
XXE via file upload
Image and document features are a rich XXE surface because the formats are XML. An SVG avatar or a tampered word/document.xml inside a .docx carries a DOCTYPE the server parses on upload or preview. XXE via file upload covers SVG, DOCX, and XLSX and how to get the result back.
XXE via content-type switch
An endpoint that expects JSON will sometimes also parse XML. Flip the Content-Type to application/xml, rewrite the body as an XML document with a malicious DOCTYPE, and the hidden XML parser does the rest. XXE via content-type switch shows how to spot and rebuild these.
XInclude attacks
When you only control part of a document and cannot add a DOCTYPE, XInclude is the way in. A single <xi:include> element pulls in a file without any entity declaration at all. XInclude attacks covers the namespace, the text-parse trick, and where XInclude is enabled.
SSRF via XXE
Point SYSTEM at an http:// URL instead of a file:// path and the parser makes a network request from inside the perimeter, reaching internal services and cloud metadata. That is SSRF delivered through the XML parser. SSRF via XXE covers the technique, and from there the full SSRF playbook applies.
A worked example
Say an API accepts an XML body and echoes a name field back. You probe with a harmless entity and see it expand, so the parser resolves entities. You try file:///etc/passwd in the name field and the file comes back in the response: in-band file read confirmed.
Now you want a file with XML-special characters that breaks the in-band read, like an app config. You switch to out-of-band: the request body references an external DTD on your server, and that DTD defines a parameter entity reading the config file and wraps it into a URL that your listener receives. The file's contents arrive in your logs as a query string, even though the response showed you nothing. That is the loop: confirm the parser resolves entities, read what you can in-band, then move to OOB for what you cannot.
Prevention
- Disable DTD processing entirely. This is the one fix that kills every XXE variant, because external entities can only be declared in a DTD.
- If you cannot disable DTDs, disable external entities and external DTD loading on the parser.
- Set the right flag per parser. The exact call differs across Java, .NET, PHP, and Python, and preventing XXE lists them.
- Do not rely on filtering
<!ENTITY>orSYSTEMout of the input. Encodings and XInclude route around a blacklist. - Prefer safer formats and libraries. JSON avoids the problem, and modern XML libraries that are safe by default remove the footgun.
Where to go next
Work through the cluster in order, or jump to what you need. Start with XML entities basics if the syntax is new, then in-band file read and blind XXE. When you want every payload in one place, the XXE cheat sheet has them. And when you are ready to break something real, sign up free and start hacking.