论文件上传绕过的各种姿势(二)

2019-11-08 约 907 字 预计阅读 5 分钟

声明:本文 【论文件上传绕过的各种姿势(二)】 由作者 未完成の歌 于 2019-11-08 09:17:16 首发 先知社区 曾经 浏览数 69 次

感谢 未完成の歌 的辛苦付出!

接着上篇的总结 (= ̄ω ̄=)
Less-11
这关采用的防御手法是白名单过滤,只允许上传jpg、png和gif类型,并且将上传的文件给重命名为了白名单中的后缀
查看源码:

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

处理上传文件的方式

$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

看起来这样防御并没有什么问题,但是这一关上传目录是可控的,所以可以先上传一个后缀名为.jpg,然后修改上传目录为.php后缀,之后在.php后使用截断后面的拼接内容,注意这里需要关掉magic_quotes_gpc这个php扩展,否则00会被转义
$_GET['save_path']这里使用00截断."/".rand(10, 99).date("YmdHis").".".$file_ext;
注意这里的00字符因为在url的GET参数中,所以需用进行url编码

  • 截断的条件
    1)php版本必须小于5.3.4
    2)打开php的配置文件php-ini,将magic_quotes_gpc设置为Off

  • 关闭magic_quotes_gpc函数:
    在php.ini文件内找到
    magic_quotes_gpc = On
    将其改为
    magic_quotes_gpc = Off



配置好后,上传webshell.jpg,抓包,添加webshell.php%00


上传成功

Less-12
和Less-11不同的是这次的save_path是通过post传进来的,我们还利用00截断,但这题需要在十六进制中进行修改,因为post不会像get对%00进行自动解码。
步骤:上传webshell.jpg 然后 send to repeater

添加文件 wenshell.php(空格)

空格的十六进制为20然后找到20 改为00

然后点击send 发包就好了

发包成功。

Less-13
PS:

这关需要将服务器版本改到PHP5.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 = "上传出错!";
        }
    }
}

发现主要是取上传文件的头两个字节判断文件类型,因此直接上传图片马即可;
图片制作方法:copy 1.jpg /b + shell.php /a webshell.jpg(图片一句话木马为:<?php phpinfo(); ?>
解释:参数/b指定以二进制格式复制、合并文件(图片),参数/a指定以ASCII格式复制、合并文件(php文件)

制作成功

然后直接上传

上传成功,得到文件名,如果是上传的是一句话木马就能连接菜刀了;我们上传的是图片码,可以利用文件包含漏洞进行测试:
先新建一个包含文件漏洞的页面upload.php,将该文件放在根目录下:

<?php
$file = $_GET[ 'page' ];
include($file);
?>

测试使用,包含成功
http://127.0.0.1/upload-labs-master/upload/include.php?page=文件名

另外有时候对文件大小也有限制,所以绕过文件幻数最合适的方式是利用16进制编辑器自己制作一个伪图片马,这里利用winhex创建shell.jpg伪图片马

Less-14
查看关键代码:

$types = '.jpeg|.png|.gif';
     if(file_exists($filename)){
        $info = getimagesize($filename);
        $ext = image_type_to_extension($info[2]);1234

这里用getimagesize获取文件类型,还是直接就可以利用图片马就可进行绕过,绕过方法同Less13 ,这里就不演示了
知识补充:

array getimagesize ( string $filename [, array &$imageinfo ] )
getimagesize() 函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型和一个可以用于普通 HTML 文件中 IMG 标记中的 height/width 文本字符串。

如果不能访问 filename 指定的图像或者其不是有效的图像,getimagesize() 将返回 FALSE 并产生一条 E_WARNING 级的错误。

Less-15
关键源码:

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

利用php_exif模块判断文件类型,还是直接利用图片马就可以绕过。(查看下你的php_exif模块是否打开,没有的话就勾上)

然后直接上传图片马,仍和 less-13一样

上传成功。

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

通过源码可以看到对文件后缀名和MIME类型进行了检查,而且用到了php的imagecreatefromjpegimagecreatefrompngimagecreatefromgif这几个图片处理函数对上传的图片进行了二次渲染生成了新的图片,所以如果在这里上传的是一个普通的图片马,虽然图片马可以上传成功,但是上传的图片马在经过二次渲染后,图片尾部的php代码就会被删除掉,所以在这里不能使用直接在图片尾部添加一句话木马的方式去合成图片马。但是这一关的代码有一个明显的逻辑漏洞,如果这几个二次渲染函数处理的不是一个图片,就会使这几个函数报错,因为这几个二次渲染的函数只会去处理一个图片内部格式正确的图片,所以在这里只需要上传一个后缀名为jpg、png、gif的一句话木马,这样的话上传的一句话木马会绕过后缀名和MIME类型的检查,通过move_uploaded_file上传至服务器,但是遇到二次渲染时,由于上传的不是一个真正的图片,所以二次渲染函数在处理时会因为图片的内部格式报错,从而突破了对图片的二次渲染,这时候页面虽然会显示图片格式不允许,但是上传的一句话木马已经上传到了服务器

上传后缀名为jpg、png、gif之一的图片马,
可以看到上传成功,但是我的并没有成功解析;

wenHex打开发现并查找不到我们的木马

无奈 翻翻大佬们的blog发现是在二次渲染的时候被替换了,至于解决办法请看这位大佬写的点这我就不再描述(●'◡'●)

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

这题我是蒙蔽的,好在看了大佬的操作才后知道了些;这里先将文件上传到服务器,然后通过rename修改名称,再通过unlink删除文件,因此可以通过条件竞争的方式在unlink之前,访问webshell。

  • unlink() 函数是用来删除文件的

  • 什么是条件竞争:

条件竞争漏洞是一种服务器端的漏洞,由于服务器端在处理不同用户的请求时是并发进行的,因此,如果并发处理不当或相关操作逻辑顺序设计的不合理时,将会导致此类问题的发生。

  • 首先在burp中不断发送上传webshell的数据包:
  • 然后不断在浏览器中访问,发现通过竞争可以访问到:

具体的操作方法我也没掌握,等学习到了就补上

Less-18
本关需要上传图片马,查看代码

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

本关对文件后缀名做了白名单判断,然后会一步一步检查文件大小、文件是否存在等等,将文件上传后,然后再 $ret = $this->renameFile();,进行了一次更改文件名;同样存在条件竞争的漏洞。可以不断利用burp发送上传图片马的数据包,由于条件竞争,程序会出现来不及rename的问题,从而上传成功。

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

分析,move_uploaded_file()函数中的img_path是由post参数save_name控制的,
因此可以在save_name利用00截断绕过:
首先上传一个图片马,后缀名改为php+jpg


然后再16进制中把加号的2b 改为 00 fordward发包就行了。然而我的burp不知道为啥发不了,唉

然后我又发现,这题可以直接加空格绕过


测试成功。

好了,到这暂时就结束了至于Less20,需要审计代码,目前自己还做不了。

关键词:[‘安全技术’, ‘WEB安全’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now