Skip to content

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

漏洞介绍

Vite是前端开发工具提供商,在6.2.3、6.1.2、6.0.12、5.4.15和4.5.10之前的版本中存在漏洞。'@fs '拒绝访问Vite服务允许列表之外的文件。添加'?生的??'或者'?进口和原料??'到URL绕过此限制并返回文件内容(如果存在)。存在此绕过是因为尾随分隔符,例如“?”在多个地方被删除,但不在查询字符串regex中考虑。任意文件的内容都可以返回到浏览器。只有将Vite dev服务器显式暴露给网络(使用“--Host”或“server. Host”配置选项)的应用程序才会受到影响。6.2.3、6.1.2、6.0.12、5.4.15和4.5.10版本修复了该问题。

影响范围

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

修复建议

升级到以下安全版本:

version >=6.2.3

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

漏洞复现

批量检测python脚本:github仓库

https://github.com/xaitx/CVE-2025-30208

注意该任意文件读取漏洞有利用过程中有缓存机制

这里我进行分析复现环境为:vite v6.2.0版本。操作系统为Windows

直接使用官方的payload的请求

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

Linux:
curl "http://localhost:5173/@fs/etc/passwd?import&raw??"

除了这个利用import参数之外还可以使用sec-fetch-dest请求头,然后配合?raw??参数进行。

我自己电脑是windows系统,测试成功读取到文件。

image-20250327000416417

漏洞分析

这里分析复现只是个人理解,有错误欢迎指出,并且我看官方的介绍中还有可以用?raw??进行任意文件读取的,我测试分析过程中并未发现在其他地方可以绕过鉴权进行加载,有知道的小伙伴欢迎指出。

(新增:我看了下进入的if条件除了import之外其他也可以,比如请求头sec-fetch-dest这样是只需要参数?raw??进行,具体的?raw??在后门会提及)

先看官方提交的修复commit,很明显直接的是添加了一个替换。

使用正则/[?&]+$/替换成空值,将url地址最后的连续的&或者?替换成空。

image-20250327001231592

将源码直接下载下来分析,这里使用的是vite v6.2.0分支

在文件packages\vite\src\node\server\middlewares\transform.ts 中可以看到这里原本的代码,新版修复也是在这个位置。通过正则匹配进行判断是否属于raw或者url类型的。满足任意一个就会使用ensureServingAccess函数进行鉴权。比如rawRe的正则为:export const rawRE = /(\?|&)raw(?:&|$)/

匹配要求raw参数结束或者raw&。结合官方的修复方案这里使用的是raw?进行绕过,但是在这个函数开头有一部分代码

    try {
      url = decodeURI(removeTimestampQuery(req.url!)).replace(
        NULL_BYTE_PLACEHOLDER,
        '\0',
      )
    } catch (e) {
      return next(e)
    }

处理后会丢失结尾的?所以url中输入的比如是两个??

image-20250327001901069

然后就是通过通过import参数进入if条件判断。并调用removeImportQuery清除import参数的函数。

这里解释下?raw??的payload

这里可以看到除了import判断还有其他的比如请求头或者其他的。我们看下js的判断函数

js
export const isJSRequest = (url: string): boolean => {
  url = cleanUrl(url)
  if (knownJsSrcRE.test(url)) {
    return true
  }
  if (!path.extname(url) && url[url.length - 1] !== '/') {
    return true
  }
  return false
}

第一个正则不用看了,就是匹配后缀啥的,第二个if可以看出来如何请求的是一个无后缀我文件,将会直接返回True。所有这里就可以解释?raw??为啥可以使用了。但是有限制,只能读取无后缀文件。有后缀就要加参数import或者其他的请求头之类的处理。

image-20250327003301790

在removeImportQuery函数中在清除掉import参数后,还将结尾的多余的?或者&给清除掉了。

image-20250327003454602

后续就是经过一系列判断啥的进行了操作,然后通过插件加载。最终实现任意文件读取的就是通过assert插件。

在文件packages\vite\src\node\plugins\asset.ts中。if判断rawRE是否满足。然后就是加载文件并通过fsp.readFile读取文件。

其中我自己理解的这个漏洞的主要成因就是第一次的检测时候让rawRE不满足,这样不会进行鉴权,第二次进行rawRE判断时候满足,这样系统认为是raw请求,就开始加载文件。

image-20250327004316875

参考链接

https://github.com/vitejs/vite/security/advisories/GHSA-x574-m823-4x7w

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2025-30208

https://github.com/vitejs/vite/commit/315695e9d97cc6cfa7e6d9e0229fb50cdae3d9f4