【网络安全学习系列】Upload-labs通关手册

admin 2022年3月1日23:33:06评论192 views1字数 15173阅读50分34秒阅读模式

【网络安全学习系列】Upload-labs通关手册

Pass-01-js检查

这种直接禁用JS,或者burp改包等等都可以。

Pass-02-只验证Content-type

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name']           
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '文件类型不正确,请重新上传!';
        }
    } else {
        $msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
    }
}

抓包改Content-Type即可。

Pass-03-黑名单绕过

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array('.asp','.aspx','.php','.jsp');
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //收尾去空
 
        if(!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;           
            if (move_uploaded_file($temp_file,$img_path)) {
                 $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

不允许上传.asp,.aspx,.php,.jsp后缀文件,但是可以上传其他任意后缀

.php .phtml .phps .php5 .pht

前提是apachehttpd.conf中有如下配置代码

AddType application/x-httpd-php .php .phtml .phps .php5 .pht

或者上传.htaccess文件需要:1.mod_rewrite模块开启2.AllowOverrideAll

文件内容

<FilesMatch "shell.jpg">
  SetHandler application/x-httpd-php
</FilesMatch>

此时上传shell.jpg文件即可被当作php来解析。

或者

AddType application/x-httpd-php .jpg

另外基本上所有的黑名单都可以用Apache解析漏洞绕过。

Pass-04-.htaccess绕过

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2","pHp1",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //收尾去空
 
        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

过滤了各种罕见后缀

$deny_ext = array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2","pHp1",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf");

但是没有过滤.htaccess,用上面的方法即可。

Pass-05-大小写绕过

$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",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //首尾去空
 
        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

过滤了.htaccess,并且代码中后缀转换为小写被去掉了,因此我们可以上传Php来绕过黑名单后缀。(Linux没有特殊配置的情况下,这种情况只有win可以,因为win会忽略大小写)

Pass-06-空格绕过

Winxx.jpg[空格] xx.jpg.这两类文件都是不允许存在的,若这样命名,windows会默认除去空格或点此处会删除末尾的点,但是没有去掉末尾的空格,因此上传一个.php空格文件即可。

Pass-07-点绕过

没有去除末尾的点,因此与上面同理,上传.php.绕过。

Pass-08-::$DATA绕过

NTFS文件系统包括对备用数据流的支持。这不是众所周知的功能,主要包括提供与Macintosh文件系统中的文件的兼容性。备用数据流允许文件包含多个数据流。每个文件至少有一个数据流。在Windows中,此默认数据流称为:$ DATA。上传.php::$DATA绕过。(仅限windows)

Pass-09-.空格.绕过

move_upload_file的文件名直接为用户上传的文件名,我们可控。且会删除文件名末尾的点,因此我们可以结合Pass-7.php.空格.绕过。windows会忽略文件末尾的.和空格。另外这里发现$_FILES['upload_file']['name']获取的是文件名中/后面的字符串,本来还想用move_uploaded_file会忽略/.trick绕过。

Pass-10-双写绕过

敏感后缀替换为空,双写.pphphp绕过即可。

Pass-11-00截断

CVE-2015-2348影响版本:5.4.x<= 5.4.39, 5.5.x<= 5.5.23, 5.6.x <= 5.6.7expmove_uploaded_file($_FILES['name']['tmp_name'],"/file.phpx00.jpg");源码中move_uploaded_file中的save_path可控,因此00截断即可。

Pass-12-

Pass-11GET方式改成了POST,同理。

Pass-13-16-图片马

Pass-13-unpack

上传图片马。

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 = "上传出错!";
        }
    }
}

制作图片马

copy smi1e.jpg /b + shell.php /a shell.jpg

Pass-14-getimagesize()

Pass-13

Pass-15-exif_imagetype()

Pass-13

Pass-16-二次渲染绕过

$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的图片文件!";
    }
}

判断了后缀名、content-type,以及利用imagecreatefromgif判断是否为gif图片,最后再做了一次二次渲染,绕过方法可以参考先知的文章,写的很详细:https://xz.aliyun.com/t/2657jpgpng很麻烦,gif只需要找到渲染前后没有变化的位置,然后将php代码写进去,就可以了。

Pass-17-条件竞争

$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 = '上传出错!';
    }
}

可以看到文件先经过保存,然后判断后缀名是否在白名单中,如果不在则删除,此时可以利用条件竞争在保存文件后删除文件前来执行php文件。

利用bp不断发送上传包和请求包。成功执行命令:

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

因为moverename之前move操作进行了一次文件保存然后rename进行了一次更改文件名

因此我们可以通过条件竞争来上传图片马。

Pass-19-/.绕过

$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 . '文件夹不存在,请手工创建!';
    }
}

这里是我在Pass9提到的一个trickmove_uploaded_file会忽略掉文件末尾的/.。但是Pass9中的文件名是从$_FILES['upload_file']['tmp_name']中获取的,这里是用户可控的。因此构造当然也可以用move_uploaded_file函数的00截断漏洞绕过。

Pass-20-数组+/.绕过

$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 = "请选择要上传的文件!";
}

可以发现$file_name经过reset($file). '.' . $file[count($file) - 1];处理。

如果上传的是数组的话,会跳过$file = explode('.', strtolower($file));。并且后缀有白名单过滤

$ext = end($file);
$allow_suffix = array('jpg','png','gif');

而最终的文件名后缀取的是$file[count($file) - 1],因此我们可以让$file为数组。$file[0]smi1e.php/,也就是reset($file),然后再令$file[2]为白名单中的jpg。此时end($file)等于jpg$file[count($file)- 1]为空。而 $file_name = reset($file) . '.' . $file[count($file) -1];,也就是smi1e.php/.,最终move_uploaded_file会忽略掉/.,最终上传smi1e.php

解析漏洞

解析漏洞是服务器端中间件的问题,可以根据不同的解析漏洞应用在以上题目的情况中。


引用参考:

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

https://blog.csdn.net/mi900709/article/details/110919368

【网络安全学习系列】Upload-labs通关手册

长按-识别-关注

【网络安全学习系列】Upload-labs通关手册

Hacking黑白红

一个专注信息安全技术的学习平台

【网络安全学习系列】Upload-labs通关手册

点分享

【网络安全学习系列】Upload-labs通关手册

点收藏

【网络安全学习系列】Upload-labs通关手册

点点赞

【网络安全学习系列】Upload-labs通关手册

点在看


本文始发于微信公众号(Hacking黑白红):【网络安全学习系列】Upload-labs通关手册

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年3月1日23:33:06
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【网络安全学习系列】Upload-labs通关手册https://cn-sec.com/archives/500688.html

发表评论

匿名网友 填写信息