网站目录结构

网站基本防护

/include/common.inc.php
第30-36行
$_GET、$_POST、$_COOKIES、$_REQUEST使用deep_addslashes()函数过滤一遍,漏了$_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);
}

追踪一下deep_addslashes()函数
/include/common.fun.php
第14-28行

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

SQL注入漏洞

前台

/ad_js.php

1
2
12:$ad_id = !empty($_GET['ad_id']) ? trim($_GET['ad_id']) : '';
19:$ad = $db->getone("SELECT * FROM ".table('ad')." WHERE ad_id =".$ad_id);

$ad_id没有使用单引号双引号包括所以addslashes()函数不起作用
getone()方法位于/include/mysql.class.php61行,作用是执行SQL语句并输出

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

$ad_id只是用trim()函数去除首尾的空格,故存在SQL注入漏洞

/guest_book.php(XF注入)

因为没有对$_SERVER进行过滤,所以使用X-Forwarded-For或者CLIENT-IP可以伪装ip进行SQL注入

全局搜索ip找到guset_book.php里面有关于ipSQL语句
第77-79行

1
2
3
$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);

$online_ip就是getip()
追踪到common.fun.phpgetip()函数
第106-133行

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
function getip()
{
if (getenv('HTTP_CLIENT_IP'))
{
$ip = getenv('HTTP_CLIENT_IP');
}
elseif (getenv('HTTP_X_FORWARDED_FOR'))
{
$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注入

评论区出现SQL注入结果

/comment.php(XF注入)

全局搜索getip

发现comment.php里面有一处SQL语句
第133-135行

1
2
3
$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);

其它参数都有addslashes()函数过滤而getip()的值没有,所以存在SQL注入漏洞,和上面一样是insert型注入,就不演示了

admin/login.php(宽字节注入万能密码)

第22-23行

1
2
$admin_name = isset($_POST['admin_name']) ? trim($_POST['admin_name']) : '';
$admin_pwd = isset($_POST['admin_pwd']) ? trim($_POST['admin_pwd']) : '';

然后第31行判断

1
if(check_admin($admin_name, $admin_pwd))

追踪check_admin()函数到common.fun.php
176-188行

1
2
3
4
5
6
7
8
9
10
11
12
13
function check_admin($name, $pwd)
{
global $db;
$row = $db->getone("SELECT COUNT(*) AS num FROM ".table('admin')." WHERE admin_name='$name' and pwd = md5('$pwd')");
if($row['num'] > 0)
{
return true;
}
else
{
return false;
}
}

因为编码是gbk2312$name使用了addslashes()函数过滤,所以存在SQL注入漏洞,可造成万能密码进后台

/user.php(登录页面宽字节注入)

第77行

1
$row = $db->getone("SELECT COUNT(*) AS num FROM ".table('admin')." WHERE admin_name='$user_name'");

$user_name使用了addslashes()函数过滤但是网页编码是gbk2312,所以存在宽字节注入

/user.php(注册页面insert型宽字节注入)

138行

1
$email  = 	!empty($_POST['email']) ? trim($_POST['email']) : '';

160行

1
$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使用单引号包围,有addslashes()函数过滤,网页编码是gbk2312所以存在宽字节注入

/user.php(注册页面宽字节注入)

第135行

1
$user_name 	=	!empty($_POST['user_name']) ? trim($_POST['user_name']) : '';

第154-156行

1
2
3
if($db->getone("SELECT * FROM ".table('user')." WHERE user_name='$user_name'")){
showmsg('该用户名已存在');
}

故存在宽字节注入,但是$user_name有大于4小于16的长度限制,不好利用,有点鸡肋

后台

/admin/nav.php

63-70行

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

$_GET['navid']直接接在后面,存在SQL注入漏洞

/admin/attachment.php

第78-85行

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注入漏洞

/admin/ad.php

第101行

1
$ad_id = !empty($_GET['ad_id']) ? trim($_GET['ad_id']) : '';

第106行

1
$ad = $db->getone("SELECT ad_id, ad_name, time_set, start_time, end_time, content, exp_content FROM ".table('ad')." WHERE ad_id=".$ad_id);

$ad_id直接接在后面,存在SQL注入漏洞

总结

后台注入大多都是直接拼接或者宽字节造成的,挺多的所以这里略了

XSS漏洞

/user.php

注册了一个用户在个人信息里面出现了一些注册信息,查看源代码看是否存在存储型XSS
135-139行

1
2
3
4
5
$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']) : '';

对邮箱$email只有个trim()以及addslashes(),所以存在存储型XSS,直接在网页表单写入payload会被前端检测无法提交,所以抓包改

/user.php

第266行

1
$content = !empty($_POST['content']) ? filter_data($_POST['content']) : '';

评论的内容采用filter_data()函数过滤
追踪filter_data()函数,位于common.fun.php第985-988行

1
2
3
4
5
function filter_data($str)
{
$str = preg_replace("/<(\/?)(script|i?frame|meta|link)(\s*)[^<]*>/", "", $str);
return $str;
}

很明显漏了img标签,所以使用img标签即可,payload:<p><img src=1 onerror=alert(1)></p>
这里必须抓包改,不然会有一个&nbsp干扰XSS payload导致无法执行

文件包含漏洞

/user.php

第742-751行

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";
}

$_POST['pay']前后加了文件,可以使用../进行目录遍历,后面的/index.php如果php版本低于5.3.4magic_quotes_gpc=off则可以使用%00截断,导致任意文件包含。还可以使用.号路径长度截断,Windows下目录最大长度为256字节,Linux下目录最大长度为4096字节
可配合用户头像上传上传图片马使用

SSRF漏洞

/user.php

第779-785行

1
2
3
4
5
6
7
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']);
}

它本来的意思是想不能包含http://或者https://应该用!==,但是他用了!=,根据php弱类型strpos()返回值为0是和false相等的,故能够绕过造成SSRF漏洞

任意文件删除

/user.php

第787-789行

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

face_pic1face_pic2为空时,输入的任意存在文件能被删除
我新建了一个test.php文件在网站根目录下

burpsuite抓包传入路径

test.php已被删除

/user.php

第615-617行

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

很明显的任意文件删除

总结

刚入坑审计,这是审计的第二个cms,目前使用的就是rips以及Seay扫一下可能存在漏洞的地方,有的漏洞是扫不出来的需要自己去发现,找SQL注入可以全局搜索一下SELECT、UPDATE、INSERT、DELETE等关键字,还可以使用查找危险函数的方法找漏洞比如unlink()函数可能存在任意文件删除。有的时候看代码太过繁琐抽象可以尝试功能点审计,比如找到一处上传或者留言板等,去看对应的代码,对变量进行回溯,看是否可控。像我这样刚入坑审计的菜鸟无疑是很枯燥和困难的,代码复杂难懂量大,但是方法不难,所以更多的还是耐心、仔细和坚持。文章中有不足和错误的地方还望师傅们指正。