BlusCMS审计

BlueCMS审计

一直想学代码审计,在假期废了二十天之后,决定还是稍微学一点吧,毕竟学个安全连个代码审计都不会这根本说不过去嘛……


首先BlueCMS v1.6 的已公开漏洞有如下

  1. /uploads/ad_js.php下存在sql注入漏洞
  2. user.php页面的存在存储型xss
  3. 文件读取1
  4. 文件读取2
  5. 注册页面存在反射型XSS
  6. ip地址伪造漏洞

使用工具:seay源码审计工具


/uploads/ad_js.php下存在sql注入漏洞

ad_js.php源码

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
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php
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();
}
/*
function getone($sql, $type=MYSQL_ASSOC){
$query = $this->query($sql,$this->linkid);
$row = mysql_fetch_array($query, $type);
return $row;
}
$pre = "blue_";
function table($table)
{
global $pre;
return $pre .$table ;
}
*/
$ad = $db->getone("SELECT * FROM ".table('ad')." WHERE ad_id =".$ad_id);
if($ad['time_set'] == 0)
{
$ad_content = $ad['content'];
}
else
{
if($ad['end_time'] < time())
{
$ad_content = $ad['exp_content'];
}
else
{
$ad_content = $ad['content'];
}
}
$ad_content = str_replace('"', '\"',$ad_content);
$ad_content = str_replace("\r", "\\r",$ad_content);
$ad_content = str_replace("\n", "\\n",$ad_content);
echo "<!--\r\ndocument.write(\"".$ad_content."\");\r\n-->\r\n";
?>

可以看到其中比较关键的$ad_id是从GET型输入获取的,然后仅仅检测了该参数是否为空,就带入SQL语句进行查询,完全可以比如1 and 0 union select 1,2,3,4,5,6,7(假定七个字段),就可以进行sql注入,之后是对于输出的内容进行替换,但其实根本谈不上什么防御效果。

user.php页面的存在存储型xss

user.php代码有点长,截取重点的贴上来

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
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';

if(strlen($user_name) < 4 || strlen($user_name) > 16){
showmsg('用户名字符长度不符');
}
if(strlen($pwd) < 6){
showmsg('密码不能少于6个字符');
}
if($pwd != $pwd1){
showmsg('两次输入密码不一致');
}
if(strtolower($safecode) != strtolower($_SESSION['safecode'])){
showmsg('验证码错误');
}
if($db->getone("SELECT * FROM ".table('user')." WHERE user_name='$user_name'")){
showmsg('该用户名已存在');
}
if($db->getone("SELECT * FROM ".table('admin')." WHERE admin_name='$user_name'")){
showmsg('该用户名已存在');
}
$sql = "INSERT INTO ".table('user')." (user_id, user_name, pwd, email, reg_time, last_login_time) VALUES ('', '$user_name', md5('$pwd'), '$email', '$timestamp', '$timestamp')";

因为email并没有进行过滤,所以写一个就可以进行一个存储xss

文件读取1

1
2
3
4
5
6
7
8
9
10
11
12
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');
}

通过GET输入获取tpl_name,直接进行文件读取,经过html实体转化后变成htm文件。

文件读取2

$from变量是用户可控的,在登陆时可以通过抓包更改from变量的值

1
2
3
4
5
6
7
8
9
10
11
/*第一处*/
$from = !empty($_REQUEST['from']) ? $_REQUEST['from'] : '';
......
elseif($act == 'do_login'){
$user_name = !empty($_POST['user_name']) ? trim($_POST['user_name']) : '';
$pwd = !empty($_POST['pwd']) ? trim($_POST['pwd']) : '';
$safecode = !empty($_POST['safecode']) ? trim($_POST['safecode']) : '';
$useful_time = intval($_POST['useful_time']);
$from = !empty($from) ? base64_decode($from) : 'user.php';
......
showmsg('欢迎您 '.$user_name.' 回来,现在将转到...', $from);

可以发现$from变量控制着页面登陆后的跳转,可以把$from改成别的,这样可以实现一个任意文件读取。

注册页面存在反射型XSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$from = !empty($_REQUEST['from']) ? $_REQUEST['from'] : '';
......
elseif($act == 'reg')
{
if (!empty($_SESSION['user_id']) && $_SESSION['user_id'] != 1)
{
showmsg('您已经登录,请先退出登录再注册!');
}
if (!isset($_SESSION['last_reg']))
{
$_SESSION['last_reg'] = 0;
}
elseif ($timestamp - $_SESSION['last_reg'] < 30)
{
showmsg('为防止恶意注册,请于30秒后再来注册!');
}
template_assign(array('current_act', 'from'), array('注册新用户', $from));
$smarty->display('reg.htm');
}

此处会接收用户输入的$from值,并且不加过滤的输出html,所以会导致一个反射性xss

ip地址伪造漏洞

由如下代码可以看出,ip可以任意被伪造

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
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;
}
/*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')";
$db->query($sql);

可以看出可以通过伪造ip,让其插入数据库中,首先可以形成一个存储xss,然后既然可以随便伪造ip,而这个语句执行结果也会返回,那么可以构造payload。

1
2
3
/*getip()=1','1'),('','6','2','1','6',(select concat(admin_name,':',pwd) from blue_admin),'1','1*/
$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', '1','1'),('','6','2','1','6',(select concat(admin_name,':',pwd) from blue_admin),'1','1', '$is_check')";

变成了插入两条数据,第二条数据需要先去查询管理员账号密码,然后就可以舒舒服服进去后台了。

文章目录
  1. 1. BlueCMS审计
    1. 1.1. /uploads/ad_js.php下存在sql注入漏洞
    2. 1.2. user.php页面的存在存储型xss
    3. 1.3. 文件读取1
    4. 1.4. 文件读取2
    5. 1.5. 注册页面存在反射型XSS
    6. 1.6. ip地址伪造漏洞
,