Phpcms v9.6.0 会员注册getshell漏洞

彩虹网

声明:

1.漏洞是远程文件下载保存在本地导致的getshell

2.因为我个人习惯把关键代码贴到notepad++分析,为了方便阅读,提到的行数都是notepad++里的,所以和源码行数不匹配很正常

3.本篇漏洞分析借鉴了Phpcms V9.6.0任意文件写入getshell漏洞分析的审计思路

4.这篇文章是学习代码审计总结笔记,如果有错误和改正之处还望不吝赐教(强烈欢迎批评指正)

5.如转载(或其它),请标注作者,不胜感激

漏洞涉及版本:phpcms v9.6.0(可能并不只涉及这一单一版本,我没试╮(╯_╰)╭)

漏洞触发涉及文件:

Register函数:phpcms/phpcms/modules/member/index.php

Get函数:phpcms/caches/caches_model/caches_data/member_input.class.php

member_input类:phpcms/caches/caches_model/caches_data/content_form.class.php

Getcache函数:phpcms/phpcms/libs/functions/global.func.php

Editor函数:phpcms/caches/caches_model/caches_data/member_input.class.php

Attachment类:phpcms/phpcms/libs/classes/attachment.class.php

缓存配置文件:phpcms\caches\caches_model\caches_data

整体思路:

这个漏洞发生在会员注册页面,是因为远程下载文件保存在本地导致的getshell,一般都会进行严格的过滤,而这里过滤不严或代码的一些问题,导致可以绕过对于后缀名的限制和对保存文件的后缀名可控。

漏洞分析:

我们先看会员注册页面的register函数,直接看如下代码:

Phpcms v9.6.0 会员注册getshell漏洞

可以看到在13行以post方式接收了info,其中new_html_special_chars函数代码如下:

Phpcms v9.6.0 会员注册getshell漏洞

这个函数只是对字符串做了一些转换,并不造成什么影响,回到开始那段代码继续往下走,可以看见info数组被传递进了get函数,跟进这个函数看看:

Phpcms v9.6.0 会员注册getshell漏洞

直接从第52行代码开始看,这里用到了$this->fields,而它的定义在member_input类的构造函数里面,代码如下:

Phpcms v9.6.0 会员注册getshell漏洞

在第7行,可以看见是对拼接后的字符串传递进了getcache函数,跟进这个函数:

Phpcms v9.6.0 会员注册getshell漏洞

这个函数用于读取缓存,而我们知道传递进来的字符串是'model_field_'.$modelid,其中$modelid是我们传递进来的参数是可控的,我们看看符合条件的文件有哪些:

Phpcms v9.6.0 会员注册getshell漏洞

总共有5个文件,先回到get函数的52行,可以看到$this->fields

'formtype'

我们先假设传递进来的$modelid值是1,那么我们读取缓存配置的文件是model_field_1.cache.php,其中关于formtype的配置是:

所以get函数的53行代码后段部分等同于$value = $this->editor($field, $value);也就是调用了editor函数,这里需要强调的是,触发漏洞需要调用editor函数,而我们读取缓存配置的文件有5个,然而其中符合'formtype' => 'editor',的文件只有4个,model_field_10.cache.php不在其中,所以我们传递进来的$modelid值就必须是1,2,3,11中的一个。然后我们现在继续跟进editor函数,代码如下:

Phpcms v9.6.0 会员注册getshell漏洞

其中$this->site_config 的定义在content_input类的构造函数,就不贴全代码了,贴关键的一行代码:

所以读取的缓存配置文件是phpcms/caches/caches_commons/caches_data/sitelist.cache.php,那么代码$this->site_config

'setting'

读取的setting的配置是:

Phpcms v9.6.0 会员注册getshell漏洞

好了,然后我们接着往下,可以看到在editor函数的9行代码调用了download函数,这个函数在attachment类下,这里需要格外注意的是,可以看见传递进去的第一个参数是规定为content,那么我们利用的时候就得提交info=xxx,download函数代码如下:

Phpcms v9.6.0 会员注册getshell漏洞

我们看download函数的第63行代码,需要强调的是这里查了url后缀是否以gif|jpg|jpeg|bmp|png为后缀,不是就会直接结束。继续往下读,读到第73行代码,可以看见调用了fillurl函数,我们跟进这个函数:

Phpcms v9.6.0 会员注册getshell漏洞

Phpcms v9.6.0 会员注册getshell漏洞

看代码的159行,这是最关键的一行代码$pos = strpos($surl,'#');这行代码将url里#前面的切割出来保存为url,而前面在download函数是查过后缀的,那么我们可以利用这一行代码达到绕过的目的,比如我们提交http://xxx/test.php#.jpg会通过对文件后缀的检查,然后经过切割,留下来的会是http://xxx/test.php成功绕过,然后fillurl函数剩下的代码就不是很重要了,主要做了先去掉url里的http://然后将在一起的多个单引号替换成了单个,最后返回的时候在加上http://。然后我们继续看download函数没看完的代码,看到第83行代码,看到这里调用了fileext函数取文件扩展名,fileext函数代码如下:

Phpcms v9.6.0 会员注册getshell漏洞

读过代码后我们可以发现取得是最后一个点之后的内容当后缀名,这里需要强调是最后一个点,接着往下读,看第89行代码,其中$this->upload_func的定义在attachment类的构造函数,代码如下:

那么回到download函数,看第90行代码$upload_func($file, $newfile)就等同于copy($file, $newfile),将文件复制到了本地,那么利用刚才的‘最后一个点’,我们可以控制文件后缀名为php,到此漏洞触发的全过程已经展现,我们已经可以导致getshell,那么最后一个问题来了,我们怎么找到shell,有两种方法:

第一种方法:

它生成的文件名是:uploadfile/年份/月日/时间具体到秒+3位100-999之间的随机字符+文件后缀

所以我们可以直接爆破,至于为什么知道这个文件命名规则,将漏洞分析的代码读后,里面有,具体就不复述了。。。

第二种方法:

我们回到最开始的register函数,找到如下代码:

代码将userid变量加到数组,再通过insert插入到数据库,插入的表是v9_member_detail数据表,我们看看这个数据表:

Phpcms v9.6.0 会员注册getshell漏洞

我们知道我们利用的时候传递进来的是info,而这个表里就两个字段,并没有content字段,就会导致报错,报错会爆出shell的地址

Poc:

Phpcms v9.6.0 会员注册getshell漏洞

Poc执行结果:

Phpcms v9.6.0 会员注册getshell漏洞

免责声明:由于无法甄别是否为投稿用户创作以及文章的准确性,本站尊重并保护知识产权,根据《信息网络传播权保护条例》,如我们转载的作品侵犯了您的权利,请您通知我们,请将本侵权页面网址发送邮件到qingge@88.com,深感抱歉,我们会做删除处理。