Skip to main content
XML lets a document define its own custom placeholders, called entities, and point one of them at an external resource instead of a fixed string. If an application parses XML you control and its parser has external entity resolution turned on, you can define an entity that points at a local file, an internal URL, or a remote Document Type Definition (DTD), and the parser will fetch it for you.

In-Band XXE

The file/URL content gets reflected directly into the application’s response. Easiest to confirm and exploit.

Blind / Out-of-Band XXE

No output comes back. Confirm and exfiltrate via a Document Type Definition (DTD) hosted on your own server and a callback you control instead.

How It Works

The core trick: declare an entity in a DOCTYPE, then reference it anywhere in the document body. The parser resolves the entity before handing the document to the application, so whatever you pointed it at gets included in as if it were always part of the XML.
An endpoint that only accepts JSON isn’t necessarily safe from this, if the same data could plausibly be submitted as XML (many frameworks accept both via content negotiation), try switching Content-Type to application/xml and converting your JSON body to an equivalent XML structure. convertjson.com does the conversion quickly if you want a starting XML shape to work from.
Beyond local file read, the SYSTEM identifier accepts other protocols/wrappers, each useful for a different goal.
php://filter matters more than it looks: a file containing raw binary data, null bytes, or characters that aren’t valid XML/UTF-8 will break the parser or truncate the output if read directly.

Advanced: CDATA Wrapping

The content of the reflected file is sometimes still escaped or otherwise corrupted by the application after the XML parser has processed it. By embedding the result in a CDATA section, any subsequent processing is instructed to treat it as a raw literal block instead, which often preserves content that would otherwise be corrupted or truncated.
This only works with regular (general) entities, which can’t reference each other across an external file the way parameter entities can. To combine CDATA wrapping with an externally hosted DTD (needed for the blind techniques below), use parameter entities (the % prefix) instead, which are evaluated inside the DTD itself rather than the document body.

Error-Based XXE

When the application reflects parser errors but not actual content, deliberately triggering an error that includes the file’s content as part of the (invalid) entity reference leaks it through the error message instead.
The malformed path (file content appended to a path that can’t possibly resolve) forces the parser to throw an error, and that error message includes the file content you injected into it.

Out-of-Band (Blind) Exfiltration

When there’s no visible output and no useful error either, the last option is making the target server reach out to you directly, carrying the file’s content along in the request.
1

Write the external DTD file

2

Serve the DTD file

3

Stand up a listener to catch the exfiltrated data

A second listener (different port) logs whatever comes back in the content parameter.
4

Submit the payload referencing your hosted DTD


Automated Exfiltration with XXEinjector

For repeated or multi-file extraction, automating the OOB chain above saves a lot of manual DTD-writing.
Capture the vulnerable request from Burp, save it to a file, and mark the injection point with the literal string XXEINJECT in place of the XML body.

SVG-Based XXE

SVG is XML under the hood, so any upload feature accepting SVG images is a potential XXE vector, worth testing alongside standard file-upload bypasses (see File Upload Attacks).
for a base64 encoded payload use this in the <!DOCTYPE svg tag

Denial of Service: Billion Laughs

Each entity references the previous one ten times over, so by the tenth layer, a few dozen bytes of definitions expand into billions of repetitions in memory, exhausting the server before any content is even returned. Not useful for data extraction, but worth knowing as a DoS vector if availability testing is in scope.

When DOCTYPE Is Blocked

Some applications block or strip <!DOCTYPE from the input before parsing, but still parse the resulting XML with an engine that supports XInclude, which achieves a similar file-read result without ever declaring a DOCTYPE at all. Useful specifically when your input lands inside an existing XML document (so you can’t control the DOCTYPE) rather than being the entire document yourself.
/etc/passwd, /etc/hosts, and /proc/self/environ are the standard confirmation targets, but once XXE is confirmed, also worth checking: ~/.ssh/id_rsa, application config files (.env, web.config, wp-config.php) for credentials.

Quick Reference Tools

Hackviser: XXE

Reference used for this page.

HackTricks: XXE

Deeper dive into additional bypasses and content-based evasion.

convertjson.com

Quick JSON-to-XML conversion for testing XML on JSON-only endpoints.

XXEinjector

Automates blind/OOB XXE exfiltration from a captured request.

OXML_XXE

Injects XXE payloads into Office/OpenXML document formats (docx, xlsx).
XXE routinely escalates into local file read, SSRF against internal infrastructure, or full RCE via expect://. Confirm scope explicitly before moving past a basic /etc/passwd read into exfiltration or internal network probing.