$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
}
else{
$msg = '上传失败!';
}
}
else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
}
}
这里很明显是定义一个白名单,只允许三种文件类型;
substr()函数是返回字符的一部分,strrpos(string,find,start)函数查找字符串中最后一次出现的位置;
这里是将后缀名提取出来赋值给 f i l e e x t , 之 后 拼 接 到 了 文 件 路 径 后 面 , 这 也 成 了 突 破 口 。 我 们 可 以 发 现 使 用 G E T 方 法 , 也 就 是 说 我 们 可 以 抓 包 修 改 g e t 的 参 数 , 然 后 通 过 _file_ext,之后拼接到了文件路径后面,这也成了突破口。我们可以发现使用GET方法,也就是说我们可以抓包修改get的参数,然后通过%00截断使 fileext,之后拼接到了文件路径后面,这也成了突破口。我们可以发现使用GET方法,也就是说我们可以抓包修改get的参数,然后通过file_ext无效,这样就可以上传PHP文件;
这里再复习一下%00截断的概念和原理:
在URL中%00表示ASCII码中的0,而0作为特殊字符保留,表示字符结束,当url中出现%00时就会认为读取已结束,这里也是利用这一性质才能使$_file_ext无效;
但是放包之后我的显示上传失败;
原来这种截断是要有条件的:php版本小于5.3.4,php的magic_quotes_gpc为OFF状态;
要满足这两个条件才可以,因为我的php是7.3.4版本的,所以这里就只介绍思路啦
这一关也是利用了截断,和上一关不同的是,是POST截断,同样是抓包:
可以先用加号做标记,找到2b改成00就可以了
然后显示这样放包就成功了;
这关就有很大不同了,查看源码
function getReailFileType($filename){
$file = fopen($filename, "rb");
$bin = fread($file, 2); //只读2字节
fclose($file);
$strInfo = @unpack("C2chars", $bin);
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
$fileType = '';
switch($typeCode){
case 255216:
$fileType = 'jpg';
break;
case 13780:
$fileType = 'png';
break;
case 7173:
$fileType = 'gif';
break;
default:
$fileType = 'unknown';
}
return $fileType;
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_type = getReailFileType($temp_file);
if($file_type == 'unknown'){
$msg = "文件未知,上传失败!";
}else{
$img_path = $UPLOAD_ADDR."/".rand(10, 99).date("YmdHis").".".$file_type;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
}
else{
$msg = "上传失败";
}
}
}
其中rb是读取二进制文件,后面的fread函数只读两字节,也就是说只对文件头进行了检测,所以可以直接上传图片马,这是方法一;
接着分析一下代码先:
unpack(format,data)函数是规定在解包数据时所使用的格式,这里是文件头按照c格式解包;
intval() 函数用于获取变量的整数值;
网上说有文件包含漏洞,但是我这里并没有include.php文件,所以我是直接上传的图片马,方法直接有记。
function isImage($filename){
$types = '.jpeg|.png|.gif';
if(file_exists($filename)){
$info = getimagesize($filename);
$ext = image_type_to_extension($info[2]);
if(stripos($types,$ext)){
//查找&ext第一次在$types出现的位置
return $ext;
}else{
return false;
}
}else{
return false;
}
}
突破口肯定在这个函数,所以我copy分析下先:
getimagesize()函数用于获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息。
语法格式:
array getimagesize ( string KaTeX parse error: Expected 'EOF', got '&' at position 19: …ename [, array &̲imageinfo ] )
getimagesize() 函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型及图片高度与宽度。
所以这里还是可以直接上传图片马就ok
虽然这里用到php_exif模块来判断文件类型,但是还是可以上传图片马;
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])){
// 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
$filename = $_FILES['upload_file']['name'];
$filetype = $_FILES['upload_file']['type'];
$tmpname = $_FILES['upload_file']['tmp_name'];
$target_path=$UPLOAD_ADDR.basename($filename);
// 获得上传文件的扩展名
$fileext= substr(strrchr($filename,"."),1);
//判断文件后缀与类型,合法才进行上传操作
if(($fileext == "jpg") && ($filetype=="image/jpeg")){
if(move_uploaded_file($tmpname,$target_path))
{
//使用上传的图片生成新的图片
$im = imagecreatefromjpeg($target_path);
if($im == false){
$msg = "该文件不是jpg格式的图片!";
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".jpg";
$newimagepath = $UPLOAD_ADDR.$newfilename;
imagejpeg($im,$newimagepath);
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = $UPLOAD_ADDR.$newfilename;
unlink($target_path);
$is_upload = true;
}
}
else
{
$msg = "上传失败!";
}
}else if(($fileext == "png") && ($filetype=="image/png")){
if(move_uploaded_file($tmpname,$target_path))
{
//使用上传的图片生成新的图片
$im = imagecreatefrompng($target_path);
if($im == false){
$msg = "该文件不是png格式的图片!";
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".png";
$newimagepath = $UPLOAD_ADDR.$newfilename;
imagepng($im,$newimagepath);
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = $UPLOAD_ADDR.$newfilename;
unlink($target_path);
$is_upload = true;
}
}
else
{
$msg = "上传失败!";
}
}else if(($fileext == "gif") && ($filetype=="image/gif")){
if(move_uploaded_file($tmpname,$target_path))
{
//使用上传的图片生成新的图片
$im = imagecreatefromgif($target_path);
if($im == false){
$msg = "该文件不是gif格式的图片!";
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".gif";
$newimagepath = $UPLOAD_ADDR.$newfilename;
imagegif($im,$newimagepath);
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = $UPLOAD_ADDR.$newfilename;
unlink($target_path);
$is_upload = true;
}
}
else
{
$msg = "上传失败!";
}
}else{
$msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
}
}
这里源码每步都会有注释,一目了然,二次渲染了上传的图片,绕过方法思路是,找到渲染前后没有变化的位置,然后将php代码写进去,就可以成功上传带有php代码的图片,推荐这篇文章 https://blog.csdn.net/u014029795/article/details/102908114
直接代码审计:
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');//定义白名单
$file_name = $_FILES['upload_file']['name'];//上传文件名
$temp_file = $_FILES['upload_file']['tmp_name'];//临时文件名路径名称
$file_ext = substr($file_name,strrpos($file_name,".")+1);//截取文件后缀名
$upload_file = $UPLOAD_ADDR . '/' . $file_name;//拼接上传文件的路径名称
if(move_uploaded_file($temp_file, $upload_file)){
//将临时文件放到指定路径,如果成功执行
if(in_array($file_ext,$ext_arr)){
//判断文件后缀
$img_path = $UPLOAD_ADDR . '/'. rand(10, 99).date("YmdHis").".".$file_ext;//生成新的文件名
rename($upload_file, $img_path);//对文件重命名,并将新的文件目录覆盖到原来的文件目录上
unlink($upload_file);
$is_upload = true;
}else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file);
}
}else{
$msg = '上传失败!';
}
}
做到这里其他一些函数应该熟悉了,基本函数我就不记笔记了,直接上注释,其中rename()函数重命名文件或目录,返回布尔值;我们猜想能不能在对文件重命名之前做点事情获取信息呢?
所以这里想到利用burp多线程发包,不断访问,当线程足够的时候可能会跳过文件重命名这个相对于比较耗费时间的过程;
我自己整了好久没有整好,思路清楚了但是不知道问题出在哪里。待学业有成时再来;
这一关就是17和16两关的结合体,还是待突破(可能我环境问题)
不知道这一关为什么会设置在这里,做了前面的关卡,这关查看源码会发现很多问题:
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
$file_name = $_POST['save_name'];
$file_ext = pathinfo($file_name,PATHINFO_EXTENSION);
if(!in_array($file_ext,$deny_ext)) {
$img_path = $UPLOAD_ADDR . '/' .$file_name;
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $img_path)) {
$is_upload = true;
}else{
$msg = '上传失败!';
}
}else{
$msg = '禁止保存为该类型文件!';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
首先看到黑名单,以及移动文件的函数,上篇笔记有很多这样的,比如.以及空格绕过
其次可以看到POST方法保存文件名,00截断(不过我的环境弄不了)
网上还有一种方法是Windows冒号截断;
到这里就结束了,针对后十关也为此次做一个总结:
绕过方法:
url%00截断;
二进制00截断POST;
上传图片马;
二次渲染绕过;
多线程发包;
Windows特性绕过;
防御思路:
文件重命名;
图片二次渲染;
白名单;
ps:只学到点皮毛,我还会再回来的!