文件上传漏洞及其Bypass技术详解
1. 基础环境搭建
使用Docker部署upload-labs靶场环境:
docker pull c0ny1/upload-labs
2. 文件上传漏洞基础绕过技术
Pass-01: 前端JS验证绕过
漏洞分析:
前端JavaScript验证文件类型,仅允许.jpg|.png|.gif扩展名。
绕过方法:
- 直接修改前端代码或禁用JS
- 使用Burp Suite拦截请求并修改文件名
关键代码:
function checkFile() {
var file = document.getElementsByName('upload_file')[0].value;
var allow_ext = ".jpg|.png|.gif";
var ext_name = file.substring(file.lastIndexOf("."));
if (allow_ext.indexOf(ext_name) == -1) {
alert("该文件不允许上传");
return false;
}
}
Pass-02: Content-Type验证绕过
漏洞分析:
服务器检查Content-Type是否为image/jpeg、image/png或image/gif。
绕过方法:
- 拦截请求修改
Content-Type为允许的类型 - 在MacOS上,修改文件扩展名可能自动修改
Content-Type
关键代码:
if (($_FILES['upload_file']['type'] == 'image/jpeg') ||
($_FILES['upload_file']['type'] == 'image/png') ||
($_FILES['upload_file']['type'] == 'image/gif')) {
// 允许上传
}
3. 黑名单绕过技术
Pass-03: 非常规PHP扩展名绕过
漏洞分析:
黑名单包含.asp,.aspx,.php,.jsp,但未包含其他PHP变种扩展名。
绕过方法:
使用不常见的PHP扩展名:
.php5,.php4,.php3,.php2,.php1.phtml,.pht,.pHp(大小写变种)
关键代码:
$deny_ext = array('.asp','.aspx','.php','.jsp');
Pass-04: .htaccess文件上传
漏洞分析:
黑名单包含常见PHP扩展名但未包含.htaccess。
绕过方法:
- 上传
.htaccess文件设置解析规则:
AddType application/x-httpd-php .abc
- 然后上传包含PHP代码的
.abc文件
关键代码:
$deny_ext = array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2","pHp1",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf");
4. Windows特性利用
Pass-06: 空格绕过
漏洞分析:
黑名单检查但未处理文件名末尾空格。
绕过方法:
在扩展名后添加空格:shell.php
关键代码:
$file_name = trim($_FILES['upload_file']['name']);
$file_ext = strrchr($file_name, '.');
Pass-07: 点号绕过
漏洞分析:
Windows会自动去除文件名末尾的点。
绕过方法:
在扩展名后添加点:shell.php.
关键代码:
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name); // 删除末尾点
Pass-08: ::$DATA绕过
漏洞分析:
Windows会忽略NTFS文件流标识::$DATA。
绕过方法:
使用shell.php::$DATA作为文件名。
关键代码:
$file_ext = str_ireplace('::$DATA', '', $file_ext);
Pass-09: 点号空格点号绕过
漏洞分析:
多重过滤后仍可构造特殊文件名。
绕过方法:
使用shell.php. .作为文件名。
5. 其他高级绕过技术
Pass-10: 双写绕过
漏洞分析:
直接替换黑名单扩展名为空字符串。
绕过方法:
双写扩展名:shell.pphphp
关键代码:
$file_name = str_ireplace($deny_ext,"", $file_name);
Pass-11/12: %00截断
漏洞分析:
PHP旧版本(<=5.3.4)的magic_quotes_gpc=Off时可利用空字节截断。
绕过方法:
- GET型:
shell.php%00.jpg - POST型:
shell.php+ Hex编辑器中修改为shell.php\x00.jpg
关键代码:
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
Pass-13-16: 图片马与文件包含
技术要点:
- 制作图片马:
copy /b test.png + shell.php webshell.png - 配合文件包含漏洞执行
- 对于二次渲染(Pass-16),需找到未被修改的部分插入代码
Pass-17/18: 竞争条件
漏洞分析:
先保存文件后检查,存在时间差。
利用方法:
- 快速重复请求上传文件
- 使用Python脚本检测文件是否上传成功
示例脚本:
import requests
url = "http://target/upload/shell.php"
while True:
html = requests.get(url)
if html.status_code == 200:
print("Success!")
break
Pass-19: CVE-2015-2348
漏洞分析:
move_uploaded_file()函数存在00截断问题。
利用方法:
使用截断文件名:shell.php%00.jpg
Pass-20: 数组绕过
漏洞分析:
通过数组形式传递文件名绕过检查。
利用方法:
修改上传表单为数组形式:name="upload_file[]"
6. 防御建议
- 使用白名单而非黑名单
- 文件重命名(不使用用户提供的文件名)
- 限制上传目录的执行权限
- 检查文件内容而不仅依赖扩展名
- 对图片文件进行二次渲染
- 设置
upload_max_filesize和post_max_size限制 - 及时更新PHP版本,修复已知漏洞
7. 总结
文件上传漏洞的绕过技术多种多样,攻击者会利用:
- 前端验证不严格
- 后端黑名单不完整
- 服务器特性(如Windows特性)
- PHP版本漏洞(如%00截断)
- 竞争条件等时间差问题
防御需要采取多层次的安全措施,不能仅依赖单一防护手段。