Goal Reached Thanks to every supporter — we hit 100%!

Goal: 1000 CNY · Raised: 1000 CNY

100.0%

CVE-2025-31486 PoC — Vite allows server.fs.deny to be bypassed with .svg or relative paths

Source
Associated Vulnerability
Title:Vite allows server.fs.deny to be bypassed with .svg or relative paths (CVE-2025-31486)
Description:Vite is a frontend tooling framework for javascript. The contents of arbitrary files can be returned to the browser. By adding ?.svg with ?.wasm?init or with sec-fetch-dest: script header, the server.fs.deny restriction was able to bypass. This bypass is only possible if the file is smaller than build.assetsInlineLimit (default: 4kB) and when using Vite 6.0+. Only apps explicitly exposing the Vite dev server to the network (using --host or server.host config option) are affected. This vulnerability is fixed in 4.5.12, 5.4.17, 6.0.14, 6.1.4, and 6.2.5.
Readme
# Vite Path Traversal Lab (CVE-2025-31486)

[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?repo=hackmelocal/CVE-2025-31486-Simulation)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A hands-on, containerized lab environment to simulate and exploit the Path Traversal vulnerability in Vite's development server, identified as `CVE-2025-31486`.

## 📖 About The Vulnerability

This lab demonstrates a significant Path Traversal vulnerability found in older versions of **Vite**, a modern web development build tool. The vulnerability allows an attacker to bypass security constraints in the Vite development server and read arbitrary files from the local file system.

The core of the issue lies in how Vite's asset plugin processes URLs. By crafting a special URL with suffixes like `?.svg` and `?.wasm?init`, an attacker can trick the server into skipping critical security checks (specifically the `server.fs.deny` function), granting access to sensitive files like `/etc/passwd`.

### Conditions for Exploitation

- **Vulnerable Vite Version:** The project must use a vulnerable version of Vite.
- **Exposed Dev Server:** The server must be exposed to the network using the `--host` flag.
- **File Size Limit:** The exploit works best for files smaller than the `build.assetsInlineLimit` (default is 4KB), as Vite inlines these files as Base64 strings in its response.

---

## 🎯 Live Simulation Lab

This repository contains everything you need to safely run a vulnerable Vite server and perform the exploit yourself. You can run the simulation in two ways:

### Method 1: Run in GitHub Codespaces (Recommended)

Get a full, cloud-based development environment in seconds. No local setup required.

1.  **Launch Codespace:** Click the "Open in GitHub Codespaces" button at the top of this README.
    [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?repo=hackmelocal/CVE-2025-31486-Simulation)

2.  **Wait for Setup:** GitHub will prepare your cloud environment. This might take a minute. A terminal will appear at the bottom of the editor once it's ready.

3.  **Start the Vulnerable Server:** In the terminal, run the following single command:
    ```bash
    docker compose up
    ```
    You will see output indicating that the vulnerable Vite development server is running and accessible.

4.  **Perform the Exploit:**
    - Open a **new terminal** in your Codespace (Click the `+` icon in the terminal panel).
    - Copy and paste the one-line exploit command below into the **new terminal** and press Enter.

### Method 2: Run Locally

If you prefer to run the lab on your own machine, follow these steps.

