文件上传漏洞 Upload-lab 实践(上)| 青训营

32 阅读6分钟

漏洞概念

文件上传漏洞是一种常见且危险的Web应用程序安全漏洞,它允许攻击者通过利用不当验证和过滤机制,向服务器上传恶意文件。这类漏洞通常出现在允许用户上传文件的功能中,如头像上传、文件分享、资源上传等场景。攻击者利用文件上传漏洞可以轻松地将包含恶意代码的文件传送到服务器上,从而在服务器端执行恶意操作,包括远程代码执行、服务器控制、数据泄露、用户敏感信息窃取等。

漏洞原理

文件上传漏洞的原理是由于Web应用程序在处理文件上传时,没有对上传的文件进行充分的验证和过滤,导致攻击者可以绕过限制,上传包含恶意代码的文件。这样的漏洞可能出现在允许用户上传文件的功能中,如头像上传、文件分享、论坛附件等场景。

文件上传漏洞的原理及可能导致漏洞的一些主要因素:

  1. 缺乏文件类型验证:Web应用程序在接收文件上传时,可能没有对文件类型进行验证,或者仅仅依靠文件的扩展名来判断文件类型。攻击者可以通过伪造文件扩展名或者使用特殊字符来绕过这种简单的验证机制。
  2. 欺骗MIME类型验证:有些Web应用程序可能会检查上传文件的MIME类型来判断文件类型,但MIME类型可以被伪造。攻击者可以修改请求头中的Content-Type字段,将上传文件的MIME类型伪装成允许的类型,从而绕过MIME类型验证。
  3. 绕过前端验证:某些前端验证措施只是对用户进行简单的提示,而没有真正地在服务器端进行验证。攻击者可以通过绕过前端验证,直接向服务器上传恶意文件。
  4. 没有文件名安全处理:有些应用程序可能将用户上传的文件名直接用于保存文件,而没有对文件名进行安全处理。攻击者可以在文件名中嵌入恶意代码,例如使用特殊字符或路径分隔符,导致文件被保存在非预期位置,从而实现攻击。
  5. 未对上传文件进行病毒扫描:某些Web应用程序在接收上传文件后,并没有对文件进行病毒扫描。攻击者可以利用这一点,上传含有病毒或恶意代码的文件,从而在服务器上执行恶意操作。
  6. 存储目录权限不当:上传的文件可能会被保存在Web服务器能够访问的目录下,攻击者可以通过上传包含恶意代码的文件,并直接通过URL访问该文件,从而执行攻击。

文件上传漏洞的原理是在文件上传过程中,缺乏对上传文件的充分验证和过滤,导致攻击者可以绕过限制,上传恶意文件。

漏洞利用过程

第一关

编写一句话木马,准备上传。

image.png

图 1-1. 编写一句话木马

尝试直接上传失败。

image.png

图 1-2. 直接上传
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;
    }
}

查看源码,前端对文件后缀名进行了检测,将文件后缀名修改为 .png,再进行抓包修改回 .php

image.png

图 1-3. 抓包修改

image.png

图 1-4. 成功上传并执行

第二关

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

image.png

图 2-1. 阅读源码

​ 观察源码我们可以发现,服务器会在前端验证文件的type字段。第一关我们直接修改文件后缀名的方法也会改变 Content-Type 字段为 image/png,所以使用第一关的方法也可以通关第二关。

image.png

图 2-2. 抓包修改

image.png

图 2-3. 成功上传并执行

第三关

$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代码。

本关需要使用 Apache 作为中间件,可能需要在 httpd.conf 中添加配置 AddType application/x-httpd-php .php .phtml .php3 .php4

该句代码的意思是将.php、.php3、.php4当作php文件。

将文件重命名为 shell.php4,上传。

image.png

图 3-1. 上传文件

image.png

图 3-2. 成功上传并执行

第四关

$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",".ini");
        $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.'/'.$file_name;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

后端过滤的文件后缀名更多了,我们可以上传.htaccess文件,将 png 文件当作 php 文件执行。

.htaccess是一个Apache Web服务器配置文件,它用于在特定目录中配置和覆盖服务器的设置。.htaccess文件可以包含一系列指令,用于控制访问权限、重定向URL、启用或禁用特定功能等。

<FilesMatch 'shell.png'>
	SetHandler application/x-httpd-php
</FilesMatch>

这段指令使用FilesMatch指令,它用于对特定文件进行配置。在这里,shell.png是被匹配的文件名。意味着对于该目录中名为shell.png的文件,将会应用下面的指令。

SetHandler application/x-httpd-php是一个处理器指令,它指示Apache服务器将该文件当作PHP脚本来处理,而不是简单地将其作为静态文件提供给用户。这意味着如果用户尝试访问shell.png文件,服务器会将其当作PHP代码来解析和执行,而不是直接返回图片内容。

image.png

图 4-1. 上传.htaccess

image.png

图 4-2. 上传 shell.png

image.png

图 4-3. 成功上传并执行