bluecms 1.6 审计入门

一直想好好学审计,以前多多少少看过一些,但都没有自己好好审计过一个 CMS ,也没有一个自己的审计流程和方法。之前一直拿一个最新版本的 CMS 来审计,企图审计出 0day ,但一直没收获。现在转变下思路,从已经有大量漏洞的老 CMS 开始,不去看别人的审计思路,看看自己能不能审计到这些历史漏洞。就从被认为是最简单的,最适合新手入门的 bluecms 1.6 开始。

1.准备

安装完 CMS 后,先对目录结构进行一个了解

接下来对跟数据库操作相关的类和函数以及是否存在全局过滤和过滤的内容,进行一个了解。这样等下挖掘跟数据库相关的漏洞时,对于哪里有过滤,以及是否能绕过,可以有一个更清晰的认识。

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
45
46
47
48
49
50
51
52
53
54
55
56
57
//全局过滤文件位于 include/common.inc.php 其中主要使用了 deep_addslashes 函数来进行一个过滤
//这里对 $_SERVER 数组没有进行一个过滤,这说明跟 $_SERVER 相关的数据库操作可能存在SQL注入
if(!get_magic_quotes_gpc())
{
$_POST = deep_addslashes($_POST);
$_GET = deep_addslashes($_GET);
$_COOKIES = deep_addslashes($_COOKIES);
$_REQUEST = deep_addslashes($_REQUEST);
}
// deep_addslashes 对传入的参数使用 addslashes 系统函数来进行转义(',",\,NUL)
function deep_addslashes($str)
{
if(is_array($str))
{
foreach($str as $key=>$val)
{
$str[$key] = deep_addslashes($val);
}
}
else
{
$str = addslashes($str);
}
return $str;
}
//还有两个暂时没用到的过滤函数,分别作用是反转义和对特殊字符进行HTML实体编码
function deep_stripslashes($str)
{
if(is_array($str))
{
foreach($str as $key => $val)
{
$str[$key] = deep_stripslashes($val);
}
}
else
{
$str = stripslashes($str);
}
return $str;
}

