Vite任意文件读取漏洞分析(CVE-2025-30208)

Vite, a provider of frontend development tooling, has a vulnerability in versions prior to 6.2.3, 6.1.2, 6.0.12, 5.4.15, and 4.5.10. @fs denies access to files outside of Vite serving allow list. Adding ?raw?? or ?import&raw?? to the URL bypasses this limitation and returns the file content if it exists. This bypass exists because trailing separators such as ? are removed in several places, but are not accounted for in query string regexes. The contents of arbitrary files can be returned to the browser. Only apps explicitly exposing the Vite dev server to the network (using --host or server.host config option) are affected. Versions 6.2.3, 6.1.2, 6.0.12, 5.4.15, and 4.5.10 fix the issue.

从漏洞描述可以看到影响版本如下:

6.2.0 <= version <=6.2.2
6.1.0 <= version <=6.1.1
6.0.0 <= version <=6.0.11
5.0.0 <= version <=5.4.14
version <=4.5.9

漏洞环境

漏洞环境已上传github:Vul-Env/Vite/CVE-2025-30208 at main · Cmisl/Vul-Env

Vite版本:v6.2.0

node版本 :v20.9.0

操作系统:Windows11

运行以下指令启动环境:

1
2
npm install
npm run dev

image-20250327114911136

漏洞复现

根据漏洞POC,并且当前环境系统是Windows。如果是Linux系统就把C://windows/win.ini换成etc/passwd即可。

1
http://localhost:5173/@fs/C://windows/win.ini?import&raw??

image-20250327120321096

漏洞分析

在Vite中,@fs是一个用于处理文件系统访问的机制,主要用于开发模式下访问项目根目录外的文件。当通过importrequire引入根目录外的文件时,Vite会将路径转换为以/@fs/开头的绝对路径形式,例如/@fs/app/vite/packages/vite/dist/client/env.mjs

由于一些原因我并没有搭建调试环境进行代码调试,因此分析只是粗糙地分析一下了,大概看了一下绕过鉴权的地方如下:

其中rawRE和urlRE如下:

1
2
export const urlRE = /(\?|&)url(?:&|$)/
export const rawRE = /(\?|&)raw(?:&|$)/

将url与上面两个正则表达式进行匹配,匹配情况如下。

输入 是否匹配 匹配部分
?raw ?raw
&raw &raw
?raw&foo ?raw&(部分)
?rawn -
?raw#anchor -
?raw? -
关于url的也同理,我们在回头看这条if判断语句:
1
2
3
4
if (
(rawRE.test(url) || urlRE.test(url)) &&
!ensureServingAccess(url, server, res, next)
)

在计算机中,对于&&运算符,如果前面的语句为false,那么&&后的语句是不会执行的。举个例子,下面代码我们运行之后不会输出任何东西,并且由于printHelloWorld方法不被调用,因此里面的语句也不会输出。

而&&前面的语句为true,那么后面的语句才会去执行,也因此调用了printHelloWorld方法,输出了里面的语句。

因此对于POC中的URL:http://localhost:5173/@fs/C://windows/win.ini?import&raw??,由于两个正则表达式都不会去匹配,因此&&前语句为false,后面的鉴权函数不会被调用。

然后就是对import的处理了。

removeImportQuery这里的处理逻辑是把匹配到的替换成第一个捕获组,也就是(\?|&)匹配到的字符,这里就是?import&替换成?,接着又会匹配结尾的?或者&

漏洞修复

看一下这个diff,添加了一个正则表达式常量 trailingQuerySeparatorsRE,用于匹配 URL 末尾的一个或多个查询分隔符(?&)。

然后在 transformMiddleware 函数中处理 URL,将 URL 中末尾的查询分隔符去掉,并将结果存储
urlWithoutTrailingQuerySeparators 变量中。将原本直接使用 url 的地方,改为使用处理后的 urlWithoutTrailingQuerySeparators

来避免rawRE和urlRE的匹配为false从未不会调用鉴权的方法。

漏洞修复

已经发布了新版本,都在原来的基础上打了补丁,直接升级到新版本即可,安全版本如下:

6.2. 3 <=version
6.1. 2 <= version <6.2.0
6.0.12 <= version <6.1.0
5.4.15 <= version <6.0.0
4.5.10 <= version <5.0.0


Vite任意文件读取漏洞分析(CVE-2025-30208)
http://example.com/2025/03/27/Vite任意文件读取漏洞分析(CVE-2025-30208)/
作者
cmisl
发布于
2025年3月27日
许可协议