**Prerequisites:**
- [Docker](https://docs.docker.com/get-docker/) must be installed.
- [Docker Compose](https://docs.docker.com/compose/install/) must be installed.

**Instructions:**
1.  **Clone the Repository:**
    ```bash
    git clone https://github.com/hackmelocal/CVE-2025-31486-Simulation.git
    cd CVE-2025-31486-Simulation
    ```

2.  **Start the Vulnerable Server:** In your terminal, run the single command:
    ```bash
    docker compose up
    ```
    This command will build the Docker image (if not already built) and start the vulnerable Vite server.

3.  **Perform the Exploit:**
    - Open a **new, separate terminal window or tab**.
    - Copy and paste the one-line exploit command below into the **new terminal** and press Enter.
File Snapshot

[4.0K] /data/pocs/06b66ef7dbc4baf0f1f77ad8aab98cde9bf7d713 ├── [4.0K] app │   ├── [4.0K] dist │   │   ├── [4.0K] assets │   │   │   ├── [2.5K] index-_AYE_jbl.js │   │   │   └── [1.2K] index-CrYBktwj.css │   │   ├── [ 454] index.html │   │   └── [1.5K] vite.svg │   ├── [ 355] index.html │   ├── [4.0K] node_modules │   │   ├── [4.0K] @esbuild │   │   │   └── [4.0K] linux-x64 │   │   │   ├── [4.0K] bin │   │   │   │   └── [9.8M] esbuild │   │   │   ├── [ 372] package.json │   │   │   └── [ 141] README.md │   │   ├── [4.0K] esbuild │   │   │   ├── [4.0K] bin │   │   │   │   └── [9.8M] esbuild │   │   │   ├── [ 11K] install.js │   │   │   ├── [4.0K] lib │   │   │   │   ├── [ 23K] main.d.ts │   │   │   │   └── [ 86K] main.js │   │   │   ├── [1.0K] LICENSE.md │   │   │   ├── [1.4K] package.json │   │   │   └── [ 175] README.md │   │   ├── [4.0K] nanoid │   │   │   ├── [4.0K] async │   │   │   │   ├── [2.6K] index.browser.cjs │   │   │   │   ├── [ 984] index.browser.js │   │   │   │   ├── [2.8K] index.cjs │   │   │   │   ├── [1.5K] index.d.ts │   │   │   │   ├── [ 982] index.js │   │   │   │   ├── [ 820] index.native.js │   │   │   │   └── [ 233] package.json │   │   │   ├── [4.0K] bin │   │   │   │   └── [1.1K] nanoid.cjs │   │   │   ├── [2.8K] index.browser.cjs │   │   │   ├── [1.0K] index.browser.js │   │   │   ├── [3.3K] index.cjs │   │   │   ├── [2.2K] index.d.cts │   │   │   ├── [2.2K] index.d.ts │   │   │   ├── [1.3K] index.js │   │   │   ├── [1.1K] LICENSE │   │   │   ├── [ 169] nanoid.js │   │   │   ├── [4.0K] non-secure │   │   │   │   ├── [1.1K] index.cjs │   │   │   │   ├── [ 983] index.d.ts │   │   │   │   ├── [ 497] index.js │   │   │   │   └── [ 99] package.json │   │   │   ├── [2.2K] package.json │   │   │   ├── [1.5K] README.md │   │   │   └── [4.0K] url-alphabet │   │   │   ├── [ 280] index.cjs │   │   │   ├── [ 110] index.js │   │   │   └── [ 99] package.json │   │   ├── [4.0K] picocolors │   │   │   ├── [ 787] LICENSE │   │   │   ├── [ 552] package.json │   │   │   ├── [ 598] picocolors.browser.js │   │   │   ├── [ 138] picocolors.d.ts │   │   │   ├── [2.6K] picocolors.js │   │   │   ├── [ 622] README.md │   │   │   └── [1013] types.d.ts │   │   ├── [4.0K] postcss │   │   │   ├── [4.0K] lib │   │   │   │   ├── [3.3K] at-rule.d.ts │   │   │   │   ├── [ 471] at-rule.js │   │   │   │   ├── [1.7K] comment.d.ts │   │   │   │   ├── [ 203] comment.js │   │   │   │   ├── [ 14K] container.d.ts │   │   │   │   ├── [ 10K] container.js │   │   │   │   ├── [6.4K] css-syntax-error.d.ts │   │   │   │   ├── [3.3K] css-syntax-error.js │   │   │   │   ├── [3.8K] declaration.d.ts │   │   │   │   ├── [ 495] declaration.js │   │   │   │   ├── [1.9K] document.d.ts │   │   │   │   ├── [ 654] document.js │   │   │   │   ├── [ 162] fromJSON.d.ts │   │   │   │   ├── [1.5K] fromJSON.js │   │   │   │   ├── [5.1K] input.d.ts │   │   │   │   ├── [6.6K] input.js │   │   │   │   ├── [4.9K] lazy-result.d.ts │   │   │   │   ├── [ 13K] lazy-result.js │   │   │   │   ├── [1.4K] list.d.ts │   │   │   │   ├── [1.2K] list.js │   │   │   │   ├── [9.5K] map-generator.js │   │   │   │   ├── [ 14K] node.d.ts │   │   │   │   ├── [ 10K] node.js │   │   │   │   ├── [1.5K] no-work-result.d.ts │   │   │   │   ├── [2.6K] no-work-result.js │   │   │   │   ├── [ 135] parse.d.ts │   │   │   │   ├── [1.1K] parse.js │   │   │   │   ├── [ 15K] parser.js │   │   │   │   ├── [1.0K] postcss.d.mts │   │   │   │   ├── [ 11K] postcss.d.ts │   │   │   │   ├── [2.8K] postcss.js │   │   │   │   ├── [ 980] postcss.mjs │   │   │   │   ├── [1.8K] previous-map.d.ts │   │   │   │   ├── [3.9K] previous-map.js │   │   │   │   ├── [3.3K] processor.d.ts │   │   │   │   ├── [1.7K] processor.js │   │   │   │   ├── [4.3K] result.d.ts │   │   │   │   ├── [ 738] result.js │   │   │   │   ├── [2.3K] root.d.ts │   │   │   │   ├── [1.2K] root.js │   │   │   │   ├── [2.9K] rule.d.ts │   │   │   │   ├── [ 569] rule.js │   │   │   │   ├── [1.4K] stringifier.d.ts │   │   │   │   ├── [8.0K] stringifier.js │   │   │   │   ├── [ 165] stringify.d.ts │   │   │   │   ├── [ 213] stringify.js │   │   │   │   ├── [ 91] symbols.js │   │   │   │   ├── [1.4K] terminal-highlight.js │   │   │   │   ├── [6.4K] tokenize.js │   │   │   │   ├── [2.9K] warning.d.ts │   │   │   │   ├── [ 739] warning.js │   │   │   │   └── [ 256] warn-once.js │   │   │   ├── [1.1K] LICENSE │   │   │   ├── [2.4K] package.json │   │   │   └── [1.2K] README.md │   │   ├── [4.0K] @rollup │   │   │   ├── [4.0K] rollup-linux-x64-gnu │   │   │   │   ├── [ 417] package.json │   │   │   │   ├── [ 95] README.md │   │   │   │   └── [2.5M] rollup.linux-x64-gnu.node │   │   │   └── [4.0K] rollup-linux-x64-musl │   │   │   ├── [ 419] package.json │   │   │   ├── [ 97] README.md │   │   │   └── [2.5M] rollup.linux-x64-musl.node │   │   ├── [4.0K] rollup │   │   │   ├── [4.0K] dist │   │   │   │   ├── [4.0K] bin │   │   │   │   │   └── [ 79K] rollup │   │   │   │   ├── [4.0K] es │   │   │   │   │   ├── [2.0K] getLogFilter.js │   │   │   │   │   ├── [ 17] package.json │   │   │   │   │   ├── [ 293] parseAst.js │   │   │   │   │   ├── [ 441] rollup.js │   │   │   │   │   └── [4.0K] shared │   │   │   │   │   ├── [897K] node-entry.js │   │   │   │   │   ├── [ 81K] parseAst.js │   │   │   │   │   └── [245K] watch.js │   │   │   │   ├── [ 171] getLogFilter.d.ts │   │   │   │   ├── [2.1K] getLogFilter.js │   │   │   │   ├── [ 471] loadConfigFile.d.ts │   │   │   │   ├── [ 705] loadConfigFile.js │   │   │   │   ├── [3.5K] native.js │   │   │   │   ├── [ 134] parseAst.d.ts │   │   │   │   ├── [ 506] parseAst.js │   │   │   │   ├── [ 32K] rollup.d.ts │   │   │   │   ├── [4.6K] rollup.js │   │   │   │   └── [4.0K] shared │   │   │   │   ├── [ 884] fsevents-importer.js │   │   │   │   ├── [235K] index.js │   │   │   │   ├── [ 21K] loadConfigFile.js │   │   │   │   ├── [ 90K] parseAst.js │   │   │   │   ├── [896K] rollup.js │   │   │   │   ├── [ 18K] watch-cli.js │   │   │   │   └── [ 10K] watch.js │   │   │   ├── [ 34K] LICENSE.md │   │   │   ├── [ 11K] package.json │   │   │   └── [9.8K] README.md │   │   ├── [4.0K] source-map-js │   │   │   ├── [4.0K] lib │   │   │   │   ├── [3.1K] array-set.js │   │   │   │   ├── [1.5K] base64.js │   │   │   │   ├── [4.6K] base64-vlq.js │   │   │   │   ├── [4.1K] binary-search.js │   │   │   │   ├── [2.3K] mapping-list.js │   │   │   │   ├── [4.0K] quick-sort.js │   │   │   │   ├── [ 40] source-map-consumer.d.ts │   │   │   │   ├── [ 41K] source-map-consumer.js │   │   │   │   ├── [ 41] source-map-generator.d.ts │   │   │   │   ├── [ 15K] source-map-generator.js │   │   │   │   ├── [ 33] source-node.d.ts │   │   │   │   ├── [ 13K] source-node.js │   │   │   │   └── [ 15K] util.js │   │   │   ├── [1.5K] LICENSE │   │   │   ├── [2.5K] package.json │   │   │   ├── [ 25K] README.md │   │   │   ├── [3.3K] source-map.d.ts │   │   │   └── [ 405] source-map.js │   │   ├── [4.0K] @types │   │   │   └── [4.0K] estree │   │   │   ├── [4.7K] flow.d.ts │   │   │   ├── [ 18K] index.d.ts │   │   │   ├── [1.1K] LICENSE │   │   │   ├── [ 829] package.json │   │   │   └── [ 443] README.md │   │   └── [4.0K] vite │   │   ├── [4.0K] bin │   │   │   ├── [2.6K] openChrome.applescript │   │   │   └── [2.5K] vite.js │   │   ├── [5.0K] client.d.ts │   │   ├── [4.0K] dist │   │   │   ├── [4.0K] client │   │   │   │   ├── [ 31K] client.mjs │   │   │   │   └── [ 643] env.mjs │   │   │   ├── [4.0K] node │   │   │   │   ├── [4.0K] chunks │   │   │   │   │   ├── [ 13K] dep-3RmXg9uo.js │   │   │   │   │   ├── [1.6M] dep-B0fRCRkQ.js │   │   │   │   │   ├── [230K] dep-BL6Xg-kE.js │   │   │   │   │   ├── [332K] dep-CvfTChi5.js │   │   │   │   │   └── [ 19K] dep-dLifoI9c.js │   │   │   │   ├── [ 28K] cli.js │   │   │   │   ├── [4.1K] constants.js │   │   │   │   ├── [149K] index.d.ts │   │   │   │   ├── [5.2K] index.js │   │   │   │   ├── [ 10K] module-runner.d.ts │   │   │   │   ├── [ 51K] module-runner.js │   │   │   │   └── [2.9K] moduleRunnerTransport.d-CXw_Ws6P.d.ts │   │   │   └── [4.0K] node-cjs │   │   │   └── [163K] publicUtils.cjs │   │   ├── [2.6K] index.cjs │   │   ├── [ 209] index.d.cts │   │   ├── [117K] LICENSE.md │   │   ├── [4.0K] misc │   │   │   ├── [ 21] false.js │   │   │   └── [ 20] true.js │   │   ├── [5.1K] package.json │   │   ├── [1.1K] README.md │   │   └── [4.0K] types │   │   ├── [1.1K] customEvent.d.ts │   │   ├── [1.2K] hmrPayload.d.ts │   │   ├── [1003] hot.d.ts │   │   ├── [1.9K] importGlob.d.ts │   │   ├── [ 249] import-meta.d.ts │   │   ├── [ 510] importMeta.d.ts │   │   ├── [4.0K] internal │   │   │   ├── [1.9K] cssPreprocessorOptions.d.ts │   │   │   └── [ 520] lightningcssOptions.d.ts │   │   ├── [ 193] metadata.d.ts │   │   └── [ 109] package.json │   ├── [ 235] package.json │   ├── [ 28K] package-lock.json │   ├── [4.0K] public │   │   └── [1.5K] vite.svg │   └── [4.0K] src │   ├── [ 247] counter.js │   ├── [ 995] javascript.svg │   ├── [ 746] main.js │   └── [1.5K] style.css ├── [ 586] docker-compose.yml ├── [ 680] Dockerfile └── [3.5K] README.md 43 directories, 203 files
Shenlong Bot has cached this for you
Remarks
    1. It is advised to access via the original source first.
    2. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
    3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →