Upload - Labs (下)
阅读原文时间:2021年10月26日阅读:4

Pass - 11:

1.查看源代码,发现进行了一次对后缀名替换成空格,因此考虑双写绕过,

2.上传成功,

关键代码:

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$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","ini");

    $file\_name = trim($\_FILES\['upload\_file'\]\['name'\]);  
    $file\_name = str\_ireplace($deny\_ext,"", $file\_name);  
    $temp\_file = $\_FILES\['upload\_file'\]\['tmp\_name'\];  
    $img\_path = UPLOAD\_PATH.'/'.$file\_name;  
    if (move\_uploaded\_file($temp\_file, $img\_path)) {  
        $is\_upload = true;  
    } else {  
        $msg = '上传出错!';  
    }  
} else {  
    $msg = UPLOAD\_PATH . '文件夹不存在,请手工创建!';  
}  

}

Pass -12:

1.查看源代码,看到是白名单判断,但是$img_path直接拼接尝试%00截断,但在进行move_uploaded_file前。利用$_GET['save_path']和随机时间函数进行拼接,拼接成文件存储路径。

因此构造文件存储路径利用了_GET传入:(注意%00要进行编码)

2.上传成功

查看源码:

$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类型文件!";  
}  

}

补充:

截断条件:
1、php版本小于5.3.4
2、php.ini的magic_quotes_gpc为OFF状态

Pass - 13:

同Pass - 12,唯一的区别就是GET换成了POST,

但这次需要在二进制中进行修改,因为post不会像get对%00进行自动解码。

关键代码:

$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 = $_POST['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类型文件!";  
}  

}

Pass - 14:

1.任务要求上传图片马,

2.制作图片马,查看源代码可知,通过读文件的前2个字节判断文件类型,也可以在一句话文件内容前面加图片的标识例如GIF89a。

3.上传成功,

关键代码:

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\_PATH."/".rand(10, 99).date("YmdHis").".".$file\_type;  
    if(move\_uploaded\_file($temp\_file,$img\_path)){  
        $is\_upload = true;  
    } else {  
        $msg = "上传出错!";  
    }  
}  

}

补充:

unpack() 函数从二进制字符串对数据进行解包。

Pass - 15:

1.查看一下提示,

2.查看源代码,用getimagesize获取文件类型,还是直接就可以利用图片马就可进行绕过,

关键代码:

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)>=0){
return $ext;
}else{
return false;
}
}else{
return false;
}
}

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$res = isImage($temp_file);
if(!$res){
$msg = "文件未知,上传失败!";
}else{
$img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").$res;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传出错!";
}
}
}

补充:

getimagesize() 函数用于获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息。

语法格式:
array getimagesize ( string $filename [, array &$imageinfo ] )

getimagesize() 函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型及图片高度与宽度。

Pass - 16:

1.查看提示,

2.查看源代码,用到php_exif模块来判断文件类型,还是直接就可以利用图片马就可进行绕过

关键代码:

function isImage($filename){
//需要开启php_exif模块
$image_type = exif_imagetype($filename);
switch ($image_type) {
case IMAGETYPE_GIF:
return "gif";
break;
case IMAGETYPE_JPEG:
return "jpg";
break;
case IMAGETYPE_PNG:
return "png";
break;
default:
return false;
break;
}
}

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$res = isImage($temp_file);
if(!$res){
$msg = "文件未知,上传失败!";
}else{
$img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$res;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传出错!";
}
}
}

补充:

exif_imagetype — 判断一个图像的类型

exif_imagetype ( string $filename ) : int
exif_imagetype() 读取一个图像的第一个字节并检查其签名。

本函数可用来避免调用其它 exif 函数用到了不支持的文件类型上或和 $_SERVER['HTTP_ACCEPT'] 结合使用来检查浏览器是否可以显示某个指定的图像。

Pass - 17:

(重点!!!)

1.查看源代码,

判断文件类型Content-Type:

$filetype = $_FILES['upload_file']['type'];

获取文件后缀扩展名:

$fileext= substr(strrchr($filename,"."),1);

然后根据不同的文件后缀扩展名进行二次渲染:

jpg:
$im = imagecreatefromjpeg($target_path); #使用上传的图片生成新的图片(二次渲染)
png:
$im = imagecreatefrompng($target_path);
gif:
$im = imagecreatefromgif($target_path);

2.上传方法与前几关类似。

关键代码:

