美文网首页代码审计
PHP代码审计实践——BlueCMS V1.6

PHP代码审计实践——BlueCMS V1.6

作者: book4yi | 来源:发表于2021-12-29 22:39 被阅读0次

    前言:


    PHP代码审计计划一直学习到22年3月份,今天是后悔学安全的第485天。

    BlueCMS


    • 任意文件读取/修改:

    漏洞代码位于:/bluecms/admin/tpl_manage.php

     elseif($act == 'edit'){
        $file = $_GET['tpl_name'];
        if(!$handle = @fopen(BLUE_ROOT.'templates/default/'.$file, 'rb')){
            showmsg('打开目标模板文件失败');
        }
        $tpl['content'] = fread($handle, filesize(BLUE_ROOT.'templates/default/'.$file));
        $tpl['content'] = htmlentities($tpl['content'], ENT_QUOTES, GB2312);
        fclose($handle);
        $tpl['name'] = $file;
        template_assign(array('current_act', 'tpl'), array('编辑模板', $tpl));
        $smarty->display('tpl_info.htm');
     }
    

    这里挺直接的,使用fopen函数打开文件时,直接利用将用户可控的动态变量进行拼接,然后将在模板运行时为模板变量赋值,可以读取任意文件,并且进行修改:

    • 文件包含:

    漏洞代码位于:/bluecms/user.php

     elseif ($act == 'pay'){
        include 'data/pay.cache.php';
        $price = $_POST['price'];
        $id = $_POST['id'];
        $name = $_POST['name'];
        if (empty($_POST['pay'])) {
            showmsg('对不起,您没有选择支付方式');
        }
        include 'include/payment/'.$_POST['pay']."/index.php";
     }
    

    这里有两个思路:00截断、点号截断

    00截断利用条件:

    magic_quotes_gpc=off

    PHP版本 < 5.3.4

    但由于有设置全局过滤,所以此路不通:

    # common.inc.php
     if(!get_magic_quotes_gpc())
     {
        $_POST = deep_addslashes($_POST);
        $_GET = deep_addslashes($_GET);
        $_COOKIES = deep_addslashes($_COOKIES);
        $_REQUEST = deep_addslashes($_REQUEST);
     }
    

    那么只能尝试点号截断,且php版本需要小于5.2.8(?)可以成功,只适用windows,点号需要长于256

    • 任意文件删除:

    可以造成任意文件删除的点挺多的,本文仅列举三处
    触发点一,漏洞代码分析:/bluecms/admin/article.php

     elseif($act == 'do_edit'){
        $title = !empty($_POST['title']) ? trim($_POST['title']) : '';
        $color = !empty($_POST['color']) ? trim($_POST['color']) : '';
        $cid = !empty($_POST['cid']) ? intval($_POST['cid']) : '';
        if(empty($cid)){
            showmsg('新闻分类不能为空');
        }
        $author = !empty($_POST['author']) ? trim($_POST['author']) : $_SESSION['admin_name'];
        $source = !empty($_POST['source']) ? trim($_POST['source']) : '';
        $is_recommend = intval($_POST['is_recommend']);
        $is_check = intval($_POST['is_check']);
    
        if((!empty($_POST['lit_pic1']) && !empty($_FILES['lit_pic2']['name'])) || !empty($_FILES['lit_pic2']['name']))
        {
            if (file_exists(BLUE_ROOT . $_POST['lit_pic1']))
            {
                @unlink(BLUE_ROOT . $_POST['lit_pic1']);
            }
    

    成功删除根目录下的delete.txt文件:

    触发点二,漏洞代码分析:/bluecms/admin/database.php

    elseif($act == 'del')
    {
        $file_name = !empty($_GET['file_name']) ? trim($_GET['file_name']) : '';
        $file = BLUE_ROOT.DATA."backup/".$file_name;
        if(!@unlink($file))
        {
            showmsg('删除备份文件失败');
        }
        else
        {
            showmsg('删除备份文件成功', 'database.php?act=restore');
        }
     }
    

    触发点三,漏洞代码分析:/bluecms/publish.php

    elseif($act == 'del_pic')
    {
        $id = $_REQUEST['id'];
        $db->query("DELETE FROM ".table('post_pic').
                    " WHERE pic_path='$id'");
        if(file_exists(BLUE_ROOT.$id))
        {
            @unlink(BLUE_ROOT.$id);
        }
    }
    
    • SQL注入:

    触发点一,漏洞代码分析:/bluecms/admin/article.php

     elseif($act == 'del'){
        $article = $db->getone("SELECT cid, lit_pic FROM ".table('article')." WHERE id=".$_GET['id']);
        $sql = "DELETE FROM ".table('article')." WHERE id=".intval($_GET['id']);
        $db->query($sql);
        if (file_exists(BLUE_ROOT.$article['lit_pic'])) {
            @unlink(BLUE_ROOT.$article['list_pic']);
        }
        showmsg('删除本地新闻成功', 'article.php?cid='.$article['cid']);
     }
    

    虽然设置了全局过滤,但此处为数字型注入,可以绕过魔术引号的转义,并不影响SQL注入:

    触发点二:

    首先我们注意到common.inc.php中对$_POST、$_GET、$_COOKIES、$_REQUEST统一进行gpc处理,但是遗漏了$_SERVER,然后想到有没有可能,Web应用程序会将用户的IP写入到数据库中,于是乎发现了:

    # common.fun.php
    function getip()
    {
        if (getenv('HTTP_CLIENT_IP'))
        {
            $ip = getenv('HTTP_CLIENT_IP'); 
        }
        elseif (getenv('HTTP_X_FORWARDED_FOR')) 
        { //获取客户端用代理服务器访问时的真实ip 地址
            $ip = getenv('HTTP_X_FORWARDED_FOR');
        }
        elseif (getenv('HTTP_X_FORWARDED')) 
        { 
            $ip = getenv('HTTP_X_FORWARDED');
        }
        elseif (getenv('HTTP_FORWARDED_FOR'))
        {
            $ip = getenv('HTTP_FORWARDED_FOR'); 
        }
        elseif (getenv('HTTP_FORWARDED'))
        {
            $ip = getenv('HTTP_FORWARDED');
        }
        else
        { 
            $ip = $_SERVER['REMOTE_ADDR'];
        }
        return $ip;
    }
    

    可以通过在请求头中添加相关字段来伪造IP地址,继续查找存在插入客户端IP的相关代码:

    # `/bluecms/common.php`
    $sql = "INSERT INTO ".table('comment')." (com_id, post_id, user_id, type, mood, content, pub_date, ip, is_check) 
                VALUES ('', '$id', '$user_id', '$type', '$mood', '$content', '$timestamp', '".getip()."', '$is_check')";
    $db->query($sql);
    

    触发点三,漏洞代码分析:bluecms/user.php

     elseif($act == 'index_login'){
        $user_name = !empty($_REQUEST['user_name']) ? trim($_REQUEST['user_name']) : '';
        $pwd = !empty($_REQUEST['pwd']) ? trim($_REQUEST['pwd']) : '';
        $remember = isset($_REQUEST['remember']) ? intval($_REQUEST['remember']) : 0;
        if($user_name == ''){
            showmsg('用户名不能为空');
        }
        if($pwd == ''){
            showmsg('密码不能为空');
        }
        $row = $db->getone("SELECT COUNT(*) AS num FROM ".table('admin')." WHERE admin_name='$user_name'");
        if($row['num'] == 1){
            showmsg('系统用户组不能从前台登录');
        }
        $w = login($user_name, $pwd);
    

    可以看到在登录处没有做任何的过滤,虽然设置了全局过滤对所有的参数值执行了addslashes(),但由于mysql使用了GBK编码,故存在宽字节注入的可能:

    触发点四,此处是通过WVS扫描出来的,yyds!

    漏洞代码分析:/bluecms/include/common.inc.php

        if($_COOKIE['BLUE']['user_id'] && $_COOKIE['BLUE']['user_name'] && $_COOKIE['BLUE']['user_pwd'])
        {
            if(check_cookie($_COOKIE['BLUE']['user_name'], $_COOKIE['BLUE']['user_pwd']))
            {
                update_user_info($_COOKIE['BLUE']['user_name']);
            }
        }
    
     function check_cookie($user_name, $pwd){
        global $db, $_CFG;
        $sql = "SELECT pwd FROM ".table('user')." WHERE user_name='$user_name'";
        $user = $db->getone($sql);
        if(md5($user['pwd'].$_CFG['cookie_hash']) == $pwd) return true;
        else return false;
     }
    

    在未登录的情况下,会验证客户端COOKIE是否正确,这里又很有意思,虽然设置了过滤函数:deep_addslashes(),但是针对$_COOKIE['BLUE']['user_name'],只会检查到username这个键名就结束了,也就是说并没有对键值进行检测,那么所有包含了include/common.inc.php的脚本文件都存在COOKIE注入:

    • 重装漏洞:

    漏洞代码分析:bluecms/install/index.php

    require_once(dirname(__FILE__) . '/include/common.inc.php');
    elseif($act == 'step5')
    {
        define('IN_BLUE', TRUE);
        include dirname(__FILE__) . '/../include/common.inc.php';
        include BLUE_ROOT . 'admin/include/common.fun.php';
        update_data_cache();
        update_pay_cache();
        if(is_writable(BLUE_ROOT.'data/'))
        {
            $fp = @fopen(BLUE_ROOT.'data/install.lock', 'wb+');
            fwrite($fp, 'OK');
            fclose($fp);
        }
        $install_smarty->display('step5.htm');
    }
    

    安装的时候一直到step5时发现页面空白,当时觉得很蹊跷,看了一遍源码似乎没啥问题。当$act == 'step5'时,会判断/data/目录是否可写并生成lock文件,然后返回step5.htm页面。但成功部署完以后,却又没生成lock文件,这时只能问问度娘了。

    归根到底是require的问题,代码里包含了/include/common.inc.php/../include/common.inc.php,这两个文件都包含了如下代码:require(BLUE_ROOT.'include/smarty/Smarty.class.php');,而require运行时碰到错误会终止运行,两个调用文件都包含了Smarty类的文件,这里重复调用产生了错误,也就不会生成install.lock文件,修复的话把require改成require_once即可,这样就不会有重复调用而产生的错误。

    参考如下:


    Bluecms代码审计
    从小众blueCMS入坑代码审计
    bluecms代码审计(一):安装漏洞
    通过 BlueCMS 学习 php 代码审计

    相关文章

      网友评论

        本文标题:PHP代码审计实践——BlueCMS V1.6

        本文链接:https://www.haomeiwen.com/subject/wyejqrtx.html