漏洞概要 关注数(51) 关注此漏洞
缺陷编号: WooYun-2014-67333
漏洞标题: cmseasy 最新版任意权限getshell
相关厂商: cmseasy
漏洞作者: phith0n
提交时间: 2014-07-04 09:30
公开时间: 2014-10-02 09:32
漏洞类型: 命令执行
危害等级: 高
自评Rank: 20
漏洞状态: 厂商已经确认
漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 help@wooyun.org
Tags标签: php源码审核
漏洞详情
披露状态:
2014-07-04: 细节已通知厂商并且等待厂商处理中
2014-07-04: 厂商已经确认,细节仅向厂商公开
2014-07-07: 细节向第三方安全合作伙伴开放(绿盟科技、唐朝安全巡航、无声信息)
2014-08-28: 细节向核心白帽子及相关领域专家公开
2014-09-07: 细节向普通白帽子公开
2014-09-17: 细节向实习白帽子公开
2014-10-02: 细节向公众公开
简要描述:
这是一个很长的故事,还请客官慢慢看来。(看在我这么晚还在挖洞写文章的份上,求闪电呀!)
版本:2014-06-05
详细说明:
0x01 首先,从一个后台未授权访问开始讲起。
看到文件/lib/admin/admin.php
这是一个抽象类,是作为所有后台类的父类,它的构造函数里有验证管理员是否登录的代码,也就是check_admin这个函数。
但是我们看到这两句话:
if($servip==front::ip()&&front::get('ishtml')==1) return;
$this->check_admin();
大概的意思是,如果当前ip等于$servip(服务器IP),而且ishtml==1的话就return,也就没有执行check_admin函数。
那么,cmseasy是怎么取当前IP的?
很经典的函数:
这个函数里HTTP_X_FORWARDED_FOR是可以伪造的!
所以我们只需要伪造一个IP地址,和服务器的IP相同,就能绕过后台登录检查,直接进入后台。
官方演示站把前后台分离了,不方便演示。我就随便百度上找一个站吧。
就它http://**.**.**.**/了,先ping一下看到ip是**.**.**.**,然后用任何方法将IP伪造成**.**.**.**:
然后带上get参数ishtml=1访问后台即可:
不过但是,cmseasy后台并不是只验证了你是否登录,还有很多地方调用了chkpw函数验证你的权限,这个是没法绕过的,所以很多后台功能用不了。
不过已经可以看到很多敏感信息了,比如说加密cookie字符串,通过这个字符串是可以注入的,但这不是今天的重点。今天是要getshell的。
0x02 丧心病狂地找一些没有验证权限的函数
很少有函数没有调用chkpw验证权限,可以说基本所有设置、修改网站信息的地方都验证了权限。
但还是被我找到一处,设置语言的函数,/lib/admin/language_admin.php 第42行:
这个函数没有调用chkpw,直接访问看看:
确实可以访问。那么我们仔细看看代码,关键就是我列出的这块:
看到file_put_contents感觉可以写文件,但怎么利用呢?
这个函数思路大概是,先从/lang/cn/system.php中读出一个字符串,并循环遍历$_POST,正则匹配出'$key'=>'(.*?)',这样的字符串,然后判断是否是删除,如果是删除则把匹配出来的替换成空,如果不是删除则把匹配到的(.*?)替换成$value。
所以,我自然地想到,匹配到的(.*?)替换成$value,只要$value能闭合单引号,就能写shell进system.php这个文件里去了。
但偏偏因为全局过滤注入,所有$_GET都被addslashes了,不可能逃逸出单引号的限制。
0x03 一次不行,二次呢?
这时我关注了前面这个if语句:if (is_array($to_delete_items) && in_array($key,$to_delete_items)),如果$to_delete_items是数组,而且$key在这个数组中,则用str_replace将匹配出的内容替换成空。
$to_delete_items从哪里来?从POST中来,也就是说是可控的。所以我们可以删除某次匹配的数据。
这时我就会想,第一次我注入了一些数据,如果我再POST一次,能把第一次注入的多余的一些数据删除掉(比如删除单引号,或转义符),那我的webshell不就可以逃逸出单引号了吗?
而且,注意,这里的正则用了非贪婪模式,也就是说匹配到第一个'就停止。
所以,我举个简单的例子,比如我第一次输入的数据是a=1111',phpinfo()
保存在文件中就是
那么第二次我再次匹配到a,因为非贪婪模式,这时匹配到第一个'为止,也就是匹配出这些内容:
如果第二次传入的参数是to_delete_items[]=a,那么就会删除我匹配到的,剩下什么?剩下phpinfo()'
phpinfo()成功逃逸出来。做一些处理,让这个文件不出错,就能完美getshell!
漏洞证明:
说干就干,首先本地搭建好了最新版cmseasy。
用firefox插件X-Forwarded-For Header修改IP为**.**.**.**(本地服务器地址)。
然后访问http://localhost/easy/index.php?case=language&act=edit&table=orders&admin_dir=admin&site=default&ishtml=1,发现越权访问成功:
然后就向其POST第一个数据包,内容是:
这是查看system.php发现send_email这一项变成了这样:
好,我们再来POST第二次:
这时再看到system.php发现已经成这样了:
访问发现getshell完成:
修复方案:
你们懂得比我多。
版权声明:转载请注明来源 phith0n@乌云
漏洞回应
厂商回应:
危害等级:高
漏洞Rank:20
确认时间:2014-07-04 14:57
厂商回复:
感谢,立即修正
最新状态:
暂无
漏洞评价:
对本漏洞信息进行评价,以更好的反馈信息的价值,包括信息客观性,内容是否完整以及是否具备学习价值