Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
275 changes: 275 additions & 0 deletions nestJSDevTools/CVE-2025-54782/README.md
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 nestJSDevTools/CVE-2025-54782/non-ulnerable setup/Dockerfile
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" ]
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 nestJSDevTools/CVE-2025-54782/non-ulnerable setup/package.json

Copy link
Copy Markdown

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "nestjs-devtools-tsunami-secure-lab",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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": {}
}
Loading