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

前言:


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 代码审计

©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容