$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\_PATH.'/'.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格式的图片!";  
            @unlink($target\_path);  
        }else{  
            //给新图片指定文件名  
            srand(time());  
            $newfilename = strval(rand()).".jpg";  
            //显示二次渲染后的图片(使用用户上传图片生成的新图片)  
            $img\_path = UPLOAD\_PATH.'/'.$newfilename;  
            imagejpeg($im,$img\_path);  
            @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格式的图片!";  
            @unlink($target\_path);  
        }else{  
             //给新图片指定文件名  
            srand(time());  
            $newfilename = strval(rand()).".png";  
            //显示二次渲染后的图片(使用用户上传图片生成的新图片)  
            $img\_path = UPLOAD\_PATH.'/'.$newfilename;  
            imagepng($im,$img\_path);

            @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格式的图片!";  
            @unlink($target\_path);  
        }else{  
            //给新图片指定文件名  
            srand(time());  
            $newfilename = strval(rand()).".gif";  
            //显示二次渲染后的图片(使用用户上传图片生成的新图片)  
            $img\_path = UPLOAD\_PATH.'/'.$newfilename;  
            imagegif($im,$img\_path);

            @unlink($target\_path);  
            $is\_upload = true;  
        }  
    } else {  
        $msg = "上传出错!";  
    }  
}else{  
    $msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";  
}  

}

补充:

imagecreatefromgif — 由文件或 URL 创建一个新图象。
说明
imagecreatefromgif ( string $filename ) : resource

imagecreatefromgif() 返回一图像标识符,代表了从给定的文件名取得的图像。

参考:

https://xz.aliyun.com/t/2657

Pass - 18:

思路:

查看代码,需要代码审计,查看源代码,先move_uploaded_file函数将上传文件临时保存,再进行判断,如果不在白名单里则unlink删除,在的话就rename重命名,所以这里存在条件竞争。

因此可以上传aaa.php只需要在它删除之前访问即可,可以利用burp的intruder模块不断上传,然后不断地访问刷新该地址即可。

步骤:

1.用burp开启两个intruder模块,一个用于重复上传,另一个用于重复访问。先设置上传请求,记住此处的文件名,等下要用来拼接访问请求的url。

因为此处没有什么参数需要爆破,只是需要重复发起请求,所以payload设置为Null payloads,设置访问次数10000次,线程50个

3.设置访问请求:

①浏览器构造请求url:http://127.0.0.1/upload-labs/upload/aaa.php,进行访问,然后用burp抓包 ②burp抓包后发送至intruder模块,然后设置payload,这一步和上传请求设置差不多,都是Null payloads、10000次、50个线程

4.设置好两个模块后同时启动,观察结果,因为我们传入的php代码是phpinfo();,所以如果访问成功的话,会返回php的配置信息。

5.大部分访问结果是状态码是404。
因为条件竞争绕过存在一定概率,实践中如果一次不成功,可以多试几次。

关键代码:

$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_PATH . '/' . $file_name;

if(move\_uploaded\_file($temp\_file, $upload\_file)){  
    if(in\_array($file\_ext,$ext\_arr)){  
         $img\_path = UPLOAD\_PATH . '/'. rand(10, 99).date("YmdHis").".".$file\_ext;  
         rename($upload\_file, $img\_path);  
         $is\_upload = true;  
    }else{  
        $msg = "只允许上传.jpg|.png|.gif类型文件!";  
        unlink($upload\_file);  
    }  
}else{  
    $msg = '上传出错!';  
}  

}

补充:

strrpos() 函数查找字符串在另一字符串中最后一次出现的位置(区分大小写)。

注释:strrpos() 函数是区分大小写的。

相关函数:

  • strpos() - 查找字符串在另一字符串中第一次出现的位置(区分大小写)
  • stripos()- 查找字符串在另一字符串中第一次出现的位置(不区分大小写)
  • strripos() - 查找字符串在另一字符串中最后一次出现的位置(不区分大小写)

move_uploaded_file() 函数把上传的文件移动到新位置。

如果成功该函数返回 TRUE,如果失败则返回 FALSE。
语法
move_uploaded_file(file,newloc)

注释:该函数仅用于通过 HTTP POST 上传的文件。

注释:如果目标文件已经存在,将会被覆盖。

Pass - 19:

思路:

查看源代码,先将上传的文件保存(move函数),再rename重命名一下。所以也存在条件竞争,绕过方法和上面Pass-18差不多

关键代码:

//index.php
$is_upload = false;
$msg = null;
if (isset($_POST['submit']))
{
require_once("./myupload.php");
$imgFileName =time();
$u = new MyUpload($_FILES['upload_file']['name'], $_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['size'],$imgFileName);
$status_code = $u->upload(UPLOAD_PATH);
switch ($status_code) {
case 1:
$is_upload = true;
$img_path = $u->cls_upload_dir . $u->cls_file_rename_to;
break;
case 2:
$msg = '文件已经被上传,但没有重命名。';
break;
case -1:
$msg = '这个文件不能上传到服务器的临时文件存储目录。';
break;
case -2:
$msg = '上传失败,上传目录不可写。';
break;
case -3:
$msg = '上传失败,无法上传该类型文件。';
break;
case -4:
$msg = '上传失败,上传的文件过大。';
break;
case -5:
$msg = '上传失败,服务器已经存在相同名称文件。';
break;
case -6:
$msg = '文件无法上传,文件不能复制到目标目录。';
break;
default:
$msg = '未知错误!';
break;
}
}

