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

漏洞危害

文件上传漏洞的危害非常严重,攻击者通过利用这类漏洞可以对受影响的Web应用程序造成严重的后果。文件上传漏洞的主要危害有:

  1. 远程代码执行:文件上传漏洞可能导致远程代码执行,这意味着攻击者可以在服务器上执行任意恶意代码。通过上传包含恶意代码的文件,攻击者可以在服务器端执行系统命令,完全控制目标服务器。这样的攻击可能导致服务器崩溃、敏感数据泄露、用户信息被窃取等严重后果。
  2. 服务器控制:通过上传恶意文件,攻击者可以获取对服务器的非授权访问,进而完全控制服务器。攻击者可以修改服务器配置、删除重要文件、安装后门、远程访问服务器等,对服务器进行长期控制。
  3. 数据泄露:攻击者可以上传恶意脚本或web shell,通过执行脚本获取服务器上的敏感数据,如数据库信息、用户凭据、用户个人信息等。泄露敏感数据可能导致隐私泄露、用户信任受损等问题。
  4. 网站篡改:攻击者可以上传恶意文件,修改网站内容,展示虚假信息或广告,损害网站的声誉和可信度。
  5. 恶意传播:攻击者可以上传恶意文件,将Web应用程序作为恶意文件的传播媒介,通过操纵上传文件在其他用户间传播恶意内容。
  6. 服务拒绝:攻击者可以通过上传大量文件或恶意文件,占用服务器资源,导致服务器性能下降,甚至服务拒绝,影响正常用户的访问。
  7. 网站黑名单:当攻击者使用Web应用程序上传恶意文件或执行违法活动时,Web应用程序的IP地址可能会被列入黑名单,导致其无法正常访问其他网站或服务。

综上所述,文件上传漏洞的危害严重,可能导致服务器受到完全控制、数据泄露、网站篡改、服务拒绝等问题。

修护思路

修复文件上传漏洞是保障Web应用程序安全的重要措施。修复文件上传漏洞的一些主要思路和建议有:

  1. 文件类型验证:在接收上传文件时,对文件类型进行验证是非常重要的。开发者应该明确指定允许上传的文件类型,并严格校验上传文件的MIME类型,而不仅仅依赖于文件的扩展名来判断文件类型。可以使用白名单的方式,只允许特定的文件类型通过验证。
  2. MIME类型验证:除了验证文件类型,还应该对文件的MIME类型进行验证。攻击者可能会伪造请求头中的Content-Type字段,欺骗服务器,因此服务器应该对上传文件的MIME类型进行独立验证,确保其与文件实际内容相符。
  3. 文件名安全处理:不应该将用户上传的文件名直接用于保存文件,而是对文件名进行安全处理。最好使用随机生成的文件名,并确保文件名不包含特殊字符和路径分隔符,以防止目录遍历攻击。
  4. 禁止执行权限:上传的文件应该被存储在不可执行的目录下,确保恶意文件不会被执行。对于上传的图片等非可执行文件,最好禁止其执行权限。
  5. 病毒扫描:在接收上传文件后,对文件进行病毒扫描是一种有效的安全措施。可以使用常见的病毒扫描引擎对上传的文件进行扫描,确保不会上传含有病毒或恶意代码的文件。
  6. 安全的存储位置:上传的文件应该存储在非Web可访问目录下,防止攻击者直接访问上传的文件。开发者可以在配置文件中指定存储目录,并确保Web服务器没有权限直接访问该目录。
  7. 后端验证:不仅仅依赖前端的验证,后端服务器也应该对上传文件进行验证。即使前端验证被绕过,后端也能进行有效的检查和过滤。
  8. 定期检查:定期检查服务器上的上传文件,及时发现异常或可疑文件,并进行处理。删除不必要的上传文件,确保服务器的安全性。
  9. 安全上传组件:使用经过安全测试和验证的上传组件或库,这些组件通常会提供更多的安全特性和选项,有助于预防文件上传漏洞。
  10. 安全审计:进行安全审计,定期对Web应用程序进行安全测试和漏洞扫描,及时发现并修复潜在的文件上传漏洞。

​ 修复文件上传漏洞需要综合多种措施,从前端到后端,从验证到存储,确保上传文件的安全性和合法性。开发者应该重视文件上传功能的安全性,并在设计和实现过程中遵循安全最佳实践。定期对Web应用程序进行安全审计和漏洞扫描,及时修复潜在的漏洞,保障Web应用程序的安全性。

漏洞利用过程

第五关

php 复制代码
$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",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".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",".htaccess");
        $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 文件,查看提示,上传目录存在php文件(readme.php)。也就是上传目录下已经有 PHP 文件了。

因为 .ini 后缀的文件可以上传,所以我们可以上传一个.user.ini文件来改变服务器配置。

具体方法:上传一个.user.ini,再上传一个图片马,包含起来进行getshell。不过前提是含有 .user.ini 的文件夹下需要有正常的php文件,否则也不能包含了(正常的文件即本身自带的 readme.php)。

.user.ini是一个PHP配置文件,用于在特定目录中配置和覆盖PHP的运行时设置。该文件类似于.htaccess文件,但是作用范围更为局限,只对所在目录及其子目录生效。.user.ini文件可以包含一系列PHP配置指令,用于修改PHP运行时的各种设置。

ini 复制代码
auto_prepend_file = shell.png

auto_prepend_file是一个PHP配置指令,用于指定一个在每个PHP文件执行之前自动包含的文件。在这里,shell.png是被指定的文件名,意味着在每个PHP文件执行之前,PHP会自动包含shell.png这个文件。

这样的配置可能是一个文件包含漏洞的后果,攻击者可能成功地上传一个伪装成图片的恶意PHP脚本,然后通过.user.ini文件中的指令,让PHP在每个执行的PHP文件之前都自动包含该文件。这样,恶意代码将被执行,并可能导致远程代码执行或其他安全问题。

第一步:首先上传一个 .user.ini 文件

第二步:然后再上传一个一句话木马文件,名字需要与 .user.ini 配置的文件名相同


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

第六关

php 复制代码
$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",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".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",".htaccess",".ini");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $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 = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

经过与第五关对比我们发现没有过滤大小写

缺少代码

php 复制代码
$file_ext = strtolower($file_ext); //转换为小写

将文件后缀名改为 .Php 绕过。


图 6-1. 抓包修改


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

第七关

php 复制代码
$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",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".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",".htaccess",".ini");
        $file_name = $_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
        
        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 = '此文件不允许上传';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

没有进行首尾去空

缺少代码

php 复制代码
$file_ext = trim($file_ext); //首尾去空

我们可以进行空格绕过,修改后缀名为 .php[空格]

空格绕过修改文件后缀名为 .php 的原理是利用文件系统对文件名的解析方式。在某些文件系统中,如果文件名包含空格,空格后的内容将被视为文件名的一部分,而不会被视为后缀名。这就意味着,当文件名包含空格并以 .php 结尾时,服务器可能会将其当作 .php 文件来解析,从而执行其中的 PHP 代码。


图 7-1. 抓包修改


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

第八关

php 复制代码
$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",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".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",".htaccess",".ini");
        $file_name = trim($_FILES['upload_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 . '文件夹不存在,请手工创建!';
    }
}

缺少代码

php 复制代码
 $file_name = deldot($file_name)

没有删除文件名末尾后的点

所以进行点绕过。

修改文件后缀名为 .php.

修改文件后缀名为 .php. 利用的是某些操作系统和Web服务器的解析机制。在一些操作系统和服务器配置中,当文件名以 .php. 结尾时,服务器可能会将其当作 .php 文件来解析,而忽略掉后面的多余点号。

假设一个Web应用程序允许用户上传图片文件,并对文件名进行简单的验证,只检查是否以 .jpg.png 等图片后缀名结尾。攻击者可以上传一个名为 image.jpg 的图片文件,而不会被拦截。然后,攻击者在文件名后面添加多余的点号和 .php,即 image.jpg..php,这样文件名看起来仍然是 image.jpg,但实际上是 image.jpg..php。由于服务器可能会忽略掉后面的多余点号,因此它可能会将该文件当作 .php 文件来解析。


图 8-1. 抓包修改


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

第九关

php 复制代码
$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",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".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",".htaccess",".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 = 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 = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

缺少代码

php 复制代码
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA

上传后缀名添加::$DATA

::$DATA是一个流传输,可以把后面的数据当成流处理和.空格类似。在某些操作系统中,如果在文件名后添加 "::$DATA",系统会将它视为一个特殊的数据流,而不是文件的一部分。例如,Windows操作系统中的NTFS文件系统支持使用 "::$DATA" 表示一个特殊的数据流,这种数据流通常不会被用户直接访问。而在一些Web服务器中,可能没有对上传文件名进行严格的处理,导致可以通过在文件名后添加 "::$DATA\" 来绕过文件类型检查。


图 9-1. 抓包修改


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

相关推荐
CallBack8 个月前
Typora+PicGo+阿里云OSS搭建个人图床,纵享丝滑!
前端·青训营笔记
Taonce1 年前
站在Android开发者的角度认识MQTT - 源码篇
android·青训营笔记
AB_IN1 年前
打开抖音会发生什么 | 青训营
青训营笔记
monster1231 年前
结营感受(go) | 青训营
青训营笔记
翼同学1 年前
实践记录:使用Bcrypt进行密码安全性保护和验证 | 青训营
青训营笔记
hu1hu_1 年前
Git 的正确使用姿势与最佳实践(1) | 青训营
青训营笔记
星曈1 年前
详解前端框架中的设计模式 | 青训营
青训营笔记
yibao1 年前
高质量编程与性能调优实战 | 青训营
青训营笔记
小金先生SG1 年前
阿里云对象存储OSS使用| 青训营
青训营笔记