代码检测时发现存在XXE问题,可通过自行改造的xml内容,请求存在XXE问题的接口,测试该漏洞。
比如使用如下xml,通过读取document中 testText这个Element,即可获取到test.json中的文件内容:
]>
&file;
采用的是org.dom4j.io.SAXReader读取xml, 修复方式如下:
try {
SAXReader reader = new SAXReader();
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
} catch (SAXException e) {
logger.error("SAXReader init error", e);
}
网上查到的一篇关于防御XXE漏洞的文章,写的挺全面,这里转来记录下。
以下内容转自:[Spoock's Blog | JAVA常见的XXE漏洞写法和防御](http://blog.spoock.com/2018/10/23/java-xxe/)
发表于 2018-10-23
貌似最近经常看到有Java项目爆出XXE的漏洞并且带有CVE,包括Spring-data-XMLBean XXE漏洞、JavaMelody组件XXE漏洞解析、Apache OFBiz漏洞。微信支付SDK的XXE漏洞。本质上xxe的漏洞都是因为对xml解析时允许引用外部实体,从而导致读取任意文件、探测内网端口、攻击内网网站、发起DoS拒绝服务攻击、执行系统命令等。
apache OFBiz中的XML解析是由UtilXml.java中readXmlDocument()
完成的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public static Document readXmlDocument(InputStream is, boolean validate, String docDescription) throws SAXException, ParserConfigurationException, java.io.IOException { //omit java code Document document = null; /* Standard JAXP (mostly), but doesn't seem to be doing XML Schema validation, so making sure that is on... */ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(validate); factory.setNamespaceAware(true); factory.setAttribute("http://xml.org/sax/features/validation", validate); factory.setAttribute("http://apache.org/xml/features/validation/schema", validate); factory.setFeature("http://xml.org/sax/features/external-general-entities", false); factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); factory.setXIncludeAware(false); factory.setExpandEntityReferences(false); |
我们就有理由相信XXE漏洞是由DocumentBuilderFactory
设置不当操作造成的,当然我们现在看到的是修改之后的版本;
JavaMelody
中是由PayloadNameRequestWrapper.java中的parseSoapMethodName
来解析XML。
1 2 3 4 5 6 7 8 9 10 11 12 |
private static String parseSoapMethodName(InputStream stream, String charEncoding) { try { // newInstance() et pas newFactory() pour java 1.5 (issue 367) final XMLInputFactory factory = XMLInputFactory.newInstance(); final XMLStreamReader xmlReader; if (charEncoding != null) { xmlReader = factory.createXMLStreamReader(stream, charEncoding); } else { xmlReader = factory.createXMLStreamReader(stream); } // omit java code } |
根据JavaMelody组件XXE漏洞解析的分析,是由于xmlReader
没有限制外部查询导致的XXE漏洞。
同样地,微信支付SDK的XXE漏洞和Spring-data-XMLBean XXE漏洞都是是使用了DocumentBuilderFactory
没有限制外部查询而导致XXE。
从这些例子中,可以发现在Java中其实存在着非常多的解析XML的库,同时由于在Java应用中会大量地使用到XML,因此就会出现使用不同的库对XML继续解析,而编写这些代码的研发人员并没有相关的安全背景,所以就导致了层出不穷地Java XXE漏洞。
我们测试的Payload很简单:
1 2 3 4 5 |
]> |
DocumentBuilderFactory
错误地修复方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = dbf.newDocumentBuilder(); String FEATURE = null; FEATURE = "http://javax.xml.XMLConstants/feature/secure-processing"; dbf.setFeature(FEATURE, true); FEATURE = "http://apache.org/xml/features/disallow-doctype-decl"; dbf.setFeature(FEATURE, true); FEATURE = "http://xml.org/sax/features/external-parameter-entities"; dbf.setFeature(FEATURE, false); FEATURE = "http://xml.org/sax/features/external-general-entities"; dbf.setFeature(FEATURE, false); FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd"; dbf.setFeature(FEATURE, false); dbf.setXIncludeAware(false); dbf.setExpandEntityReferences(false); // 读取xml文件内容 FileInputStream fis = new FileInputStream("path/to/xxexml"); InputSource is = new InputSource(fis); builder.parse(is); |
看似设置得很很全面,但是直接仍然会被攻击,原因就是在于DocumentBuilder builder = dbf.newDocumentBuilder();
这行代码需要在dbf.setFeature()
之后才能够生效;
正确地修复方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); String FEATURE = null; FEATURE = "http://javax.xml.XMLConstants/feature/secure-processing"; dbf.setFeature(FEATURE, true); FEATURE = "http://apache.org/xml/features/disallow-doctype-decl"; dbf.setFeature(FEATURE, true); FEATURE = "http://xml.org/sax/features/external-parameter-entities"; dbf.setFeature(FEATURE, false); FEATURE = "http://xml.org/sax/features/external-general-entities"; dbf.setFeature(FEATURE, false); FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd"; dbf.setFeature(FEATURE, false); dbf.setXIncludeAware(false); dbf.setExpandEntityReferences(false); DocumentBuilder builder = dbf.newDocumentBuilder(); // 读取xml文件内容 FileInputStream fis = new FileInputStream("path/to/xxexml"); InputSource is = new InputSource(fis); Document doc = builder.parse(is); |
注意DocumentBuilder builder = dbf.newDocumentBuilder();
在两种不同的位置的差异性。
这个库貌似使用得不是很多。SAXBuilder
如果使用默认配置就会触发XXE漏洞;如下
1 2 |
SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(InputSource); |
修复方法
方式1
1 2 |
SAXBuilder builder = new SAXBuilder(true); Document doc = builder.build(InputSource); |
方式2
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); |
同样地,在默认配置下就会存在XXE
漏洞。
1 2 3 |
SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser parser = spf.newSAXParser(); parser.parse(InputSource, (HandlerBase) null); |
修复方法
1 2 3 4 5 6 7 |
SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); spf.setFeature("http://xml.org/sax/features/external-general-entities", false); spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); SAXParser parser = spf.newSAXParser(); parser.parse(InputSource, (HandlerBase) null); |
在默认情况下会出现XXE
漏洞。
1 2 |
SAXReader saxReader = new SAXReader(); saxReader.read(InputSource); |
修复方法
1 2 3 4 5 6 |
SAXReader saxReader = new SAXReader(); 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); saxReader.read(InputSource); |
在默认情况下会出现XXE
漏洞
1 2 3 |
SAXTransformerFactory sf = (SAXTransformerFactory) SAXTransformerFactory.newInstance(); StreamSource source = new StreamSource(InputSource); sf.newTransformerHandler(source); |
但是有趣的是,在默认配置,虽然能够触发XXE漏洞,但是出现运行时会报错;如下所示:
但是只是存在Web的解析记录。
修复方法
1 2 3 4 5 |
SAXTransformerFactory sf = (SAXTransformerFactory) SAXTransformerFactory.newInstance(); sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); StreamSource source = new StreamSource(InputSource); sf.newTransformerHandler(source); |
通过跟踪源代码发现,XMLConstants.ACCESS_EXTERNAL_DTD
的内容是http://javax.xml.XMLConstants/property/accessExternalDTD
,XMLConstants.ACCESS_EXTERNAL_STYLESHEET
是http://javax.xml.XMLConstants/property/accessExternalStylesheet
在默认情况下也会出现XXE
漏洞。
1 2 3 |
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); StreamSource source = new StreamSource(ResourceUtils.getPoc1()); Schema schema = factory.newSchema(InputSource); |
和SAXTransformerFactory
虽然在运行时会报错,当时仍然能够触发XXE
。
同样也只存在Web的解析记录。
修复方法
1 2 3 4 5 |
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); StreamSource source = new StreamSource(InputSource); Schema schema = factory.newSchema(source); |
和SAXTransformerFactory
的修复原理一样就不作说明了。
使用默认的解析方法会存在XXE
问题。
1 2 3 |
TransformerFactory tf = TransformerFactory.newInstance(); StreamSource source = new StreamSource(InputSource); tf.newTransformer().transform(source, new DOMResult()); |
修复方法
1 2 3 4 5 |
TransformerFactory tf = TransformerFactory.newInstance(); tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); StreamSource source = new StreamSourceInputSource); tf.newTransformer().transform(source, new DOMResult()); |
使用默认的解析方法会存在XXE
问题
1 2 3 4 5 |
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); Schema schema = factory.newSchema(); Validator validator = schema.newValidator(); StreamSource source = new StreamSource(InputSource); validator.validate(source); |
修复方法
1 2 3 4 5 6 7 |
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); Schema schema = factory.newSchema(); Validator validator = schema.newValidator(); validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); validator.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); StreamSource source = new StreamSource(InputSource); validator.validate(source); |
使用默认的解析方法会存在XXE
问题
1 2 |
XMLReader reader = XMLReaderFactory.createXMLReader(); reader.parse(new InputSource(InputSource)); |
修复方法
1 2 3 4 5 6 |
XMLReader reader = XMLReaderFactory.createXMLReader(); reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); reader.setFeature("http://xml.org/sax/features/external-general-entities", false); reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false); reader.parse(new InputSource(InputSource)); |
使用默认的解析方法不会存在XXE
问题,这也是唯一一个使用默认的解析方法不会存在XXE
的一个库。
1 2 3 4 5 |
Class tClass = Some.class; JAXBContext context = JAXBContext.newInstance(tClass); Unmarshaller um = context.createUnmarshaller(); Object o = um.unmarshal(ResourceUtils.getPoc1()); tClass.cast(o); |
其实,通过对不同的XML解析库的修复方式可以发现,XXE
的防护值需要限制带外实体的注入就可以了,修复方式也简单,需要设置几个选项为发false
即可,可能少许的几个库可能还需要设置一些其他的配置,但是都是类似的。
总体来说修复方式都是通过设置feature的方式来防御XXE
。两种方法分别是:
1 2 3 4 |
"http://apache.org/xml/features/disallow-doctype-decl", true "http://apache.org/xml/features/nonvalidating/load-external-dtd", false "http://xml.org/sax/features/external-general-entities", false "http://xml.org/sax/features/external-parameter-entities", false |
配置如上。
另外一种是:
1 2 |
XMLConstants.ACCESS_EXTERNAL_DTD, "" XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "" |
本质上XXE
的问题就是一个配置不当的问题,即容易发现也容易防御,但是前提是需要知道有这个漏洞,这也是就是很多开发人员因为不知道XXE
最终写出了含有漏洞的代码。
以上。
Java服务XXE漏洞防御方法