Java代码审计之XXE

  • A+

关于XEE

漏洞原理

  Java中XML内容的解析主要依赖于其丰富的库。XML 的解析过程中若允许加载外部实体,若不添加安全的XML解析配置,则XML文档将包含来自外部 URI 的数据,将会导致XEE。

  Java中的XXE支持以下协议:http,https,file,ftp,mailto,jar,netdoc。

  可以利用file协议读取文件,利用http协议探测内网,没有回显时可组合利用file协议和ftp协议来读取文件。如果存在报错的情况下还可以尝试使用报错XXE进行敏感信息的获取。甚至是尝试递归调用造成拒绝服务攻击。

XML解析涉及的业务功能点

  • WebServices接口
  • RESTful接口
  • Excel文件解析
  • Soap协议
  • ......

  凡是可以解析XML数据的地方都可能存在XXE。

挖掘思路

  若相关解析接口的解析内容用户可控,需检查使用XML解析器时是否设置了相关的安全属性,禁用DTDs或者禁止使用外部实体。还有是否使用了不安全的漏洞组件。

常见的XML解析库

  • javax.xml.stream.XMLStreamReader
  • javax.xml.parsers.DocumentBuilderFactory
  • org.dom4j.io.SAXReader
  • org.xml.sax.helpers.XMLReaderFactory
  • javax.xml.parsers.SAXParser
  • javax.xml.parsers.DocumentBuilder
  • org.jdom.input.SAXBuilder
  • org.dom4j.DocumentHelper
  • org.jdom.output.XMLOutputter
  • ......

常见漏洞组件

  • xlsx-streamer.jar-2.0.0及以下版本
  • poi-ooxml-3.10-FINAL.及以下版本(CVE-2014-3529)
  • poi-ooxml-3.13-FINAL.及以下版本(CVE-2016-5000)
  • poi-ooxml-3.15.jar 及以下版本(CVE-2017-5644(拒绝服务))
  • ......

漏洞示例

  以解析如下内容为例,根据常用的XML解析器,列举几个未做任何措施的case:

图片.png

  (1)JAXP:

java
import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/*
*JAXP DOM解析
*/
public class Demo1 {
public static void main(String[] args) throws Exception {
//调用DocumentBuilderFactory.newInstance()方法得到创建DOM解析器的工厂
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
//调用工厂对象的newDocumentBuilder方法得到DOM解析器对象
DocumentBuilder builder = builderFactory.newDocumentBuilder();
//解析指定的文件
File file =new File("./src/test.xml");
Document doc = builder.parse(file);
//获取根节点的元素对象
Element root = doc.getDocumentElement();
//遍历子节点
ListNodes(root);
}
}

  (2)JDOM:

