深入Vite任意文件读取与分析复现
字数 1169 2025-08-29 22:41:32
Vite任意文件读取漏洞分析与复现指南
漏洞概述
本漏洞是Vite开发服务器中存在的一个任意文件读取漏洞,攻击者可以通过精心构造的URL请求读取服务器上的任意文件,包括敏感系统文件如C:/Windows/win.ini等。
环境搭建
-
创建Vite项目:
npm create vite@latest -
安装依赖:
cd your-project npm install -
运行开发服务器:
npm run dev
漏洞复现
有效Payload
Windows系统:
http://localhost:3000/@fs/C:/Windows/win.ini?raw
http://localhost:3000/@fs/C:/Windows/win.ini?raw??
Linux系统:
http://localhost:3000/@fs/etc/passwd
测试说明
- 对于Windows系统,需要包含
raw参数 - 多个问号
??可以绕过某些检查 - Linux系统不需要特殊参数,直接请求文件路径即可
漏洞分析
Vite执行流程
- 入口点:
cli.js是Vite的入口文件,具体入口函数是createServer - 服务器创建:
createServer调用_createServer函数 - 中间件注册:
_createServer会注册一系列中间件,顺序很重要:servePublicMiddlewaretransformMiddleware- 其他中间件...
关键中间件分析
1. servePublicMiddleware
- 无论是否有
cleanUrl,最终执行next()时URL保持不变 - 对漏洞利用影响不大,只是传递请求到下一个中间件
2. transformMiddleware
关键处理逻辑:
-
移除时间戳查询参数:
url = removeTimestampQuery(url)- 移除形如
t=1234567890123的时间戳参数 - 处理后的URL会减少问号数量
- 移除形如
-
公共路径检查:
if (url.startsWith(publicPath + '/') || url === publicPath) { warnAboutExplicitPublicPathInUrl(url, publicPath, timestamp) }- 检查URL是否以
public开头 - 仅打印警告,不影响请求处理
- 检查URL是否以
-
请求类型判断:
if (!(rawRE.test(url) || urlRE.test(url))) { return next() }- 需要满足
raw参数或特定URL模式才会继续处理 - 多个问号可以绕过此检查
- 需要满足
-
文件类型判断:
if (isJSRequest(url) || isImportRequest(url))isJSRequest: 检查是否为JS请求或没有后缀的请求isImportRequest: 检查是否包含import参数
核心漏洞点
-
文件加载阶段:
pluginContainer.load(id, { ssr })id参数最终被处理为文件路径(如C:/Windows/win.ini)- 经过
cleanUrl处理后,查询参数被移除
-
路径处理缺陷:
- Vite没有正确限制
@fs前缀的访问范围 - 对Windows路径和Linux路径的处理存在逻辑缺陷
- Vite没有正确限制
漏洞利用条件
-
Windows系统:
- 必须包含
raw参数 - 可以附加额外的问号绕过检查
- 必须包含
-
Linux系统:
- 不需要特殊参数
- 直接请求文件路径即可
防御建议
- 升级到已修复的Vite版本
- 在开发环境中限制文件系统访问范围
- 避免在生产环境使用Vite开发服务器
- 实施严格的路径验证和过滤
总结
该漏洞源于Vite开发服务器对@fs前缀的处理不当,结合查询参数的解析缺陷,导致攻击者可以读取任意文件。理解该漏洞有助于开发者更好地保护自己的应用,同时也展示了中间件处理顺序和安全检查的重要性。