记一次绕过后缀安全检查进行文件上传
字数 1092 2025-08-06 23:10:24
绕过文件后缀安全检查的上传技术分析
技术背景
在Windows系统中,当保存文件的文件名以点(.)结尾时,系统会自动将该点去掉。这一特性可以被利用来绕过某些文件上传功能的后缀安全检查,实现任意文件上传。
漏洞原理
-
Windows文件名处理特性:
- Windows会自动去除文件名末尾的点(.)
- 例如:上传"shell.php."会被系统存储为"shell.php"
-
安全检查绕过机制:
- 许多文件上传检查仅验证文件扩展名
- 检查逻辑可能使用简单的字符串匹配或正则表达式
- 检查"shell.php."时可能认为不是.php文件(因为有尾随点)
- 但实际存储为"shell.php"后仍可被解析执行
攻击场景
-
典型应用环境:
- 使用Windows服务器的Web应用
- 有文件上传功能且仅做简单后缀检查
- 上传目录有脚本执行权限
-
检查逻辑缺陷示例:
// 不完善的检查代码 $allowed = array('jpg', 'png', 'gif'); $ext = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION); if(!in_array($ext, $allowed)) { die('Invalid file type!'); }
利用方法
-
基本利用:
- 准备恶意脚本文件,如PHP webshell
- 将文件名命名为"shell.php."(注意末尾的点)
- 上传文件
-
变体利用:
- "shell.php "
- "shell.php::$DATA"
- "shell.php\x00.jpg"
- 其他Windows文件名特性相关的变形
防御措施
-
服务器端防御:
- 使用白名单验证文件类型
- 不仅检查扩展名,还要检查文件内容
- 重命名上传的文件
- 设置正确的文件权限
-
代码示例:
// 安全的检查方式 $allowed = array('jpg' => 'image/jpeg', 'png' => 'image/png'); $fileinfo = finfo_open(FILEINFO_MIME_TYPE); $mime = finfo_file($fileinfo, $_FILES['file']['tmp_name']); $ext = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION); if(!array_key_exists($ext, $allowed) || $allowed[$ext] !== $mime) { die('Invalid file!'); } // 重命名文件 $newname = md5_file($_FILES['file']['tmp_name']).'.'.$ext; move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/'.$newname); -
其他防御:
- 禁用上传目录的脚本执行权限
- 使用单独的子域名托管用户上传内容
- 定期审计上传的文件
实际案例
-
漏洞发现:
- 在文件上传功能测试时尝试上传"test.php."
- 服务器返回上传成功
- 实际访问时发现文件被存储为"test.php"并可执行
-
影响评估:
- 可能导致任意代码执行
- 服务器完全沦陷风险
- 数据泄露可能性
扩展知识
-
Windows其他文件名特性:
- 保留字(CON, PRN, AUX等)
- 文件流特性(如test.php::$DATA)
- 长文件名处理
-
相关漏洞类型:
- 双扩展名绕过(如shell.php.jpg)
- 大小写混淆(如sHell.PhP)
- 空字节注入(如shell.php%00.jpg)
- MIME类型欺骗
测试方法
-
手工测试:
- 尝试上传各种变形文件名
- 观察服务器响应和实际存储结果
- 验证是否可访问和执行
-
自动化测试:
- 使用Burp Suite等工具拦截上传请求
- 修改文件名参数进行模糊测试
- 检查响应差异
总结
利用Windows文件名处理特性绕过文件上传检查是一种常见且有效的攻击技术。防御此类攻击需要开发者深入理解文件系统特性,实施多层次的安全检查,而不仅仅是简单的扩展名验证。