前段时间在搞应急,从一个啥都不会的小白学了一些XXE的简单poc构造和防御手段,网上攻击的poc很多,就不多说了,防御方法五花八门,而且有些防御根本就是无效的!!写篇文章分享一下,如何用Java彻底防御XXE。
一、简介 Java提供了一些解析xml的API,也有第三方写的,如果攻击者构造恶意的xml让Java应用去解析,可以造成任意文件读。在Java里是无法代码执行的,但在其他语言里可能会。
最全的链接是这个 https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#JAXB_Unmarshaller (但经过测试,大部分写的不错,漏了一些细节,也就有了这篇文章)
Github地址 二、各种写法 1、DocumentBuilderFactory 官方patch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); String FEATURE = null ; 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(); builder.parse(new ByteArrayInputStream(s.getBytes()));
可以使用本文最后的poc1来验证。如果出现了网络上的卡顿、或者文件NotFound,就说明仍然可以被攻击,如果出现了抛出异常,就是成功防御。
另一种patch方式:
1 2 3 4 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); String FEATURE = null ; FEATURE = "http://javax.xml.XMLConstants/feature/secure-processing" ; dbf.setFeature(FEATURE, true );
使用这个Feature也是比较保险的方式,后面会讲我是如何找到这个Feature的,也是很巧合。
民间的垃圾patch
1 dbf.setExpandEntityReferences(false );
只写这句没用的,照样可以被日。
2、SAXBuilder 官方patch
1 2 3 4 5 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 ); Document doc = builder.build(new File(fileName));
这个没什么好讲的,用poc1验证即可,应该也是第一句最重要,后面两句用处不是很大。
3、SAXParserFactory 官方patch
1 2 3 4 5 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 ); SAXParser parser = spf.newSAXParser(); parser.parse(ResourceUtils.getPoc1(), (HandlerBase) null );
使用poc1来验证。
1 2 spf.setFeature("http://xml.org/sax/features/external-parameter-entities" , false ); spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd" , false );
民间流行的垃圾patch
1 2 3 4 5 SAXParserFactory parserFactory = SAXParserFactory.newInstance(); parserFactory.setValidating(false ); parserFactory.setXIncludeAware(false ); parserFactory.setNamespaceAware(false ); SAXParser parser = parserFactory.newSAXParser();
4、SAXReader 官方patch
1 2 3 4 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 );
使用poc1来验证。
官方patch
1 2 3 4 SAXTransformerFactory sf = SAXTransformerFactory.newInstance(); sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "" ); sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "" ); sf.newXMLFilter(Source);
使用poc1来验证。
6、SchemaFactory 官方patch
1 2 3 4 SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema" ); factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "" ); factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "" ); Schema schema = factory.newSchema(Source);
使用poc1来验证。
官方patch
1 2 3 TransformerFactory tf = TransformerFactory.newInstance(); tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "" ); tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "" );
使用poc1来验证。
8、Validator 官方patch
1 2 3 Validator validator = schema.newValidator(); validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "" ); validator.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "" );
使用poc1来验证。
官方patch
1 2 xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false ); xmlInputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false );
使用poc1来验证。
10、XMLReader 官方patch
1 2 3 4 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 );
使用poc1来验证。
11、unmarshaller的操作 Unmarshaller的一般需要提供一个class,对其中的对象进行自动的编码和解码,所以没有通用的poc,需要和其他类进行配合。在实际使用场景中,为了防止自己写错,可以先marshall一份String,再将这个String unmarshall回去。 例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import javax.xml.bind.annotation.XmlElement;import javax.xml.bind.annotation.XmlRootElement;@XmlRootElement public class Person { @XmlElement(name = "age") public int age; @XmlElement(name = "person") public String name; } <person> <age>10 </age> <person>Who</person> </person>
这个类和xmlString可以相互转化,那么poc构造时候,就要使用第二种,见末尾。
一般 unmarshall 是从上文的类里分出来的,只要工厂类设置好feature,这里就不会出事。
12、额外JAXBContext,自带防御。 1 2 3 4 5 JAXBContext context = JAXBContext.newInstance(tClass); Unmarshaller um = context.createUnmarshaller(); StringReader sr = new StringReader(poc); Object o = um.unmarshal(sr); tClass.cast(o);
这个虽然没有设置过feature,但也是无法被日的,就是因为JAXBContext里在创建unmarshaller的时候,某句话,设置了secure-processing的Feature。 使用poc2来验证。
附:poc1
1 2 3 4 5 6 7 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xdsec [ <!ELEMENT methodname ANY > <!ENTITY xxe SYSTEM "http://111.222.111.222:12345/test.txt" >]> <methodcall> <methodname>&xxe;</methodname> </methodcall>
附:poc2
1 2 3 4 5 6 7 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE ANY[ <!ENTITY poc SYSTEM "http://111.222.111.222:12345/test.txt">]> <person> <age>10</age> <name>&poc;</name> </person>
13、dom4j低版本自带的XXE(2018.11更新) 处理线上风险时,不小心看到了这个 API,我写的扫描器扫出来的,准备去官网报个洞,发现被人报了。
https://github.com/dom4j/dom4j/issues/28
很简单,DocumentHelper.parseText() 里直接用了 SaxReader,没有做保护。 方案有两个:一是升级版本,升级到2.1.1即可;二是重构代码,不要用这个 API。
注意,从1.x到2.x的过程,官方改名了! 1.x 是 dom4j:dom4j ;2.x是 org.dom4j:dom4j
三、Tips 1、如果是一些工厂类的话,一定要先设置Feature,再去生成数据。例如下面两种写法,后者是无效的。
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 29 30 31 32 33 34 35 36 37 public void safe () throws ParserConfigurationException, IOException, SAXException { 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(); builder.parse(ResourceUtils.getPoc1()); } public void unsafe () throws ParserConfigurationException, IOException, SAXException { 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 ); builder.parse(ResourceUtils.getPoc1()); }
2、觉得防御好后,记得测一下,不然很容易被网上的一些垃圾patch给坑了。
四、相关链接 https://blog.spoock.com/2018/10/23/java-xxe/ 后来的另一篇介绍xxe的,比我这篇更清晰。