AI智能
改变未来

Java审计之XXE


Java审计之XXE

写在前面

因为已经很久没有接触到XXE了,所以借此机会打算温习一遍XXE再来讲一下在Java中去审计XXE的一个思路和流程。

About XXE

基础知识

XXE(XML External Entity Injection) 全称为 XML 外部实体注入,与其他的注入漏洞类似,只不过XXE注入的是XML外部实体。

那么什么是XML?

XML是一种非常流行的标记语言,在1990年代后期首次标准化,并被无数的软件项目所采用。

XML用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。

XML被设计为传输和存储数据,其焦点是数据的内容,其把数据从HTML分离,是独立于软件和硬件的信息传输工具。它用于配置文件,文档格式(如OOXML,ODF,PDF,RSS,…),图像格式(SVG,EXIF标题)和网络协议(WebDAV,CalDAV,XMLRPC,SOAP,XMPP,SAML, XACML,…)。比如我们常见的web.xml,applicationContext.xml,Mybatis中用来写SQL的Mapper.xml等等

那什么是外部实体?

说到外部实体首先要说一下DTD(Document Type Definition) 即文档类型定义,用来为XML文档定义语义约束。可以嵌入在XML文档中(内部声明),也可以独立的放在一个文件中(外部引用)。在DTD中有一个实体 (ENTITY)概念,如果需要在XML文档种频繁的使用某一条数据,我们可以预先给这个数据起一个别名,也就是一个ENTITY,之后再在文档种调用它。

DTD实体的引用有内部声明实体和外部引用实体的区别。按类型分则分为:内置实体,字符实体,通用实体,参数实体。

而在XXE中最常见的就是通用实体和参数实体

内部实体

简单的内置实体格式如下:

<!DOCTYPE  文件名 [<!ENTITY  实体名 "实体内容">]>

内部实体可以通过如下格式声明:

<?xml version="1.0" encoding="utf-8"?><!DOCTYPE aaa[<!ENTITY name "Zh1z3ven">]><root><name>&name;</name>

上面提到的都是内部实体,而外部的DTD实体则需要通过URL来远程调用一个.dtd文件,也或者可以利用file协议引用一个本地的文件;

外部实体

下面以通用实体来举例子

通用实体:在DTD 中定义,在XML中以

&实体名;

引用

1.利用file协议

<?xml version="1.0" encoding="utf-8"?><!DOCTYPE aaa [<!ENTITY name SYSTEM "file:///c:/windows/win.ini" >]><name>&name;</name>

2.利用http协议

<?xml version="1.0" encoding="utf-8"?><!DOCTYPE aaa [<!ENTITY name SYSTEM "http://127.0.0.1/123.txt" >]><name>&name;</name>

参数实体:使用

% 实体名

在 DTD 中定义,并且只能在 DTD 中使用

%实体名;

引用

<?xml version="1.0" encoding="utf-8"?><!DOCTYPE a [<!ENTITY % name SYSTEM "http://127.0.0.1/test.dtd">%name;]><d>&test;</d>

Test.dtd

<!ENTITY test SYSTEM "http://127.0.0.1/123.txt" >

当然不仅是file和http,也支持其他的协议,不过不同的语言支持的协议不一样,可以参考下图。

XXE相关更多的利用姿势也可以参考下面这篇文章

注意:libxml2.9.1及以后,默认不解析外部实体;在java中netdoc可以做到类似于file的功能

Java中的XXE

其实不仅是Java,其他语言依旧是一样的思路,XML解析一般在导入配置、数据传输接口等场景可能会用到,涉及到XML文件处理的场景可查看XML解析器是否禁用外部实体,从而判断是否存在XXE。审计时首先需要定位危险函数,在Java中有如下的几个函数支持XML解析

javax.xml.parsers.DocumentBuilderjavax.xml.parsers.DocumentBuilderFactoryjavax.xml.parsers.SAXParserjavax.xml.parsers.SAXParserFactoryjavax.xml.transform.TransformerFactoryjavax.xml.validation.Validatorjavax.xml.validation.SchemaFactoryjavax.xml.transform.sax.SAXTransformerFactoryjavax.xml.transform.sax.SAXSourceorg.xml.sax.XMLReaderorg.xml.sax.helpers.XMLReaderFactoryorg.dom4j.io.SAXReaderorg.jdom.input.SAXBuilderorg.jdom2.input.SAXBuilderjavax.xml.bind.Unmarshallerjavax.xml.xpath.XpathExpressionjavax.xml.stream.XMLStreamReaderorg.apache.commons.digester3.Digester

之后就是判断是否参数可控且没有被禁用外部实体,例如如下代码会禁用外部实体

DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();dbf.setExpandEntityReferences(false);.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true);.setFeature("http://xml.org/sax/features/external-general-entities",false).setFeature("http://xml.org/sax/features/external-parameter-entities",false);

下面以https://github.com/JoyChou93/java-sec-code/项目为例去做一个简单分析

DocumentBuilder

这是JDK自带的类,以此产生的XXE是存在回显的

WebUtils

