文件上传漏洞学习

文件上传漏洞学习

​ 文件上传漏洞是指用户上传了一个可执行脚本文件,并通过此文件获得了执行服器端命令的能力。在大多数情况下,文件上传漏洞一般是指上传 WEB 脚本能够被服务器解析的问题,也就是所谓的 webshell 问题。完成这一攻击需要这样几个条件,一是上传的文件能够被 WEB 容器执行,其次用户能从 WEB 上访问这个文件,最后,如果上传的文件被安全检查、格式化、图片压缩等功能改变了内容,则可能导致攻击失败。

文件上传漏洞类型判断

image-20220424160953061

以下记录upload-labs靶场通关记录

靶场包含的漏洞类型

image-20220424195258738

Pass-01 前端校验白名单

此关为前端验证文件类型白名单,源码如下

image-20220423184706312

function checkFile() {
    var file = document.getElementsByName('upload_file')[0].value;
    if (file == null || file == "") {
        alert("请选择要上传的文件!");
        return false;
    }
    //定义允许上传的文件类型
    var allow_ext = ".jpg|.png|.gif";
    //提取上传文件的类型
    var ext_name = file.substring(file.lastIndexOf("."));
    //判断上传文件类型是否允许上传
    if (allow_ext.indexOf(ext_name) == -1) {
        var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
        alert(errMsg);
        return false;
    }
}

法一 抓包改名绕过

​ 将写好的一句话木马 1.php 修改文件名为 1.jpg 然后上传,用bp抓包,在数据包中重新修改文件名为 1.php 完成绕过

image-20220423185114901

法二 改前端js

直接修改前端js函数,在控制台Console中重写checkFile函数,白名单中加入.php即可

image-20220423190048143

注:如果在element编辑框内修改代码,白名单中添加php,修改的代码并不会生效,因为chrome已经在内存里加载了这段代码,要重新加载代码只能靠刷新,然而刷新会丢失我们所作的修改。

Pass-02 后端校验Content-Type

此题仅为后端校验数据包的Content-Type,抓包修改即可

image-20220423191132210

Content-Type: image/jpeg

Pass-03 后端.asp,.aspx,.php,.jsp黑名单

此关为后端黑名单,不允许上传.asp,.aspx,.php,.jsp后缀文件

采用 .php3 等后缀绕过即可

注:需在配置文件中支持解析的php文件后缀名才可以解析

Pass-04 后端黑名单预处理不完善 .htaccess绕过

此关为后端黑名单,过滤大多数脚本后缀,但未过滤特殊文件 .htaccess (Apache服务器特有的分布式配置文件)

该文件可以理解为是一个httpd.conf

<FilesMatch "4.jpg">

  SetHandler application/x-httpd-php

</FilesMatch>

代码意思为 将 4.jpg 当作 php 文件执行

之后将写好的 4.php 文件重命名为 4.jpg 成功上传

使用蚁剑成功连接

image-20220424114638361

Pass-05 后端黑名单预处理不完善 .user.ini绕过

此次后端黑名单比上一关新增了 .htaccess

多次尝试未果后查看提示 上传目录存在php文件(readme.php)

根据网络题解先上传.user.ini

auto_prepend_file=5.jpg

再上传带有一句话木马的 5.jpg

这样readme.php 就可以自动包含5.jpg文件 蚁剑连接readme.php即可

但本地环境存在问题未成功,可能是服务器未使用CGI/FastCGI模式 ,暂时未解决

Pass-06 后端黑名单预处理不完善 大小写绕过

​ 与Pass-05不同为删除以下语句

$file_ext = strtolower($file_ext); //转换为小写

所以可以采用 .PHp 后缀直接绕过

Pass-07 后端黑名单预处理不完善 空格绕过

​ 与Pass-05不同为删除以下语句

 $file_ext = trim($file_ext); //首尾去空

所以上传 7.php 在bp中抓包加空格

image-20220424145330257

Pass-08 后端黑名单预处理不完善 .绕过

与Pass-05不同为删除以下语句

 $file_name = deldot($file_name);//删除文件名末尾的点

所以上传 8.php 在bp中抓包加 .

Pass-09 后端黑名单预处理不完善 ::$DATA绕过

与Pass-05不同为删除以下语句

$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA

::$DATA为Windows下NTFS文件系统的一个数据流属性,此内容后续文本会当做数据流处理,依次绕过后缀名检测

Pass-10 后端黑名单预处理不完善 . .绕过

过滤了.和空格但只过滤了一次

. .即可绕过

Pass-11 字符串替换,双写绕过

源码中

 $file_name = str_ireplace($deny_ext,"", $file_name);

即替换黑名单中字符串为空字符串,双写后缀 .pphphp

Pass-12 13 白名单 上传路径可控截断

利用条件:

  1. php版本小于5.3.4
  2. php的magic_quotes_gpc为OFF状态

%00截断原理

截断的核心,就是chr(0)这个字符 这个字符不为空(Null),也不是空字符(“”),更不是空格。 当程序在输出含有chr(0)变量时 chr(0)后面的数据会被停止,换句话说,就是误把它当成结束符,后面的数据直接忽略,这就导致漏洞产生

12源码如下

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

可以看到白名单过滤,且save_path是通过get获得的

抓包如图

image-20220428202959992

save_path末尾增加 7.php%00

filename改为 7.jpg 绕过后缀名白名单

合成后的路径为 ../upload/7.php%00/7.jpg

截断后等效为 ../upload/7.php

上传成功

13源码如下

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

与12相同,区别仅在save_path是通过post获得的

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

其他同理

Pass-14

源码如下

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

后端校验文件前2字节判断文件类型,与后缀名无关

JPEG (jpg),文件头:FFD8FF
PNG (png),文件头:89504E47
GIF (gif),文件头:47494638

所以在数据包中加上文件头绕过即可

image-20220429091842302

参考资料

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

https://ctf-wiki.org/web/php/php/#_4

https://blog.csdn.net/fjh1997/article/details/107588701

posted @ 2022-07-10 14:13  qweg_focus  阅读(255)  评论(0编辑  收藏  举报