1 概念

文件上传漏洞是指由于程序员未对上传的文件进行严格的验证和过滤而导致的用户可以越过本身权限向服务器上传可执行的动态脚本文件。

1.1 危害

非法用户可以上传恶意文件控制整个网站,甚至是控制服务器,这个恶意脚本文件,又被称为webshell后门,可以很方便的查看服务器信息、目录、执行系统文件等

1.2 有关文件上传的知识

1.2.1 过程

客户端 选择发送的文件服务器接收网站程序判断移动到指定路径

1.3 php文件上传代码

文件上传时会返回一些代码给客户端,客户端会根据这些值判断上传是否正常

  • 值:0
    • 没有错误发生,上传成功
  • 值:1
    • 上传文件超过php.ini 中 upload_maax_filesize 限制大小
  • 值:2
    • 上传文件大小超过HTML表单中MAX_FILE_SIZE选项限定的值
  • 值:3
    • 文件只有部分被上传
  • 值:4
    • 没有文件上传

2 分类

文件上传漏洞可分为直接文件上传和有条件的文件上传

直接文件上传:属于高危漏洞的一种,能直接getshell,没有任何限制,攻击者很容易通过上传点,获取网站控制权

有条件的文件上传:一般是开发者经验不足,对文件上传做了简单

3 修复方案

  • 在网站中存在上传模块,需要做好权限认证,不能让匿名用户可访问
  • 文件上传目录设置为进制脚本文件执行。这样设置即使被上传后门的动态脚本也不能解析
  • 设置上传白名单
  • 上传后缀名,一定要设置成图片格式

4 攻击方式

常见攻击方式:头像上传、修改上传、文件编辑器中文件上传、图片上传、媒体上传等,通过抓包上传恶意文件进行测试

4.1 常见攻击文件后缀

后缀名主要对应环境利用原理
asp, aspx, ashxIIS (Windows)标准脚本执行
asa, cdx, cerIIS 6.0 (或特定映射)IIS 解析映射漏洞(绕过黑名单)
phpApache / Nginx标准脚本执行
php3, phtmlApache别名后缀(绕过黑名单)
php.aApacheApache 多后缀解析特性
jspTomcat / WebLogic (Java)标准脚本执行
shtmlApache / IIS (开启SSI)SSI 注入 / 命令行
有些网站会对asp或者php进行过滤成空
可以利用这点伪造:aaspsp、pphphp

5 任意文件上传漏洞

任意文件上传漏洞又名文件直接上传漏洞,这种漏洞危害极大,若果攻击者能直接上传恶意脚本到网站存放的目录,且这个目录可解析动态脚本语言,那么攻击者就能够直接获取网站权限,甚至进一步权限提升,控制服务器。

6 有条件的文件上传

6.1 前端绕过

6.1.1 JS绕过

<form action="upload.php" method="post" enctype="multipart/form-data" onsubmit="return checkFile()">
    <input type="file" id="file" name="uploadedfile">
    <input type="submit" value="Upload">
</form>

<script>
function checkFile() {
    var file = document.getElementById("file").value;
    // 获取文件后缀
    var ext = file.substring(file.lastIndexOf(".")).toLowerCase();
    // 白名单校验
    if (ext != ".png" && ext != ".jpg" && ext != ".gif") {
        alert("只能上传图片文件!");
        return false; // 阻止表单提交
    }
    return true;
}
</script>

绕过手法:

  1. 禁用 JS: 直接在浏览器设置或使用插件(如 NoScript)禁用当前网页的 JavaScript 运行。
  2. Burp Suite 抓包替换(最常用): 把木马文件命名为 shell.jpg 骗过前端 JS 校验,点击上传后,在 Burp 中拦截请求,将报文里的 filename="shell.jpg" 改回 filename="shell.php",然后放行。

6.1.2 sig验证

6.2 绕过 contnet-type 检测上传

有些上传模块,会对 http 类型头进行检测 ,如果是图片类型,允许上传文件到服务器,否则上传失败。因为服务端是通过 contnet-type 判断类型,contnet-type 在客户端是可以被修改的,因此可以绕过