//myupload.php
class MyUpload{
……
……
……
var $cls_arr_ext_accepted = array(
".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
".html", ".xml", ".tiff", ".jpeg", ".png" );

……
……
……
/** upload()
**
** Method to upload the file.
** This is the only method to call outside the class.
** @para String name of directory we upload to
** @returns void
**/
function upload( $dir ){

$ret = $this->isUploadedFile();

if( $ret != 1 ){  
  return $this->resultUpload( $ret );  
}

$ret = $this->setDir( $dir );  
if( $ret != 1 ){  
  return $this->resultUpload( $ret );  
}

$ret = $this->checkExtension();  
if( $ret != 1 ){  
  return $this->resultUpload( $ret );  
}

$ret = $this->checkSize();  
if( $ret != 1 ){  
  return $this->resultUpload( $ret );  
}

// if flag to check if the file exists is set to 1

if( $this->cls\_file\_exists == 1 ){

  $ret = $this->checkFileExists();  
  if( $ret != 1 ){  
    return $this->resultUpload( $ret );  
  }  
}

// if we are here, we are ready to move the file to destination

$ret = $this->move();  
if( $ret != 1 ){  
  return $this->resultUpload( $ret );  
}

// check if we need to rename the file

if( $this->cls\_rename\_file == 1 ){  
  $ret = $this->renameFile();  
  if( $ret != 1 ){  
    return $this->resultUpload( $ret );  
  }  
}

// if we are here, everything worked as planned :)

return $this->resultUpload( "SUCCESS" );

}
……
……
……
};

Pass - 20:

(重要!!!)

1.查看源代码,要绕过.jpg,

2.move_uploaded_file()函数会忽视文件名末尾的 /.

因此可以构造aaa.php/.进行绕过,

post: save_name = aaa.php%00.jpg
post: save_name = aaa.php/.

3.上传成功,

关键代码:

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$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)) {  
        $temp\_file = $\_FILES\['upload\_file'\]\['tmp\_name'\];  
        $img\_path = UPLOAD\_PATH . '/' .$file\_name;  
        if (move\_uploaded\_file($temp\_file, $img\_path)) {  
            $is\_upload = true;  
        }else{  
            $msg = '上传出错!';  
        }  
    }else{  
        $msg = '禁止保存为该类型文件!';  
    }

} else {  
    $msg = UPLOAD\_PATH . '文件夹不存在,请手工创建!';  
}  

}

补充:

pathinfo() 函数
定义和用法
pathinfo() 函数以数组的形式返回关于文件路径的信息。

返回的数组元素如下:
[dirname]: 目录路径
[basename]: 文件名
[extension]: 文件后缀名
[filename]: 不包含后缀的文件名

语法
pathinfo(path,options)

path

必需。规定要检查的路径。

options

可选。规定要返回的数组元素。默认是 all。

可能的值:

  • PATHINFO_DIRNAME - 只返回 dirname

  • PATHINFO_BASENAME - 只返回 basename

  • PATHINFO_EXTENSION - 只返回 extension

  • PATHINFO_FILENAME - 只返回 filename

     如果不是请求所有的元素,则 pathinfo() 函数返回字符串

Pass - 21:

1.看查看源代码,第5-6行先进行了一个Content-Type判断

$allow_type = array('image/jpeg','image/png','image/gif');
if(!in_array($_FILES['upload_file']['type'],$allow_type)){

2.10-13行,如果save_name是字符串的话就通过explode函数,将post进去的save_name转成小写后按’.'打散成数组。

$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
if (!is_array($file)) {
$file = explode('.', strtolower($file));

3.第20行,由于后面filename构成的过程中由于file[count(file[count(file[count(file) - 1]的作用,导致$file[1] = NULL,所以构造文件名后相当于直接就是xx.php/.,

$file_name = reset($file) . '.' . $file[count($file) - 1]; 

关键代码:

$is_upload = false;
$msg = null;
if(!empty($_FILES['upload_file'])){
//检查MIME
$allow_type = array('image/jpeg','image/png','image/gif');
if(!in_array($_FILES['upload_file']['type'],$allow_type)){
$msg = "禁止上传该类型文件!";
}else{
//检查文件名
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
if (!is_array($file)) {
$file = explode('.', strtolower($file));
}

    $ext = end($file);  
    $allow\_suffix = array('jpg','png','gif');  
    if (!in\_array($ext, $allow\_suffix)) {  
        $msg = "禁止上传该后缀文件!";  
    }else{  
        $file\_name = reset($file) . '.' . $file\[count($file) - 1\];  
        $temp\_file = $\_FILES\['upload\_file'\]\['tmp\_name'\];  
        $img\_path = UPLOAD\_PATH . '/' .$file\_name;  
        if (move\_uploaded\_file($temp\_file, $img\_path)) {  
            $msg = "文件上传成功!";  
            $is\_upload = true;  
        } else {  
            $msg = "文件上传失败!";  
        }  
    }  
}  

}else{
$msg = "请选择要上传的文件!";
}

参考:

https://blog.csdn.net/weixin_41182861/article/details/105546654