import javax.servlet.http.Cookie;import javax.servlet.http.HttpServletRequest;import java.io.File;import java.io.IOException;import java.io.InputStream;import com.google.common.base.Preconditions;import org.springframework.web.util.HtmlUtils;public class WebUtils {// Get request body.public static String getRequestBody(HttpServletRequest request) throws IOException {InputStream in = request.getInputStream();return convertStreamToString(in);}// https://stackoverflow.com/questions/309424/how-do-i-read-convert-an-inputstream-into-a-string-in-javapublic static String convertStreamToString(java.io.InputStream is) {java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\\\A");return s.hasNext() ? s.next() : "";}public static String getCookieValueByName(HttpServletRequest request, String cookieName) {Cookie cookie = org.springframework.web.util.WebUtils.getCookie(request, cookieName);return cookie == null ? null : cookie.getValue();}public static String json2Jsonp(String callback, String jsonStr) {return HtmlUtils.htmlEscape(callback) + "(" + jsonStr + ")";}public static String getFileExtension(String fullName) {Preconditions.checkNotNull(fullName);String fileName = (new File(fullName)).getName();int dotIndex = fileName.lastIndexOf(\'.\');return dotIndex == -1 ? "" : fileName.substring(dotIndex + 1);}public static String getNameWithoutExtension(String file) {Preconditions.checkNotNull(file);String fileName = (new File(file)).getName();int dotIndex = fileName.lastIndexOf(\'.\');return dotIndex == -1 ? fileName : fileName.substring(0, dotIndex);}}

漏洞代码

public String DocumentBuilderVuln01(HttpServletRequest request) {try {String body = WebUtils.getRequestBody(request);logger.info(body);DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();DocumentBuilder db = dbf.newDocumentBuilder();StringReader sr = new StringReader(body);InputSource is = new InputSource(sr);Document document = db.parse(is);  // parse xml// 遍历xml节点name和valueStringBuilder buf = new StringBuilder();NodeList rootNodeList = document.getChildNodes();for (int i = 0; i < rootNodeList.getLength(); i++) {Node rootNode = rootNodeList.item(i);NodeList child = rootNode.getChildNodes();for (int j = 0; j < child.getLength(); j++) {Node node = child.item(j);buf.append(String.format("%s: %s\\n", node.getNodeName(), node.getTextContent()));}}sr.close();return buf.toString();} catch (Exception e) {logger.error(e.toString());return EXCEPT;}

SAXReader

SAXReader是第三方的库,该类是无回显的

public String SAXReaderVuln(HttpServletRequest request) {try {String body = WebUtils.getRequestBody(request);logger.info(body);SAXReader reader = new SAXReader();// org.dom4j.Document documentreader.read(new InputSource(new StringReader(body))); // cause xxe} catch (Exception e) {logger.error(e.toString());return EXCEPT;}

SAXBuilder

public String SAXBuilderVuln(HttpServletRequest request) {try {String body = WebUtils.getRequestBody(request);logger.info(body);SAXBuilder builder = new SAXBuilder();// org.jdom2.Document documentbuilder.build(new InputSource(new StringReader(body)));  // cause xxereturn "SAXBuilder xxe vuln code";} catch (Exception e) {logger.error(e.toString());return EXCEPT;

SAXParserFactory

该类也是JDK内置的类,但他不可回显内容,可借助dnslog平台

public String SAXParserVuln(HttpServletRequest request) {try {String body = WebUtils.getRequestBody(request);logger.info(body);SAXParserFactory spf = SAXParserFactory.newInstance();SAXParser parser = spf.newSAXParser();parser.parse(new InputSource(new StringReader(body)), new DefaultHandler());  // parse xmlreturn "SAXParser xxe vuln code";} catch (Exception e) {logger.error(e.toString());return EXCEPT;}}

XMLReaderFactory

public String xmlReaderVuln(HttpServletRequest request) {try {String body = WebUtils.getRequestBody(request);logger.info(body);XMLReader xmlReader = XMLReaderFactory.createXMLReader();xmlReader.parse(new InputSource(new StringReader(body)));  // parse xmlreturn "xmlReader xxe vuln code";} catch (Exception e) {logger.error(e.toString());return EXCEPT;}

Digester

public String DigesterVuln(HttpServletRequest request) {try {String body = WebUtils.getRequestBody(request);logger.info(body);Digester digester = new Digester();digester.parse(new StringReader(body));  // parse xml} catch (Exception e) {logger.error(e.toString());return EXCEPT;}return "Digester xxe vuln code";

XMLReader

public String XMLReaderVuln(HttpServletRequest request) {try {String body = WebUtils.getRequestBody(request);logger.info(body);SAXParserFactory spf = SAXParserFactory.newInstance();SAXParser saxParser = spf.newSAXParser();XMLReader xmlReader = saxParser.getXMLReader();xmlReader.parse(new InputSource(new StringReader(body)));} catch (Exception e) {logger.error(e.toString());return EXCEPT;}return "XMLReader xxe vuln code";}

小结

简单水一片文章,作为记录,后续遇到会没有的会继续补充。同时关于盲注外带XXE执行结果的利用姿势也可以参考先知的那篇文章

Reference

https://www.cnblogs.com/nice0e3/p/13746076.html

https://xz.aliyun.com/t/3357#toc-21

https://github.com/JoyChou93/java-sec-code

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Java审计之XXE