java
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
/*
* Jdom解析XML
*/
public class Demo2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
//1.创建一个SAXBuilder的对象
SAXBuilder builder =new SAXBuilder();
try {
// 2.创建一个输入流,将xml文件加载到输入流中
InputStream in = new FileInputStream("./src/test.xml");
// 3.通过saxBuilder的build方法,将输入流加载到saxBuilder中
Document document = builder.build(in);
// 4.通过document对象获取xml文件的根节点
Element rootElement = document.getRootElement();
// 5.获取根节点下的子节点的List集合
List<Element> List = rootElement.getChildren();
for (Element test : List) {
......
......
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JDOMException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

  (3)Dom4j:

java
import java.io.File;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.xml.sax.SAXException;
public class Demo3 {
public static void main(String[] args) throws SAXException {
try {
SAXReader reader = new SAXReader();
Document doc = reader.read(new File("./src/test.xml"));
System.out.println(doc.asXML());
} catch (DocumentException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}

  (4)SAX:

java
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
public class Demo4 {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
// 获取SAX解析工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
// 通过工厂获取SAX解析器
SAXParser parser = factory.newSAXParser();
// 获取XML阅读器
XMLReader reader = parser.getXMLReader();
// 关联事件解析器
reader.setContentHandler(new MyContentHandler());
// 解析XML文件
reader.parse("./src/test.xml");
}
}

图片.png
  上述case由于没有设置相关的安全属性,禁用DTDs或者禁止使用外部实体,所以可以通过file协议读取/etc/passwd内容,具体效果:

相关案例

  以CVE-2014-3529为例:

  xlsx文件其实是一个压缩包,其中包含一些xml文件:

图片.png

  在进行xlsx文件解析时,主要是通过ZipContentTypeManager进行解析的。其继承了ContentTypeManager,实例化方法如下:

```java
public class ZipContentTypeManager extends ContentTypeManager
{
private static POILogger logger = POILogFactory.getLogger(ZipContentTypeManager.class);

public ZipContentTypeManager(InputStream in, OPCPackage pkg)
throws InvalidFormatException
{
super(in, pkg);
}
......
}
```

  因为继承了ContentTypeManager类,所以查看其实例化方法:

```java
public abstract class ContentTypeManager
{
public static final String CONTENT_TYPES_PART_NAME = "[Content_Types].xml";
public static final String TYPES_NAMESPACE_URI = "http://schemas.openxmlformats.org/package/2006/content-types";
private static final String TYPES_TAG_NAME = "Types";
private static final String DEFAULT_TAG_NAME = "Default";
private static final String EXTENSION_ATTRIBUTE_NAME = "Extension";
private static final String CONTENT_TYPE_ATTRIBUTE_NAME = "ContentType";
private static final String OVERRIDE_TAG_NAME = "Override";
private static final String PART_NAME_ATTRIBUTE_NAME = "PartName";
protected OPCPackage container;
private TreeMap defaultContentType;
private TreeMap overrideContentType;

public ContentTypeManager(InputStream in, OPCPackage pkg)
throws InvalidFormatException
{
this.container = pkg;
this.defaultContentType = new TreeMap();
if (in != null) {
try
{
parseContentTypesFile(in);
}
catch (InvalidFormatException e)
{
throw new InvalidFormatException("Can't read content types part !");
}
}
}
......
}
```

  其中调用了parseContentTypesFile方法,将相关xlsx文件的InputStream对象传入进行解析:

```java
......
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.dom4j.io.SAXReader;

private void parseContentTypesFile(InputStream in)
throws InvalidFormatException
{
try
{
SAXReader xmlReader = new SAXReader();
Document xmlContentTypetDoc = xmlReader.read(in);

  List defaultTypes = xmlContentTypetDoc.getRootElement().elements("Default");

  Iterator elementIteratorDefault = defaultTypes.iterator();
  while (elementIteratorDefault.hasNext())
  {
    Element element = (Element)elementIteratorDefault.next();
    String extension = element.attribute("Extension").getValue();

    String contentType = element.attribute("ContentType").getValue();

    addDefaultContentType(extension, contentType);
  }
  List overrideTypes = xmlContentTypetDoc.getRootElement().elements("Override");

  Iterator elementIteratorOverride = overrideTypes.iterator();
  while (elementIteratorOverride.hasNext())
  {
    Element element = (Element)elementIteratorOverride.next();
    URI uri = new URI(element.attribute("PartName").getValue());

    PackagePartName partName = PackagingURIHelper.createPartName(uri);

    String contentType = element.attribute("ContentType").getValue();

    addOverrideContentType(partName, contentType);
  }
}
catch (URISyntaxException urie)
{
  throw new InvalidFormatException(urie.getMessage());
}
catch (DocumentException e)
{
  throw new InvalidFormatException(e.getMessage());
}

}
```

  可以看到使用SAXReader 来解析xml文档,由于没有设置相关的安全属性,存在XXE风险。

修复建议

  关于XML Reader修复可参考:

https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.md

  以DocumentBuilderFactory举例:

  禁止DTDS解析:

java
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

  使用安全处理模式避免ssrf和文件读取:

java
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);

  不包括外部通用实体:

java
dbf.setFeature("http://xml.org/sax/features/external- general-entities", false);

  不包括外部实体或外部DTD子集:

java
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

  忽略外部DTD:

java
factory.setFeature("http://apache.org/xml/features/nonvalidati ng/load-external-dtd", false);

  关于相关的漏洞漏洞组件:

  • 如果系统使用了低版本POI库,导致处理Excel文件时存在XXE和SSRF漏洞,升级该库,必须大于3.14版本
  • 如果系统使用了Excel Streaming Reader <= 2.0.0 XXE 中,升级该库,必须大于2.0.0版本

相关推荐: 如何优雅的在Linux上使用Powershell]

译文声明 本文是翻译文章,文章原作者 TJ Null,文章来源:https://www.offensive-security.com 原文地址:https://www.offensive-security.com/offsec/kali-linux-power…