<?php
if (isset($_POST["submit"])) {
    // 漏洞点:直接读取 HTTP 报文中的 Content-Type 字段
    $file_type = $_FILES['uploadedfile']['type']; 
    
    // 校验 Content-Type 是否为图片
    if ($file_type == "image/jpeg" || $file_type == "image/png" || $file_type == "image/gif") {
        move_uploaded_file($_FILES['uploadedfile']['tmp_name'], "uploads/" . $_FILES['uploadedfile']['name']);
        echo "上传成功!";
    } else {
        echo "上传失败,非法的 MIME 类型!";
    }
}
?>

6.3 逻辑漏洞

6.3.1 条件竞争

在文件上传中,最经典的逻辑漏洞就是条件竞争 (Race Condition)。很多开发者的逻辑是:“先允许文件上传到服务器,然后再用代码去检查文件合法性,如果发现是非法文件,再将其删除。”

<?php
$target_path = "uploads/" . $_FILES["uploadedfile"]["name"];
// 致命逻辑:先不管三七二十一,把文件保存下来
move_uploaded_file($_FILES["uploadedfile"]["tmp_name"], $target_path);

// 然后再进行后缀检查
$ext = pathinfo($target_path, PATHINFO_EXTENSION);
$blacklist = array("php", "asp", "jsp");

if (in_array(strtolower($ext), $blacklist)) {
    // 发现是黑名单文件,执行删除
    unlink($target_path); 
    echo "非法文件,已删除!";
} else {
    echo "上传成功!";
}
?>

绕过手法: 虽然服务器最终会删除我们的 .php 木马,但在保存成功执行删除之间,存在一个极短暂的时间差(几毫秒)。 我们可以利用 Burp Suite 的 Intruder 模块或编写 Python 脚本,极其密集地(多线程) 同时发送“上传木马”和“访问木马”的请求。只要我们在木马被服务器删除之前的一瞬间访问到了它,木马内部的恶意代码就会执行(例如:让木马在执行时立刻在同目录下生成一个新的、隐蔽的后门文件)。

6.3.2 信任前端隐藏字段绕过

某些 PHP 代码在处理文件上传时,没有对上传的真实文件的元数据进行严格校验,而是过度信任了前端 HTML 表单中传递过来的参数(如 <input type="hidden">)。开发者的本意可能是利用前端辅助传递文件信息,但这直接跨越了安全信任边界。

<?php
// 获取随机文件名的函数
function makeRandomPathFromFilename($dir, $fn) {
    // 致命逻辑:pathinfo 直接读取了用户可控的 $fn 的后缀,并没有检查真正上传的文件 $_FILES
    $ext = pathinfo($fn, PATHINFO_EXTENSION);
    return $dir."/".genRandomString().".".$ext;
}

if(array_key_exists("filename", $_POST)) {
    // 后端完全信任了前端 POST 过来的 filename 字段
    $target_path = makeRandomPathFromFilename("upload", $_POST["filename"]);
    
    // 直接按照伪造的后缀保存了文件
    move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path);
}
?>

6.4 黑名单上传绕过

上传模块,有时会写成黑名单限制,在上传文件的时候获取后缀名,再把后缀名与程序中黑名单进行检测;如果后缀名在黑名单内,将禁止文件上传

6.4.1 绕过攻击

上传图片时,如果提示不允许php、asp这种信息提示,可判断未黑名单限制,上传黑名单以外的后缀名即可

禁止asp时,可以上传asa、cer、cdx这些后缀名,如果网站允许.net执行时可以上传aspx

在不同的中间件中有特殊情况,如果在apache中开启 application/x-httpd-php

在AddType application/x-httpd-php .php .phtml .php3时,这些都会解析成php

6.4.2 重写解析绕过上传

