“ 我终于成为了你想要的那种人,可是你依然不属于我。”
代码审计一直是从事网络安全人员必备的技能之一。作为菜鸡的我,最近开始接触代码审计,顺便记录一下自己的学习历程。
下面是我针对无框架代码审计简单总结的一个思维导图。
简单来说,代码审计主要是通过工具结合手工的方式来完成。在工具帮助我们进行一次初步的筛选之后,我们可以对筛选出来相关的点进行手工审计。
关键字搜索,主要是搜索特定函数、可控变量是否不存在或者不完全存在安全过滤导致漏洞产生。
挖掘功能点,主要是定位具体功能点进行抓包,配合阅读代码段进行审计。
我这里审计的是BEECMS某版本源代码,打开首页如下。
将代码导入Seay审计工具。
之前查阅资料,这个版本的BEECMS报过文件上传漏洞。PHP中有关文件上传的关键字有$_FILES、move_uploaded_file()等等。
先尝试全局搜索“上传”,看是否在代码中有相关注释。
可以看到有117处关于上传字样的地方。经观察很多地方都是前端代码有上传的字样,且关于上传的字样有点多,于是我们尝试更换搜索关键字。
搜索$_FILES,瞬间少了很多结果。
进入第一个结果,按照文件名,先去浏览器查看一番。
登陆后台。
找到对应功能点
这里配合抓包,分析上传图片的参数。
可以看到有两个参数 file_info和uppic
Seay中定位搜索
$submit=$_POST['uppic'];
if($submit){
$up=$_POST['up'];
$file_info=fl_html($_POST['file_info']);
$value_arr=array('');
$type=explode('|',$type_file);
//有文件
if(is_uploaded_file($_FILES['up']['tmp_name'])){
$is_up_size = $_sys['upload_size']*1000*1000;
$value_arr=up_file($_FILES['up'],$is_up_size,$type);
//处理上传后的图片信息
$file_path=$value_arr['file'];//文件保存路径
$file_ext=$value_arr['ext'];//文件扩展名
$file_size = empty($value_arr['size'])?0:$value_arr['size'];//文件大小
$file_time = $value_arr['time'];//上传时间
//入库
$sql="insert into ".DB_PRE."upfiles (file_info,file_ext,file_size,file_path,file_time) values ('".$file_info."','".$file_ext."',".$file_size.",'".$file_path."','".$file_time."')";
$mysql->query($sql);
分析代码段,查看是否存在过滤。其中empty是php自带过滤,判断变量是否为空。is_uploaded_file判断文件是否通过HTTP POST形式上传。is_up_size判断大小,这个也可以不用考虑,因为我们是可以控制上传图片的大小的。
up_file,不是php自带函数,定位寻找。通读代码可以看到是对上传文件的一些验证。
回到刚刚上传图片的地方,我们上传的是一张jpg格式的图片,提示我们上传文件格式不正确jpg
在上面的代码中,也看到了'上传文件格式不正确'的字样。
那这两处是否有所关联呢?修改代码验证一下。
好,确定就是这个地方的代码了。
继续分析。
检索$file_type
追踪$pic_name
在fun.php中定位到$pic_name。
与之关联的pathinfo的功能是以数组的形式返回文件路径信息。
例如:
<?php
print_r(pathinfo("/testweb/test.txt"));
?>
输出:
Array
(
[dirname] => /testweb
[basename] => test.txt
[extension] => txt
)
也就是说,$type就是上传格式的类型限制。
好,那我们要保证的,就是$file_type的值要在$type里面才可以上传成功。
那们要改变的,就是这个extension的类型了,在本地尝试Fuzz测试
经过多次测试,添加空格,点等等方式都无法改变extension匹配最后一个格式。这里使用的pathinfo将文件分割开来,取最后一个后缀名与白名单中的类型进行匹配,如果不符合就无法上传。因此无法使提交文件的$type类型绕过验证。
后续尝试在上传点抓包,用00截断,文件虽然上传成功,但是会是如下情况
即使上传成功,文件后缀名还不是我么想要的,所以这里的上传不存在漏洞。
好,那就换一处$FILES的地方继续审计。
找到对应文件路径
上传文件,抓包
定位pic_cate
if(is_uploaded_file($v)){
$pic_info['tmp_name']=$v;
$pic_info['size']=$_FILES['up']['size'][$k];
$pic_info['type']=$_FILES['up']['type'][$k];
$pic_info['name']=$_FILES['up']['name'][$k];
$pic_name_alt=empty($is_alt)?'':$pic_alt[$k];
$is_up_size = $_sys['upload_size']*1000*1000;
$value_arr=up_img($pic_info,$is_up_size,array('image/gif','image/jpeg','image/png','image/jpg','image/bmp','image/pjpeg','image/x-png'),$up_is_thumb,$up_thumb_width,$up_thumb_height,$logo=1,$pic_name_alt);
//处理上传后的图片信息
$pic_name=$value_arr['up_pic_name'];//图片名称空
$pic_ext=$value_arr['up_pic_ext'];//图片扩展名
$pic_title = $pic_alt[$k];//图片描述
$pic_size = $value_arr['up_pic_size'];//图片大小
$pic_path = $value_arr['up_pic_path'];//上传路径
$pic_time = $value_arr['up_pic_time'];//上传时间
$pic_thumb = iconv('GBK','UTF-8',$value_arr['thumb']);//缩略图
$cate = empty($pic_cate)?1:$pic_cate;//图片栏目
//入库
$sql="insert into ".DB_PRE."uppics (pic_name,pic_ext,pic_alt,pic_size,pic_path,pic_time,pic_thumb,pic_cate) values ('".$pic_name."','".$pic_ext."','".$pic_title."','".$pic_size."','".$pic_path."','".$pic_time."','".$pic_thumb."',".$cate.")";
$mysql->query($sql);
这里可以明显看到up_img有三个参数,先定位前两个看看
可以看到前两个参数只是对图片进行格式和大小的判断
好,看一下第三个参数,只是判断了文件的MIME类型
并且在上传处的代码,只是单纯进行上传成功与否的判断,没有别的过滤和判断机制,导致产生了文件上传漏洞。
好,抓包尝试利用。
正常上传,看到文件返回路径。
由于只是验证了MIME,所以我们直接修改文件为php文件
可以看到php文件已经正常上传成功。
接下来后续操作大佬们就都晓得了
冰蝎、哥斯拉搞起来~
这里经过审计发现的问题,就是在文件上传时,验证就不要再通过MIME类型验证了,安全的验证方式需要直接检测后缀名。
初学代码审计,如有不足,还请师傅们指正
喜欢的话,劳烦点个小小的关注吧
原文始发于微信公众号(小杰安全):记一次无框架PHP代码审计
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论