CVE-2025-22381 の詳細な分析と Proof-of-Concept。Aggie オープンソースプロジェクトで発見された Host Header インジェクションの脆弱性です。
脆弱性の概要+
CVE ID: CVE-2025-22381
公開: 2025年10月(MITRE による割り当て)
一般公開: 2026年2月
報告者: Anas Abderrahman Benbarek
発見日: 2025年9月17日
影響を受けるプロジェクト: TID-Lab/aggie
影響を受けるバージョン: 全バージョン(2.6.1 以前を含む。2026年2月時点で修正は未適用)
深刻度: 中〜高(推定 CVSS 約7.1〜7.5)
影響: パスワードリセットトークンの窃取につながるフィッシング攻撃を可能にし、アカウント乗っ取りの可能性があります。
背景
私は GitHub 上のオープンソースの node.js プロジェクト、特に認証フローを扱うプロジェクトのレビューにかなりの時間を費やしています。2025年9月、Aggie リポジトリを調べているときに、パスワードリセットのロジックにすぐに目につく点があることに気づきました。日常的なコードリーディングとして始まったものが、結局は CVE-2025-22381 に行き着きました。これは、攻撃者がパスワードリセットメールのドメインを制御できるようにする、典型的な Host Header インジェクションの脆弱性です。
発見の経緯
リポジトリをクローンし、lib/api/ 配下のファイルを読み始め、認証とメール生成に関連するものに焦点を当てました。
lib/api/reset-password.js には /reset-password のエンドポイントロジックが含まれています。重要な部分は sendEmail ヘルパー内にあります:
function sendEmail(user, req, callback) { var token = encodeToken(user);
mailer.sendFromTemplate({ template: 'forgotPassword', user: user, token: token, host: req.headers.host, // ← vulnerable protocol: req.protocol, acceptLanguage: req.headers['accept-language'] }, callback); }
host: req.headers.host という行が問題です。express では req.headers.host は Host HTTP ヘッダーから直接取得され、これは完全に攻撃者が制御できます。検証も、ホワイトリストも、設定からの信頼できるドメインへのフォールバックもありません。
初期確認
README の指示(Ubuntu、nvm、npm install、テスト用 SMTP を設定した secrets.json)に従ってすぐにローカルインスタンスをセットアップし、サーバーを起動してパスワードリセットを実行しました。生成されたメールのリンクは想定どおり localhost:3000 を使用していました。
次に、改ざんした Host ヘッダーでリクエストを再送信しました:
curl -X POST http://localhost:3000/reset-password
-H "Host: evil-phish.example"
-d "email=[email protected]"
(MailHog で取得した)メールには次のものが含まれていました: http://evil-phish.example/reset-password?token=...
証明完了です。このアプリケーションはリセットリンクを構築する際に、クライアントが指定した Host ヘッダーを信頼しています。
攻撃が実際に機能する仕組み
攻撃者は被害者のメールアドレスに対してパスワードリセットリクエストを送信しますが、Host ヘッダーは自身が制御するドメイン(例: evil-phish.example)に設定します。
Aggie は正規のリセットトークンを生成します(サーバーサイド、時間制限付き、設定の秘密鍵で暗号化)。
実際のドメインの代わりに攻撃者のドメインへのリンクを含むメールが送信されます。
被害者はメールを受信してリンクをクリックします(フィッシング成功条件)。
被害者は攻撃者のサーバーに誘導されます。
攻撃者のサーバーは次のことが可能です:
偽の「リセット失敗」ページを表示してトークンを静かに破棄する、または
クエリ文字列からトークンを取得する(サーバーサイドのログ記録または JavaScript による)、または
リクエストを実際の Aggie インスタンスにプロキシし、トークンを取得して、ユーザーを正規のリセットページに転送する(被害者がすぐに異常に気づかないようにするため)。
攻撃者は後で実際のドメイン上で取得したトークンを使用して、被害者のパスワードをリセットします。
重要なポイント: Host Header インジェクション単独では、攻撃者がトークンを直接使用できるようにはなりません。トークンが攻撃者のインフラに届くには、被害者が悪意のあるリンクを訪問する必要があります。そのため、これはユーザーの操作なしに直接アカウントを乗っ取る脆弱性ではなく、フィッシングを可能にする脆弱性です。
技術的な深刻度と影響
これは文脈によっては中〜高程度の深刻度の問題です:
AV:N: ネットワークから到達可能
PR:N: 特権は不要
AC:L: 複雑度は低い
UI:R: ユーザーの操作が必要
S:C: スコープが変更される可能性あり(影響は正規ドメイン上の被害者のアカウントにまで及ぶ)
C:L / I:H: 被害者のアカウントへの機密性・完全性への影響
多くのデータベースでは CVSS 約7.1〜7.5 の範囲で掲載されています。Aggie が(選挙や危機などの)機密性の高い監視に使用される本番環境では、ここでのフィッシングの成功が完全なアカウント乗っ取りにつながる可能性があるため、個人的には深刻だと考えています。
Proof-of-Concept(詳細かつ再現可能)
環境
Ubuntu 18.04/20.04(推奨に従う)
Node 12.16(.nvmrc に従う)
MailHog: メール取得用にローカルで実行(docker run -d -p 8025:8025 -p 1025:1025 mailhog/mailhog)
Aggie は email.transport が localhost:1025 を指すように設定
手順
Aggie をクローンして起動:
git clone https://github.com/TID-Lab/aggie.git cd aggie nvm install npm install cp config/secrets.json.example config/secrets.json secrets.json を編集 → adminPassword を設定、必要に応じてテスト SMTP を追加 npm start
Web UI または MongoDB に直接アクセスしてテストユーザーを作成します。
悪意のあるリセットを実行:
curl -i -X POST http://localhost:3000/reset-password
-H "Host: evil-phish.example"
-H "Content-Type: application/x-www-form-urlencoded"
-d "email=[email protected]"
MailHog を開く: 送信されたメールを検査します。リセットリンクは http://evil-phish.example/reset-password?token=... を指しています。
開示のタイムライン
2025年9月17日: 発見 + ローカル PoC
2025年9月17日: 詳細と PoC を [email protected] にメール送信
2025年9月17日: MITRE に提出(サービスリクエスト 1926730 / MCID15453119)
2025年10月9日: MITRE が CVE-2025-22381 を割り当て
2025年10月〜12月: 公開パッチや応答は確認されず
2026年2月: 一般公開(本記事)
推奨される修正
コードの変更
lib/api/reset-password.js の脆弱な行を信頼できる値に置き換えます:
// In lib/api/reset-password.js, inside sendEmail() const config = require('../../config/secrets').get();
// Option A: Hard trust config value (recommended for single-domain) const host = config.appHost || 'localhost:3000';
// Then use it: mailer.sendFromTemplate({ template: 'forgotPassword', user: user, token: token, host: host, // Use the trusted variable protocol: config.environment === 'production' ? 'https' : req.protocol, acceptLanguage: req.headers['accept-language'] }, callback);
設定
secrets.json に追加:
"appHost": "https://your-real-domain.com"
最後に
Host Header インジェクションは、2025〜2026年においても驚くほど一般的なままです。特に、何年も前に開始され、十分に監査されていないプロジェクトで顕著です。Aggie はシビックテックや危機監視にとって貴重なツールです — メンテナーの皆さんがすぐに修正を適用してくれることを願っています。
Aggie を保守または使用している場合は、デプロイを確認し、公式リリースが提供されるまで手動でパッチを適用してください。質問がある場合や、他のプロジェクトでの同様の問題について議論したい場合は、お気軽にご連絡ください。
読んでいただきありがとうございます。どうぞお気をつけて。