PaPoo
cover
technews
Author
technews
世界の技術ニュースをリアルタイムでキャッチし、日本語でわかりやすく発信。AI・半導体・スタートアップから規制動向まで、グローバルテックシーンの「今」をお届けします。

Qubes OSのdom0に届くエラーメッセージが、思わぬ侵入口になった

Qubes OSが、セキュリティ勧告QSB-118を出しました。対象は、ファイルコピー機能 qvm-copy-to-vm のエラー表示まわりにある欠陥で、条件がそろうと dom0 で任意のコマンドを実行されるおそれがあるという話です。Qubes OSは「仮想マシン同士を厳しく隔離する」ことを売りにしているので、中心にある管理領域である dom0 に話が及ぶだけでも重い。しかも今回の欠陥は、コピー失敗時の表示という、ふだん見落としがちな部分に潜んでいました。

qvm-copy-to-vm の失敗通知が dom0 を巻き込むまで

Qubes Security Bulletin 118 は、2026-08-28 付の「Dom0 arbitrary code execution in qvm-copy-to-vm error reporting」です。Qubes チームは、qvm-copy-to-vm を使って dom0 から悪意ある qube にファイルを送ろうとしたとき、その qube が dom0 に任意のコマンドを注入できると説明しています。利用者が dom0 側からコピー操作を始めた場面で、コピー先の qube がすでに侵害されていると、攻撃者はこの欠陥を使って dom0 を乗っ取れる、という整理です。

仕組みはやや地味ですが、筋は単純です。qvm-copy-to-vm は dom0 から指定した qube にファイルを送る道具で、内部では qfile という簡略化されたアーカイブ形式を使います。転送の最後には、受け取り側から送信側へ確認情報が返ります。そこには、転送したファイル群の checksum、エラーコード、最後に受け取ったファイル名が含まれます。問題はエラーが返ったときで、dom0 はそのファイル名を含めた GUI のエラーメッセージを表示します。

欠陥は、この「最後に受け取ったファイル名」の扱いにありました。wait_for_result() は受け取った名前に sanitize_remote_filename() を通しますが、その処理は非 ASCII 文字とダブルクォートを _ に置き換えるだけです。空白やシェルのメタ文字までは消しません。その後、display_error()system() を使って kdialogzenity を起動します。しかも、そのコマンド文字列の中に、攻撃者が細工した名前が埋め込まれる構造になっていました。つまり、見た目は単なるエラーダイアログでも、実際にはシェルに渡される文字列だったわけです。

Qubes チームは、VM 側の qvm-copy-to-vm 実装は影響を受けないとも明記しています。こちらの gui_fatal()system() を使わず、execlp()zenitykdialog を直接呼び出します。少なくともこの差が、被害の広がりを分けています。影響範囲は「すべての Qubes OS リリース」とされ、修正済みパッケージとしては Qubes 4.3 の dom0 向け qubes-core-dom0-linux 4.3.22 が挙げられています。利用者は通常どおり更新を続ければよく、今回の QSB に対して追加の操作は不要です。

ただの表示処理が、なぜここまで危ないのか

まず引っかかるのは、攻撃の入口が「コピー先のファイル名エラー」だという点です。普通なら、ファイル名は表示用の文字列でしかないと思いがちです。しかし今回は、その表示用文字列が system() に流れ込んでいました。ここが怖い。エラー処理は本来、防御の最後の砦のはずなのに、そこでシェル解釈が入ると逆に実行経路になってしまう。しかもコピー失敗時なので、開発側も「通常フローではない」と考えて見落としやすい場所です。私は、この種の不具合が消えにくいのは、機能追加よりも例外処理のほうがレビューで軽く扱われがちだからではないかと思います。

もうひとつ重要なのは、sanitize_remote_filename() が「サニタイズしているつもり」になりやすいことです。非 ASCII 文字と " を消しているので、一見すると危険物は取り除けているように見えます。けれどシェルの世界では、空白、;&$() など、まだまだ危険な文字が残っています。ここでの失敗は、入力検証の不足というより、「何に対して安全でなければならないか」を取り違えたことにあります。ファイル名を人間に見せるのか、シェルに食わせるのかで、必要な防御はまるで違う。Qubes OS のように隔離を売りにする製品であっても、この手の境界の取り違えは起きるのだと分かります。

さらに気になるのは、dom0 が常に“特別に安全”とは限らない、という現実です。Qubes OS は dom0 をネットワークから切り離し、他の qube と分離して使う設計ですが、今回のようにユーザーが dom0 からファイルを送る操作をした瞬間、その隔離の外にあるはずの qube との間に信頼の連鎖が生まれます。侵害済みの qube が相手なら、そこで返ってくる情報は信用できません。Qubes OS の思想からすると当然の教訓ですが、実装の細部では「相手は敵かもしれない」という前提が抜け落ちた。私はここに、この事件のいちばん大きな教訓があると思います。

修正の方向性は、見ればかなりまっとうです。VM 側の実装が execlp() を使っているのに対し、dom0 側は system() を使っていた。つまり、危険な抽象化を一段下げれば避けられた種類の事故です。こうした差は、脆弱性そのもの以上に、コードベースの設計文化を映します。安全なプロダクトでも、危険な呼び出しをひとつ残すだけで全体の意味が変わる。Qubes OS の利用者は更新を続けるだけでよいとはいえ、開発者側には「エラーメッセージは安全な領域ではない」という、かなり厳しい戒めが残ったのではないでしょうか。


参考: QSB-118: Dom0 arbitrary code execution in qvm-copy-to-vm error reporting

同じ著者の記事