6.4.2.1 .htaccess 文件(Apache 服务器的“局部特权”)

  1. 本质定位: 它是 Apache Web 服务器的分布式配置文件。它允许网站管理员在不修改服务器主配置文件(如 httpd.conf)的情况下,对当前所在的目录及其所有子目录进行单独的权限控制和行为调整。

  2. 常见合法用途: 例如设置网页的 301 重定向、自定义 404 错误页面、或者限制某些特定 IP 段的访问。

  3. 安全风险(利用原理): 在网安实战中,如果我们发现目标系统使用的是 Apache,并且管理员配置不当,开启了 AllowOverride 权限。攻击者就可以在文件上传点,上传一个自己伪造的 .htaccess 文件。

    • 攻击者会在里面写入类似 AddType application/x-httpd-php .png 的指令。

    • 这条指令会强行篡改服务器的“认知”,让 Apache 把该目录下所有上传的 .png 图片文件,都当作 PHP 可执行代码来解析。这样,攻击者上传的图片木马就能直接被触发。

6.4.2.2 .user.ini 文件(PHP 解析器的“寄生机制”)

  1. 本质定位: 它是 PHP 解析器(在 CGI/FastCGI 模式下运行时)的局部配置文件。它的作用类似于 PHP 的全局主配置文件 php.ini,但它的生效范围仅限于它所在的当前目录及子目录。

  2. 常见合法用途: 在共享主机环境下,允许不同的虚拟主机租户根据自己的业务需求,修改部分 PHP 的环境变量(例如修改上传文件的大小限制 upload_max_filesize)。

  3. 安全风险(利用原理): 它最危险的配置项是 auto_prepend_file(在脚本执行前解析文件)和 auto_append_file(在脚本执行后解析文件)。

    • 攻击者会上传一个 .user.ini,里面写上 auto_prepend_file=evil.png

    • 这意味着,当任何用户(或攻击者自己)访问该目录下的任何一个正常的 PHP 文件(如 index.php)时,PHP 都会被迫在执行正常代码前,先悄悄加载并执行 evil.png 里的恶意木马代码。这是一种极其隐蔽的“寄生”手法,木马本身不需要被直接访问就能触发。 上传模块如果过滤了所有可以执行的后缀名,在允许过滤.htaccess文件的情况下可以帮我们实现:文件夹密码保护、用户自动重定向、自定义错误页面、封禁特定ip的用户、只允许特定IP地址的用户、禁止目录列表,以及使用其他文件作为index文件等一些功能

在htaccess中写入SetHandler application/x-httpd-php则可以将文件重写成php文件,要htaccess的规则生效,则需要在apache开启rewrite重写模块

6.4.3 大小写绕过上传

有的上传模块,后缀名采用黑名单判断,但是没有对黑名单大小写进行严格判断,导致可以绕过,如pHp、Php

6.4.4 空格绕过上传

在上传模块中,如果没有对空格进行限制可能会绕过

6.4.5 利用系统特性绕过

6.4.5.1 windows特性

6.4.5.1.1 后缀名特性

在windows中后缀名.,系统会自动忽略,所以shell.php,与 shell.php. 作用是一样的

6.4.5.1.2 NTFS交换数据流::$DATA绕过上传

如果后缀名没有对::$DATA进行判断,windows系统NTFS特性可以绕过上传

7 过滤绕过

7.1 绕过<?

<script language="php">@eval($_POST['pwd']);</script>

只在 PHP 7.0 之前的版本(比如 PHP 5.x)才有效

因为 PHP 官方也觉得这种长得像前端标签的写法太非主流了,所以在 PHP 7.0 及以后的版本中,彻底废弃并移除了这个语法。如果现在的 CTF 靶机是较新的 PHP 7/8 环境,你上传这段代码不但不会执行,还会被当成普通的 HTML 文本直接打印在网页上。

图里的作者把文件保存为 .phtml,是因为在很多服务器配置里,.phtml 也会被当做 PHP 文件来解析。这就完成了一次针对旧版 PHP 环境的经典绕过!

需要我给你讲讲如果在 PHP 7+ 的环境下遇到过滤 <?,除了前面说的 .user.ini,还有什么办法吗?