function deep_htmlspecialchars($str)
{
if(is_array($str))
{
foreach($str as $key => $val)
{
$str[$key] = deep_htmlspecialchars($val);
}
}
else
{
$str = htmlspecialchars($str);
}
return $str;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 数据库操作类位于 include/mysql.class.php
// 其中跟查询相关的函数中并没有针对sql注入有一个防御,直接是封装了系统函数,对传入的sql语句进行一个查询
// 所以这里如果sql语句是直接拼接了用户传入的参数的话,也会存在注入的可能性
function query($sql){
if([email protected]_query($sql, $this->linkid)){
$this->dbshow("Query error:$sql");
}else{
return $query;
}
}

function getall($sql, $type=MYSQL_ASSOC){
$query = $this->query($sql);
while($row = mysql_fetch_array($query,$type)){
$rows[] = $row;
}
return $rows;
}

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

然后再对该 cms 进行一个功能点上的探测,到 cms 的前台和后台查看,改 cms 存在什么功能,这些功能可能导致一些什么样的漏洞,对此有一个大概的了解。在之后漏洞挖掘的过程中,目标会更加明确。

1.任意文件读取修改

后台存在模板编辑的功能,要编辑的模板文件名由用户传入且可控,之后打开的文件只是将模板文件名简单的拼接上一个默认的模板路径,中间并没有经过什么过滤。所以这里就可以通过 ../ 来进行一个目录穿越,实现对任意文件的一个读取。

编辑完之后,对目标文件进行保存也是同样的原理。所以可以修改任意的一个文件内容。

2.储存型 XSS

站点设置这个功能,设置的参数,肯定是存到数据库里的,这里如果没有对数据进行一个校验,那么就可能会造成注入和存储型XSS。

1
2
3
4
5
6
7
8
9
10
11
12
13
//修改站点设置的主要逻辑如下,文件位于 admin/setting.php
foreach($_POST as $k => $v){
/* 这里遍历 $_POST 变量,没有对数据额外过滤,只应用了全局过滤中的 deep_addslashes 函数,
而这个函数主要转义了引号来防范了SQL注入,这里拼接的sql语句被引号包围了,所以这里不存在sql注入,
但这个函数并没有针对xss进行过滤 */
if(!$db->query("UPDATE ".table("config")." SET value='$v' WHERE name='$k'")){
showmsg('更新站点设置失败', true);
}
elseif($k != 'act'){
$config_arr[$k] = $v;
}
}
showmsg('更新站点成功', 'setting.php', true);

payload如图

后台所有信息的修改和添加都是采用相同的方式,对于输入没有过滤 XSS,所有其他信息修改和添加处也存在同样的存储型XSS。

3.SQL注入

第一处

这处SQL注入在后台用户信息修改处

1
2
3
4
5
6
7
8
9
10
11
//主要代码在 admin/user.php 第 83 行
elseif($act == 'edit')
{
/* 这里的SQL语句获取时直接拼接了 get 请求中的 user_id ,虽然 $_GET 数组存在全局过滤引号,但这里是数字
型注入,不需要用到引号,随意全局过滤对于这处注入也就失效了。 */
$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');
}

payload 如图所示

第二处

此处SQL注入为前台SQL注入,前台可直接访问

这是功能入口

触发原理跟上一处的 SQL 注入相同

1
2
3
4
5
6
7
8
$ad_id = !empty($_GET['ad_id']) ? trim($_GET['ad_id']) : ''; // 没有对 $ad_id 进行一个过滤
if(empty($ad_id))
{
echo 'Error!';
exit();
}

$ad = $db->getone("SELECT * FROM ".table('ad')." WHERE ad_id =".$ad_id);//此处是整型注入 不受全局过滤影响

4.反射型XSS

第一处

首先这里本来应该也是存在注入的,原理跟注入相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if(empty($_POST['user_id'])){
return false;
}
// 这三处 delete 语句直接拼接了user_id 明佑进行过滤且无引号保护
$db->query("DELETE FROM ".table('user')." WHERE user_id = ".$_POST['user_id']);
if($_POST['del_info']){
$db->query("DELETE FROM ".table('post')." WHERE user_id = ".$_POST['user_id']);
}
if($_POST['del_coment']){
$db->query("DELETE FROM ".table('comment')." WHERE user_id = ".$_POST['user_id']);
}
if(defined('UC_API') && @include_once(BLUE_ROOT.'uc_client/client.php')){
echo uc_user_delete($_POST['username']);
}
showmsg('删除会员成功','user.php');

但是比较尴尬的是不知道这里如何利用,对于 delete 处的注入,我知道可以报错注入,但是这里如果 SQL 查询出错,并不报错,而是直接输出报错的 SQL 语句。也正因如此,所以有了这处的反射性 XSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    
function query($sql){
if([email protected]_query($sql, $this->linkid)){
$this->dbshow("Query error:$sql");
}else{
return $query;
}
}
// 这是查询方法 query 的实现,可以看到其中如果 sql 查询出错了,调用 dbshow 方法
function dbshow($msg){
if($msg){
echo "Error:".$msg."<br><br>";
}else{
echo "Errno:".$this->errno()."<br>Error:".$this->error();
}
exit;
}
// 当给 dbshow 方法传参,则直接输出Error字符+传入的信息。这里传入的 $sql 是我们查询的sql语句,没有任何的过滤,最后直接输出,就导致了一个反射型的 XSS

第二处

相同的漏洞,在 “分类信息” 这个功能模块中,在删除的功能这,也有多处相同的漏洞

漏洞触发点

1
2
3
4
5
6
7
8
if(!$db->query("DELETE FROM ".table('ad_phone')." WHERE id=".$_GET['id']))
{
showmsg('删除电话广告出错', true);
}
else
{
showmsg('删除电话广告成功', 'ad_phone.php', true);
}

原理的话跟前一个相同

5.任意文件删除

第一处

触发的功能点在模块管理,友情链接管理处

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	// 代码位于 admin/link.php 55-56行
/*
在点击提交后,前端会给后端传的参数中 link_logo 为 '站内图片相对路径' 这个输入框中的内容,
link_login2 为一个 hidden 标签,其值是为 logo 的相对路径。

后端删除的逻辑是,判断 输入框中是否还有值,若没值,则当前 logo 被删除,则获取 link_logo2 中保存的图片地址,然后拼接上网站根目录的路径,形成一个绝对路径地址,然后判断这个地址的文件是否存在,存在则删除。

这里是从网站根目录进行拼接。而 link_logo2 的值是前段传入,我们可控的,所以造成可以拼接任意文件路径,造成任意文件删除。

修复:限定目录为 link_logo 存储的目录,且过滤 . / 等特殊字符
*/
if (!empty($_POST['link_logo'])){
if (strpos($_POST['link_logo'], 'http://') != false && strpos($_POST['link_logo'], 'https://') != false){
showmsg('只支持本站相对路径地址');
}
else{
$link_logo = trim($_POST['link_logo']);
}
}else{
if(file_exists(BLUE_ROOT.$_POST['link_logo2'])){
@unlink(BLUE_ROOT.$_POST['link_logo2']);
}
}

第二处

这里是在查看

1
2
3
4
5
6
7
8
// 漏洞位置位于 admin/info.php 431-434行
/*
也是直接拼接变量造成的,全局过滤的字符中,不包括 . / 所以这里这样直接拼接,产生了任意文件删除
*/
if (file_exists(BLUE_ROOT.$_POST['lit_pic']))
{
@unlink(BLUE_ROOT.$_POST['lit_pic']);
}

总结

这个 CMS 漏洞产生的原因,都是全局过滤只转义了单引号,双引号,反斜杠,这样只能防御住字符型的注入,对于数字型的注入没有办法。

然后后对 XSS,虽然在全局函数里,定义了相关的过滤函数,但在后台的相关功能中都没有使用起来,所以造成了这样 XSS 漏洞。

文件操作相关的漏洞,没有解决好路径拼接和目录穿越的问题。

毕竟这个 CMS 是快要10年前的东西了。整体结构也比较简单,各功能模块的实现也比较类似,源码看了一半,也大概知道哪里可能会存在什么样的一个问题了。所以最后源码也没看完。相较于现在的这些 MVC 框架,这个整体结构和写法上,就看着简单多了。

对于入门审计,这个 CMS 确实是一个不错的选择。