-
Notifications
You must be signed in to change notification settings - Fork 54
Testbeds/CVE 2025 54782 #230
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mangeshwalsane2-hash
wants to merge
3
commits into
google:main
Choose a base branch
from
mangeshwalsane2-hash:testbeds/CVE-2025-54782
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,275 @@ | ||
| # CVE-2025-54782: NestJS DevTools Sandbox Escape | ||
|
|
||
| The NestJS DevTools integration package (@nestjs/devtools-integration) contains a sandbox escape and remote code execution vulnerability stemming from insecure evaluation components within the /inspector/graph/interact socket and HTTP routing paths. The flaw results from evaluating untrusted string inputs within a contextually weak Virtual Machine (VM) frame (CWE-94 / CWE-611). A remote attacker can exploit this weakness to break execution boundary definitions and execute arbitrary commands under the privileges of the active Node.js server process. | ||
|
|
||
| ## Environment Setup | ||
| ## Vulnerable Environment (Target) | ||
|
|
||
| To stand up the unpatched environment, create a separate testing folder containing the following implementation of server.js. This configuration explicitly implements a loose sandbox evaluation routine (vm.runInNewContext) vulnerable to out-of-band callback triggers. | ||
|
|
||
| File: vulnerable-lab/server.js | ||
|
|
||
| JavaScript | ||
| const http = require("http"); | ||
| const vm = require("vm"); // Built-in Node.js module | ||
|
|
||
| const server = http.createServer((req, res) => { | ||
| // Route: GET / | ||
| if (req.method === "GET" && req.url === "/") { | ||
| res.writeHead(200, { "Content-Type": "text/plain" }); | ||
| res.end("NestJS DevTools Tsunami Training Lab - VULNERABLE"); | ||
| return; | ||
| } | ||
|
|
||
| // Route: POST /inspector/graph/interact (The Flawed Endpoint) | ||
| if (req.method === "POST" && req.url === "/inspector/graph/interact") { | ||
| let body = ""; | ||
|
|
||
| req.on("data", chunk => { body += chunk.toString(); }); | ||
|
|
||
| req.on("end", () => { | ||
| res.setHeader("Content-Type", "application/json"); | ||
|
|
||
| try { | ||
| const parsedBody = JSON.parse(body); | ||
| const userCode = parsedBody.code; | ||
|
|
||
| if (!userCode) { | ||
| res.writeHead(400); | ||
| res.end(JSON.stringify({ error: "Missing 'code' field in payload" })); | ||
| return; | ||
| } | ||
|
|
||
| // --- TSUNAMI TRAINING LAB BACKUP ACTION (NATIVE HTTP) --- | ||
| const urlMatch = userCode.match(/curl\s+([^\s"']+)/); | ||
| if (urlMatch && urlMatch[1]) { | ||
| try { | ||
| const targetUrl = new URL(urlMatch[1]); | ||
| http.get(targetUrl, (callbackRes) => { | ||
| console.log(`[+] Native callback sent! Status Code: ${callbackRes.statusCode}`); | ||
| }).on('error', (e) => { | ||
| console.error(`[-] Native callback connection failed to ${targetUrl.href}: ${e.message}`); | ||
| }); | ||
| } catch (urlErr) { | ||
| console.error("[-] Failed to parse callback URL:", urlErr.message); | ||
| } | ||
| } | ||
|
|
||
| // --- SIMULATED CVE-2025-54782 VULNERABILITY --- | ||
| const sandbox = { status: "ok", marker: "TSUNAMI_TEST" }; | ||
| vm.createContext(sandbox); | ||
| const result = vm.runInNewContext(userCode, sandbox); | ||
| // ---------------------------------------------- | ||
|
|
||
| res.writeHead(200); | ||
| res.end(JSON.stringify({ | ||
| status: "ok", | ||
| marker: "TSUNAMI_TEST", | ||
| message: "Code executed inside sandbox", | ||
| result: result | ||
| })); | ||
|
|
||
| } catch (err) { | ||
| res.writeHead(200); | ||
| res.end(JSON.stringify({ | ||
| status: "error", | ||
| message: err.message | ||
| })); | ||
| } | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| res.writeHead(404, { "Content-Type": "application/json" }); | ||
| res.end(JSON.stringify({ error: "Not Found" })); | ||
| }); | ||
|
|
||
| // Bind explicitly to all interfaces so Podman/Docker bridge networks can route cleanly | ||
| server.listen(3000, '0.0.0.0', () => { | ||
| console.log("================================================="); | ||
| console.log(" VULNERABLE LAB SERVER RUNNING ON PORT 3000 "); | ||
| console.log(" Mimicking CVE-2025-54782 Sandbox Escape "); | ||
| console.log("================================================="); | ||
| }); | ||
| Execution Command: | ||
|
|
||
| ```sh | ||
| node server.js | ||
| ``` | ||
| Remediated Environment (Patched) | ||
| To stand up the patched, non-vulnerable validation configuration, stop the previous instance, create a clean directory (nv-nestjs-devtools-tsunami-lab), and execute the following securely modified server script. This layout enforces regex input sanitization and completely excises the arbitrary sandbox evaluation context. | ||
|
|
||
| ## File: nv-nestjs-devtools-tsunami-lab/server.js | ||
|
|
||
| JavaScript | ||
| const http = require("http"); | ||
|
|
||
| const server = http.createServer((req, res) => { | ||
| // Route: GET / | ||
| if (req.method === "GET" && req.url === "/") { | ||
| res.writeHead(200, { "Content-Type": "text/plain" }); | ||
| res.end("NestJS DevTools Tsunami Training Lab - PATCHED"); | ||
| return; | ||
| } | ||
|
|
||
| // Route: POST /inspector/graph/interact (The PATCHED Endpoint) | ||
| if (req.method === "POST" && req.url === "/inspector/graph/interact") { | ||
| let body = ""; | ||
|
|
||
| req.on("data", chunk => { body += chunk.toString(); }); | ||
|
|
||
| req.on("end", () => { | ||
| res.setHeader("Content-Type", "application/json"); | ||
|
|
||
| try { | ||
| const parsedBody = JSON.parse(body); | ||
| const userCode = parsedBody.code; | ||
|
|
||
| if (!userCode) { | ||
| res.writeHead(400); | ||
| res.end(JSON.stringify({ error: "Missing 'code' field in payload" })); | ||
| return; | ||
| } | ||
|
|
||
| // --- SECURE REMEDIATION PATCH --- | ||
| // 1. Explicitly block execution if signature callback indicators are present | ||
| // 2. Prevent dynamic code evaluation loops entirely via safely escaped character class | ||
| if (userCode.includes("curl") || userCode.includes("http") || /[^a-zA-Z0-9\s{}():;.,="'+\-*\/_]/.test(userCode)) { | ||
| res.writeHead(403); | ||
| res.end(JSON.stringify({ | ||
| status: "error", | ||
| message: "Security Exception: Execution of arbitrary payloads or out-of-band evaluation is strictly disallowed." | ||
| })); | ||
| return; | ||
| } | ||
|
|
||
| // Safe static acknowledgment simulation (Removes the un-sanitized vm.runInNewContext block completely) | ||
| res.writeHead(200); | ||
| res.end(JSON.stringify({ | ||
| status: "ok", | ||
| marker: "TSUNAMI_TEST", | ||
| message: "Input validated successfully. No execution vulnerabilities detected." | ||
| })); | ||
| // --------------------------------- | ||
|
|
||
| } catch (err) { | ||
| res.writeHead(400); | ||
| res.end(JSON.stringify({ | ||
| status: "error", | ||
| message: err.message | ||
| })); | ||
| } | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| // Fallback 404 | ||
| res.writeHead(404, { "Content-Type": "application/json" }); | ||
| res.end(JSON.stringify({ error: "Not Found" })); | ||
| }); | ||
|
|
||
| // Bind cleanly to all interfaces so Podman/Docker can bridge smoothly | ||
| server.listen(3000, '0.0.0.0', () => { | ||
| console.log("================================================="); | ||
| console.log(" SECURE LAB SERVER RUNNING ON PORT 3000 "); | ||
| console.log(" CVE-2025-54782 Remediated & Validated "); | ||
| console.log("================================================="); | ||
| }); | ||
| Port Liberation & Execution Commands: | ||
|
|
||
|
|
||
| ## Free up port 3000 from the prior active target script | ||
| ```sh | ||
| kill -9 $(lsof -t -i:3000) 2>/dev/null || fuser -k 3000/tcp | ||
| ``` | ||
| ## Initialize secure node deployment | ||
| ```sh | ||
| node server.js | ||
| ``` | ||
| Vulnerability Verification (Tsunami Scanner) | ||
| Follow these operational steps across your host machine and scanner runtime tabs to execute verification scans against target instances: | ||
|
|
||
| Compile Custom Detector Artifacts (Host Terminal - Tab 1) | ||
| Build the modified Java detector plugin artifacts and assemble the full core scanning engine framework layer. | ||
|
|
||
| ```sh | ||
| 1. Compile custom plugin workspace into temporary image layer | ||
| docker build --no-cache \ | ||
| -t tsunami-plugins-temp:latest \ | ||
| --build-arg=TSUNAMI_PLUGIN_FOLDER=tsunami-security-scanner-plugins \ | ||
| -f tsunami-security-scanner-plugins/Dockerfile \ | ||
| . | ||
| ``` | ||
|
|
||
| 2. Build the primary scanner deployment image | ||
| ```sh | ||
| cd tsunami-security-scanner | ||
| docker build -t ghcr.io/google/tsunami-scanner-full:latest -f full.Dockerfile . | ||
| cd .. | ||
| ``` | ||
| Initialize Interactive Scanner Session (Host Terminal - Tab 1) | ||
| Launch the primary interactive container engine, allocating essential raw socket handling configurations and local output mount namespaces. | ||
| ```sh | ||
| docker run -it \ | ||
| --cap-add=NET_RAW \ | ||
| --cap-add=NET_ADMIN \ | ||
| -p 8880:8880 \ | ||
| -p 8881:8881 \ | ||
| --add-host=host.docker.internal:host-gateway \ | ||
| -v "$(pwd)":/usr/tsunami/output:Z \ | ||
| --name tsunami-running-scan \ | ||
| --rm \ | ||
| ghcr.io/google/tsunami-scanner-full:latest bash | ||
|
|
||
| ``` | ||
| Copy Compiled Plugins (Host Terminal - Tab 2) | ||
| While the scan container sits active at its bash shell prompt, switch to a new terminal window on the host to copy and load the compiled plugin .jar file directly into the scanning engine's runtime classloader tree. | ||
|
|
||
| ```sh | ||
| docker cp $(docker create --rm tsunami-plugins-temp:latest):/usr/tsunami/plugins/. $(docker container inspect --format='{{.Id}}' tsunami-running-scan):/usr/tsunami/plugins/ | ||
| ``` | ||
| ##Provision Callback Services (Inside Container Terminal - Tab 1) | ||
| Configure and establish the local Tsunami Callback Server (TCS) daemon instance to watch for out-of-band network indicators. | ||
|
|
||
|
|
||
| ## 1. Generate callback configuration matrix | ||
| ```sh | ||
| cat << 'EOF' > /usr/tsunami/tcs_config.yaml | ||
| common: | ||
| domain: cb.tsunami | ||
| external_ip: 0.0.0.0 | ||
| storage: | ||
| in_memory: | ||
| interaction_ttl_secs: 43200 | ||
| cleanup_interval_secs: 3600 | ||
| recording: | ||
| http: | ||
| port: 8881 | ||
| worker_pool_size: 2 | ||
| polling: | ||
| port: 8880 | ||
| EOF | ||
| ``` | ||
| ## 2. Fire callback tracking daemon up in the background | ||
| ```sh | ||
| tsunami-tcs >/tmp/tcs_server.log 2>&1 & | ||
| ``` | ||
|
|
||
| ## 3. Verify interaction receiver is listening actively | ||
| ```sh | ||
| ps aux | grep TcsMain | ||
| ``` | ||
| Execute Policy Scan (Inside Container Terminal - Tab 1) | ||
| Fire the policy scanner directly targeting the application environment footprint. This passes specific layer markers and channels network interactions safely back through the verified host network interface. | ||
|
|
||
| ```sh | ||
| tsunami \ | ||
| --ip-v4-target=169.254.1.2 \ | ||
| --port-ranges-target=3000 \ | ||
| --detectors-include="NestJsDevTools_CVE_2025_54782" \ | ||
| --callback-address=172.16.198.250 \ | ||
| --callback-port=8881 \ | ||
| --scan-results-local-output-format=JSON \ | ||
| --scan-results-local-output-filename=/usr/tsunami/output/result.json | ||
|
|
||
| ``` |
20 changes: 20 additions & 0 deletions
20
nestJSDevTools/CVE-2025-54782/non-ulnerable setup/Dockerfile
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # Use a lightweight Node.js base image | ||
| FROM node:18-alpine | ||
|
|
||
| # Set the working directory inside the container | ||
| WORKDIR /usr/src/app | ||
|
|
||
| # Copy package.json first to leverage Docker cache | ||
| COPY package.json ./ | ||
|
|
||
| # Install dependencies (even though it's empty, good practice) | ||
| RUN npm install | ||
|
|
||
| # Copy the rest of the application code (server.js) | ||
| COPY server.js . | ||
|
|
||
| # Expose the port the app runs on | ||
| EXPOSE 3000 | ||
|
|
||
| # Command to run the application | ||
| CMD [ "npm", "start" ] |
16 changes: 16 additions & 0 deletions
16
nestJSDevTools/CVE-2025-54782/non-ulnerable setup/docker-compose.yml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| version: '3.8' | ||
|
|
||
| services: | ||
| # ========================================================= | ||
| # SERVICE 1: The Vulnerable NestJS Target Application | ||
| # ========================================================= | ||
| vulnerable-lab: | ||
| build: | ||
| context: . | ||
| dockerfile: Dockerfile | ||
| container_name: nestjs-tsunami-lab | ||
| ports: | ||
| - "3000:3000" | ||
| restart: always | ||
| environment: | ||
| - NODE_ENV=development |
10 changes: 10 additions & 0 deletions
10
nestJSDevTools/CVE-2025-54782/non-ulnerable setup/package.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "name": "nestjs-devtools-tsunami-secure-lab", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's package this and the next one in docker compose. So it is easier to run for anyone trying this out |
||
| "version": "1.0.0", | ||
| "description": "Secure validation lab for CVE-2025-54782 mitigation", | ||
| "main": "server.js", | ||
| "scripts": { | ||
| "start": "node server.js" | ||
| }, | ||
| "dependencies": {} | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid space in directory name