基本概念

  1. 定义

    在web程序的文件上传功能中,因为没有对用户上传的文件进行充分的安全检查,导致攻击者能够上传恶意文件并被服务端解析执行,从而执行恶意的操作。

  2. 漏洞成因

    • 没有严格验证上传文件的类型,比如对文件后缀名(扩展名)、MIMETYPE(文件类型)、文件头没有做严格的验证。
    • 没有对文件内容进行安全检查,比如检查文件中是否包含恶意脚本或代码
    • 没有对上传文件做权限限制,比如没有将上传文件目录设置为不可执行权限
  3. 利用条件

    ① 恶意文件可以成功上传

    ② 恶意文件上传后的路径可以被获取到

    ③ 恶意文件可被访问或执行

  4. 危害

    可能会导致攻击者获得对服务器的完全控制,获取敏感数据,进行恶意篡改或植入病毒,甚至发起大规模的攻击。为了防止这些危害,企业和开发者应加强文件上传功能的安全性,采用严格的文件类型验证、内容检查、权限控制等措施。

文件上传和检测流程

1)用户通过Web表单选择文件进行上传,通常使用<input type="file"> HTML元素来允许用户选择文件。

  • 在这里通常客户端 javascript 会对文件进行拓展名检测、大小限制等

2)点击提交按钮后,文件会以multipart/form-data格式的请求数据发送到服务器。

3)服务器解析请求,提取出其中的文件内容。

4)服务器对文件进行验证

  • 服务端 MIME 类型检测(检测 Content-Type 内容)
  • 服务端目录路径检测(检测跟 path 参数相关的内容)
  • 服务端文件扩展名检测(检测跟⽂件 extension 相关的内容)
  • 服务端文件内容检测(检测内容是否合法或含有恶意代码)

5)生成唯一文件名并存储到指定位置,可能生成一个文件URL。

6)服务器返回响应给客户端,告知用户上传结果

绕过方法

前端JS绕过

  1. 如何判断是否是前端验证

    抓包监听,如果上传文件的时候还没有抓取到数据包,浏览器就提示文件类型不正确的话,多半就是被前端校验拦截了。

  2. 前端 JS 绕过方法

    1)禁用 JS 绕过客户端检验:使用 javascript switcher 插件

    2)抓包修改请求包,绕过客户端校验

    • 修改文件后缀为 png(绕过前端校验),BP 拦截包之后再改回 php
    • 修改 MIMETYPE(Content-Type)为图片类型 image/jpeg

    3)修改前端中文件上传验证逻辑的 JS 代码绕过

服务端绕过

后端文件拓展名检测绕过

黑名单规则不全面

黑名单不全面,仅仅对 .asp,.aspx,.php,.jsp 等常见后缀的脚本拓展名进行限制,不包括 .phtml, .php5, .phps, .pht 等其他比较冷门的拓展名

后缀名大小写绕过

如果源码中没有将后缀名转换成统一的大写或小写

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

可以通过大小写绕过,例如上传后缀名为 .pHP 的文件,因为 Windows 操作系统大小写不敏感,所以文件仍然会被当成 .PHP 文件解析。

.htaccess 绕过

.htaccess 文件是 Apache 服务器中的一种配置文件,可以用来控制资源访问行为

前提:

  • 允许上传 .htaccess 文件
  • apache 开启 rewite 重写模块(一般默认开启)

.htaccess 文件中写入下面的规则,可以使得这些文件被当作PHP执行

1
2
3
<FilesMatch "png">
AddType application/x-httpd-php .jpg .jpeg .png
</FileMatch>

双写后缀绕过

服务端将黑名单的后缀名替换为空,但仅进行一次,上传 .phphpp 后缀,

白名单情况下,有时候也可以使用双写绕过,如image.jpg.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$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 = trim($_FILES['upload_file']['name']);
$file_name = str_ireplace($deny_ext,"", $file_name);
$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 = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}

00截断

原理NULL 字符(%00\x00),在许多编程语言和操作系统中表示字符串的结束符,所以程序处理带有 NULL 字符的字符串时,通常会认为该字符后面的部分是无效的

例子:上传文件 malicious.php%00.jpg

在文件拓展名验证的时候,验证 .jpg 通过,但是保存到本地的时候,00阶段文件名,只保存 %0 之前的内容 malicious.php ,文件被当作 php 文件处理且可能被执行

服务器解析漏洞绕过

有些版本的web服务器中间件(Apache、IIS、nginx等)存在解析漏洞

  1. IIS6目录解析漏洞

    在老版本的IIS6中,/.asp/ 目录下的所有文件都会被当作ASP脚本解析,攻击者可以看能不能上传恶意脚本文件到 /.asp/

  2. IIS6分号漏洞

    IIS6 在解析文件名时,如果文件名中包含分号 ;,它会将分号后面的内容丢弃。

    例如:我们可以上传一个 a.asp;jpg 文件,在其中插入恶意代码

  3. Windows Server中的空格和点漏洞

    在一些旧版Windows Server中,文件名中的点(.)和空格会被自动删除。

    例如:构造 a.php.a.php[空格] 的文件,Windows会去除文件名中的点和空格,导致文件名被解析为 .php 文件,绕过后端的扩展名过滤。

  4. Nginx空字节漏洞

    Nginx的某些版本存在空字节 (%00) 漏洞,攻击者可以上传包含 %00 的文件名(如 xxx.jpg%00.php

  5. Apache解析漏洞

    在 Apache 版本1和2中,文件名解析是从右向左进行的,遇到无法识别的扩展名(如 .rar.gif)时,Apache会跳过这些扩展名。

    例如:上传文件 a.php.rara.php.gif ,Apache会从文件名的右侧开始解析,遇到 .rar.gif 扩展名时忽略它们,将文件当作 .php 文件处理。

内容检测绕过

在后端会对上传的文件内容进行检查,常见的检查方法有文件头检测、二次渲染等

文件头(文件幻数)

  1. 原理

    不同类型的文件开头的二进制文件头不一样,后端可能根据文件头来判断是否为对应类型文件

    • 常见文件幻数
    • JPG: FF D8 FF EO 00 10 4A 46 49 46.
    • GIF:47 49 46 3839 61(GIF89a).
    • PNG:89 50 4E 47
  2. 绕过方法

    通过BP抓包,给上传脚本加上对应类型的文件头字节,php引擎会将标签 <? 之前的内容当作 html 文本,不解释就直接跳过执行后面的代码

    image-20250423150818897

图片马

  1. 原理:将恶意代码嵌入到图片文件,后续通过利用.htaccess 等方法解析图片为PHP文件,执行图片内代码

  2. 例子

    • 在路径下准备好一句话木马.php和一张图片 .png (或者 .jpg )

    • 输入系统指令: copy 一张图片.png/b+一句话木马.php/a 生成图片名称.png

⼆次渲染绕过

  1. 原理:在我们上传文件后,网站可能会对图片进行二次处理(格式、尺寸要求等),服务器会把里面的内容进行替换更新,生成一个新的图片再返回到网站上。在这种情况下,我们可以先上传一个图片马,将一句话木马插入到图片二次渲染后仍然保留的那部分数据里,确保不会在二次处理时删除掉。

  2. 例子

    上传一个图片马,一句话木马放在最后,上传后再从网站上下载这个图片,查看16进制内容中木马代码是否已经被去除,哪些位置是不变的,将代码插入到这个位置再重新上传

    image-20250423163919242

防御

  1. 服务端文件扩展名使用白名单检测 + 文件名重命名

    例如:可以使用一个随机或自动生成的文件名来替代用户上传的原始文件名,这可以避免文件名中包含特殊字符、空格、分号等恶意字符,而且可以确保文件路径安全

  2. 对文件内容进行检测

    • MIME类型验证

    • 文件头检查

    • 内容解析和验证:使用一些解析方法来确保文件没有篡改

      例如,对于图片文件,可以通过 getimagesize() 来验证文件是否为有效的图像文件;对于PDF文件,可以使用专门的PDF解析库来确保文件没有被篡改。

    • 采用压缩函数处理图片,破坏图片中的HTML代码

  3. 做好中间件的安全配置,定时修复服务器端的解析类漏洞。

  4. 文件上传的目录设置为不可执行:只要web容器无法解析目录下的文件,即使攻击者上传了脚本文件,服务器也不会执行

参考文章

技术文章:

实战记录: