一、文件上传漏洞原理
文件上传漏洞是指 Web 应用程序在对用户上传的文件进行校验时存在缺陷,导致攻击者可以上传恶意文件(如 WebShell、恶意脚本、可执行文件等)到服务器,从而获取服务器控制权限或造成其他危害。
1.1 漏洞成因
未对上传文件类型做充分校验
仅在前端做校验,后端未做二次验证
校验逻辑存在缺陷(如仅检查 Content-Type,未检查文件内容)
文件存储路径可预测或未做安全处理
文件重命名逻辑被绕过
1.2 漏洞危害
获取 WebShell,完全控制服务器
植入恶意程序进行横向移动
上传恶意文件作为钓鱼或恶意软件分发点
上传 HTML 文件实现 XSS 攻击
覆盖系统文件导致服务器异常
二、前端绕过
许多 Web 应用在前端使用 JavaScript 对文件扩展名或 MIME 类型进行检查,但后端未做相同的验证。前端绕过是最基础也是最常见的绕过方式。
// 前端常见校验代码
function checkFile() {
var file = document.getElementById('upload').value;
var ext = file.substring(file.lastIndexOf('.') + 1).toLowerCase();
if (ext !== 'jpg' && ext !== 'png' && ext !== 'gif') {
alert('仅允许上传图片文件!');
return false;
}
return true;
}绕过方法
直接修改请求:使用 Burp Suite 截断上传请求,修改文件扩展名
禁用 JavaScript:在浏览器中禁用 JS,直接提交表单
修改响应:删除或修改前端验证函数
使用浏览器开发者工具:直接删除
onsubmit事件或修改验证逻辑
三、MIME 类型绕过
部分后端代码仅检查 HTTP 请求中的 Content-Type 字段来判断文件类型。
// 后端仅检查 MIME 类型的示例
$mime = $_FILES['file']['type'];
if ($mime !== 'image/jpeg' && $mime !== 'image/png') {
die('仅允许上传图片!');
}绕过方法
使用 Burp Suite 等工具截断上传请求,将 Content-Type 修改为允许的类型:
原始请求:Content-Type: application/x-php
修改后:Content-Type: image/jpeg四、文件头伪造(Magic Number 绕过)
当后端使用 getimagesize()、finfo_file() 或 exif_imagetype() 等函数检查文件头(Magic Number)时,需要在 payload 前加上合法的文件头。
4.1 常见的文件头
4.2 图片马制作
将 WebShell payload 附加到合法图片文件末尾,或者直接在 payload 前添加文件头:
// GIF 文件头 + WebShell
GIF89a
[WEBSHELL_PAYLOAD_REMOVED]
// 使用命令合并图片和脚本
copy normal.jpg + shell.jpg webshell.jpg五、双扩展名绕过
利用服务器或中间件对文件名解析的特性,通过双扩展名绕过后端校验。
5.1 常见双扩展名技巧
六、解析漏洞
不同 Web 服务器和中间件存在不同的文件解析规则,利用这些规则可以绕过上传限制。
6.1 Apache 解析漏洞
多后缀解析:
shell.php.rar或shell.php.xxx若扩展名无法识别,Apache 会向前寻找可识别的扩展名配置漏洞:
AddHandler指令将某一类文件以 PHP 方式解析
6.2 IIS 解析漏洞
目录解析:创建
shell.asp/目录,目录下所有文件按 ASP 解析分号截断:
shell.asp;.jpg被解析为 ASP 文件PUT 请求:WebDAV 开启时可 PUT 任意文件
6.3 Nginx 解析漏洞
PHP CGI 解析:
shell.jpg/xx.php将图片文件以 PHP 解析(cgi.fix_pathinfo 缺陷)%00 截断:在部分低版本 Nginx + PHP FastCGI 组合中生效
七、.htaccess 与 .user.ini 利用
7.1 .htaccess 绕过
Apache 允许通过 .htaccess 文件进行目录级别的配置。如果服务器允许上传 .htaccess 文件,可以将任意扩展名映射为可执行脚本:
# 将 .jpg 文件以 PHP 方式解析
AddType application/x-httpd-php .jpg
# 或者在当前目录启用 PHP 解析
SetHandler application/x-httpd-php7.2 .user.ini 利用
PHP 5.3.0 以后支持 .user.ini 文件,可以在每个目录下覆盖 PHP 配置。如果允许上传 .user.ini,可以通过 auto_prepend_file 或 auto_append_file 在请求其他 PHP 文件时自动执行指定文件:
; 在每次 PHP 请求前自动执行 shell.jpg
auto_prepend_file = shell.jpg八、条件竞争绕过
某些上传功能先将文件保存到服务器,再进行安全性检查,检查不通过则删除。攻击者可以在文件存活的时间窗口内访问并执行该文件。
8.1 利用原理
攻击流程:上传恶意文件(服务器保存)→ 在文件被删除前快速并发访问 → 执行成功。
8.2 自动化脚本示例
# 使用 Python 多线程同时进行上传和访问
import requests
import threading
def upload():
while True:
files = {'file': ('evil.php', payload)}
requests.post('http://target/upload.php', files=files)
def access():
while True:
r = requests.get('http://target/uploads/evil.php')
if r.status_code == 200:
print('竞态条件利用成功!')
break
# 启动多个线程
for i in range(20):
threading.Thread(target=upload).start()
threading.Thread(target=access).start()九、黑名单绕过技巧
很多 Web 应用使用黑名单方式禁止特定扩展名,但由于黑名单不可能穷尽所有可执行扩展名,因此存在多种绕过方式。
9.1 可执行扩展名列表
9.2 黑名单绕过方式总结
扩展名变体:使用未被列入黑名单的可执行扩展名(如
.phtml代替.php)大小写混淆:
.PhP、.pHp、.ASP特殊字符:
shell.php.(末尾点号 Windows 自动去除)、shell.php(末尾空格)双扩展名:
shell.php.jpg参数截断:
shell.php%00.jpg解析漏洞:利用服务器解析特性
配合文件头:图片马 + 图片扩展名
配置文件上传:上传
.htaccess或.user.ini配合绕过换行截断:在文件名中插入换行符(
%0a或%0d%0a)Unicode 编码:利用操作系统 Unicode 转换特性(如
shell.php%FF)
十、各种绕过方式对比表
十一、通用防御方案
白名单策略:只允许特定扩展名,而非禁止特定扩展名
文件内容检测:使用 MIME Magic Number 检测 + 二次渲染图片
文件重命名:上传后使用不可预测的文件名(如 UUID)
文件存储分离:文件存储与 Web 根目录分离,通过专门脚本访问
权限控制:上传目录禁止执行脚本(取消执行权限)
禁用危险功能:关闭不必要的文件解析功能(如 cgi.fix_pathinfo = 0)
WAF 部署:使用 Web 应用防火墙检测恶意文件内容
文件上传与WebShell绕过技术详解
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
评论交流
欢迎留下你的想法