1 ポイント 投稿者 GN⁺ 3 시간 전 | 1件のコメント | WhatsAppで共有
  • Linux 6.9以降、ノートPCのsuspend時にドライブをロックするツールが静かに失敗しており、LUKSのフルディスク暗号化キーがメモリに残っていた
  • 原因は、2024年5月にLinux 6.9へ入ったブロックデバイスアクセスのリファクタリングと暗号化コードの予期しない相互作用で、提案されている修正は1行のパッチ
  • 完全シャットダウンでは問題は表面化しなかったが、suspend-to-RAMではキーが残り、電源が入ったままのノートPCを確保した攻撃者がRAMからキーを取り出せる状態になっていた
  • 発見は、Debianのcryptsetup-suspendのNixOSポートを整理している最中に/proc/keysの項目を見つけたことから始まり、QEMUのメモリダンプで消去されているはずのvolume keyが残っていることを確認した
  • NixOSの統合テストとcryptsetupの警告パッチが提案されており、suspend直前のキー削除のようなセキュリティ機能は正常に見えても、実際のメモリ検証なしでは失敗を見逃しやすい

Linux 6.9以降のsuspend中にLUKSキーが残る問題

  • Linux 6.9、つまり2024年5月以降、ノートPCのsuspend時にドライブをロックするツールが静かに失敗していた
  • LUKSのフルディスク暗号化は、ノートPCの紛失・押収・盗難時にデータを保護するために使われるが、今回のケースではsuspend中も暗号化キーがメモリに残っていた
  • 完全シャットダウンでは引き続き動作していたものの、ノートPCを完全に電源オフにせずsuspend-to-RAMで運用することは多く、その点で影響が大きい
  • 電源が入った状態のノートPCを確保した人物がいれば、メモリに残ったキーが露出しうる状態だった
  • Windowsで同目的のソフトウェアとしてVeraCryptに言及があったが、その後のコメントで“canonical software”は最も広く使われているソフトではなく、ITセキュリティ分野での代表的な推奨を意味すると訂正された

原因と1行のパッチ

  • 原因は、Linuxカーネルのリファクタリングコミットである md: port block device access to file にあった
    • 変更自体は妥当で有用なリファクタリングだったが、暗号化コードと長距離の相互作用を引き起こした
  • 提案されている修正は 1行のパッチ
  • パッチ作成者は、形式的検証なしにこのパッチが正しく、ほかの長距離相互作用がないとは言えないと述べている
  • 再発防止のための後続作業も進められている

発見の経緯

  • 発端は、Debianのcryptsetup-suspendのNixOSポートを整理する作業だった
  • Debian本家とNixOSポートの両方に、ノートPCがときどきスリープできなくなる、煩わしいが有害ではない競合状態があった
  • これを解決するため、Pali Rohárの未マージのカーネルパッチである dm-crypt suspend/hibernationキー削除パッチ を復活させようとした
  • その過程でcryptsetupとカーネルのソースコードを調べ、文書上ではkeyringが呼び出しスレッドに紐づき、スレッド終了時に削除されることを確認した
  • しかし、以前は知らなかった/proc/keysに項目が見え、この点が疑念を深めた
  • 最終的にQEMU仮想マシンを起動してメモリをダンプし、消去されているはずのLUKS volume keyがそのまま残っていることを確認した

NixOS secure suspend-to-RAMプロジェクト

  • 別途公開されているsecure-suspendプロジェクトは、NixOSで実験的なsecure suspend-to-RAMを提供する
  • 一般的なフルディスク暗号化では、ノートPCがsuspend状態のときキーがメモリに残るため、cold boot attackやRAM流出の手法に弱い可能性がある
  • このプロジェクトは、Pali Rohárの古いカーネルパッチを復活させ、suspend時にLUKS暗号化キーを消去する方式を採る
  • Debianのcryptsetup-suspendに着想を得ているが、カーネルパッチを使うことで、ノートPCがときどきスリープできなくなる競合状態を避け、追加の予防策も加えている
  • 暗号化されたroot filesystemを完全にサポートし、統合テストも提供されている
  • カーネルパッチとユーザー空間ツールは、ほかのLinuxディストリビューション向けにも調整して適用できる
  • プロジェクトは secure-suspend として公開されている

suspendのセキュリティ検証が難しい理由

  • suspend後に画面ロックが表示されても、ストレージデバイスが実際にロックされたとは限らない
  • suspendから復帰した直後にディスクへそのままアクセスできるなら、ストレージデバイスは最初からロックされていなかったことが分かる
    • たとえば、ロック画面の裏で動き続けるスクリプトでディスクアクセスを確認できる
    • suspend後、SSH公開鍵で先に暗号化ストレージを解除しなくても接続できるなら、ストレージがロックされていないことを容易に確認できる
  • UbuntuやDebianのデフォルト設定には、こうした保護を提供しようとする試み自体がなかったというコメントもある
  • ストレージをロックしようとする試みが実際に正しく動作したかどうかは、別途検証する必要がある
    • ログのタイムスタンプはsuspend前に生成されていても、wake後に記録された可能性がある
    • 逆に、wake直後にシステム時刻が調整される前に生成されたログが、suspend時点の時刻のように見えることもある
  • ストレージのロックがsuspend直前に行われたのか、resume直後に行われたのかは、ユーザー体験上は同じに見えても、セキュリティ上は決定的に異なる
  • NixOSの統合テストは、仮想マシンでシステムを起動してメモリをダンプし、キーがsuspend時に実際に消去されたかを確認する方式になっている

1件のコメント

 
GN⁺ 3 시간 전
Hacker News のコメント
  • 興味深いバグであることは確かだが、タイトルは少しクリックベイトのように感じる
    私の理解では、cryptsetup luksSuspend は公式にサポートされた機能というより、Debian が作った拡張に近く、このリグレッションの影響を受けたのも Debian だけではないかと思う
    サポートされているわけでも広くテストされているわけでもない機能について、カーネルを責められるのかはよく分からない
    それでも印象的ではあるし、このリグレッションが再発しないようにテストが追加されたのは良いことだ。NixOSTests は本当に素晴らしいという OP の意見にも同意する
    ただ、タイトルだけ見ると、特定のディストリビューション 1 つではなく広範に広がった問題のように見える

    • 技術的に正確なタイトルを狙ったのであって、クリックを誘導しようとしたわけではない
      その通り。デフォルト設定を使っている人には影響しない。そもそも suspend 中にボリュームキーが安全だとは期待していないはずだからだ
      Debian の解決策は複数の、おそらく大半の他のディストリビューションに移植されており、個人で移植を維持していた人もかなりいたと思う
      thread-keyring(7) のマニュアルページは「thread keyring は、それを参照しているスレッドが終了すると破棄される」と約束している
      cryptsetup プロジェクトは、ユーザー空間からカーネル空間へキーを渡す仕組みでこの性質に依存していたが、カーネル 6.9 がこの性質を壊すリグレッションを導入した
    • これがなぜ Debian 専用と言われているのか混乱している。luksSuspend は upstream の機能で、2009 年に v1.1.0 リリースで追加された
      以前 Arch や openSUSE でも時々使ったことがあり、Debian 以外のディストリビューションにも確実に存在する
      おそらく system suspend との自動統合を思い浮かべているのだろうが、それは本筋から外れている。luksSuspend はシステムメモリからキーを消去すると文書化されており、Linux 6.9 の該当するリファクタリングパッチによってその動作が止まった
      ただし実際には cryptsetup 側のバグとも見なせる。カーネル keyring キーの非常に具体的な寿命の挙動に依存していたためで、ユーザー空間でより明示的に消去すべきだったという主張も可能だ
      [1]: https://gitlab.com/cryptsetup/cryptsetup/-/commit/3cea5dcc7b...
      [2]: https://gitlab.com/cryptsetup/cryptsetup/-/blob/main/docs/v1...
      [3]: https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/93...
    • サブコマンドは公式の cryptsetup リポジトリにあり、説明も正しそうだ: https://gitlab.com/cryptsetup/cryptsetup/-/blob/main/man/cry...
    • この機能を Arch で使ったことがあり、一般的な LUKS でも利用できる。ただし知る限り、suspend 時にデフォルトで使われるわけではない
      おそらく luksSuspend の後に実際に有用な形で RAM suspend を実行する仕組みのことを言っているのだと思うが、それは当初 Debian 向けで、その後 Arch にも入ったものの、どちらもデフォルトではなかった
    • Debian のどのバージョンで最初に 6.9 が配布されたのか気になる
  • 他の方法はあまり見当たらない。スリープ、つまり RAM suspend をすると、すべてが RAM に保存され暗号化されているが、マスターキーはカーネルメモリに残っていると記憶している
    一方 hibernate、つまりディスク suspend をすると、RAM 全体の内容がマスターキーまで含めてディスクに書き込まれて暗号化され、RAM は消去される
    再び起こすときには、マスターキーを復号してディスク内容をメモリに戻すためにパスフレーズを再入力する必要がある

    • その通り。ほとんどの標準的な Linux ディストリビューションでは、単にノート PC を suspend すると、マスターキーを含むすべてがメモリに残る
      しかし Debian は、選択機能である cryptsetup-suspend アドオンを先に作っており、これはメモリからキーを消去することになっている luksSuspend コマンドを実行した後、resume 時にパスフレーズを再要求する
      カーネル 6.8 までは説明どおりに動作していたが、カーネル 6.9 以降は静かに動作しなくなった
    • ここ 5 年ほどの Intel/AMD CPU はどちらも、OS には透過的な全メモリ暗号化をサポートしている
      この機能を有効にすれば、コールドブート攻撃は過去のものになる。通常は RAM 速度を約 0.5% 低下させるため、デフォルトで無効になっているだけだ
  • Sleep 後に起動パスワードを再入力しないのだから、暗号化キーがまだメモリに残っているのは明らかだ

    • ディストリビューションが cryptsetup-luksSuspend を使っていないのは明らかだ
  • これは自分にとってはあまり気になる問題ではない
    ディスク暗号化をする唯一の理由は、ノート PC を売るときに誰かが税務書類やクレジットカード情報を漁ることを心配しなくて済むようにするためだ
    もちろんノート PC も消去するが、データがドライブレベルで暗号化されていれば、フォレンジックツールのようなものでデータを復元されるリスクは非常に小さいと見ている

    • 手頃な妥協策として、LUKS ヘッダーだけ消去してもよい
      LUKS は、ディスクを開くにはボリュームキー全体が必要になるアンチフォレンジック・アルゴリズムを使っている。キーブロック群を拡散アルゴリズムで結合し、XOR して実際のマスターキーを作る仕組みなので、理論上はボリュームキーの 1 セクターだけを消去しても全体が復元不能になるはずだ
      つまり、キーのブロックが 1 つでも欠けていれば、残りを簡単に推測することはできないという意味だ
    • 暗号化キーが強いという前提なら、消去は理論上は重複作業だ
  • セキュリティ専門家ではまったくないが、最近「リファクタリング中にファイルをまたぐCのチェック1行を見落とした」ことで生じた致命的なセキュリティバグが定期的に見つかっているのを見ると、巨大で安全なオープンソースのCコードベースという前提そのものが疑わしく思える
    Cだけの問題ではないが、特にCでは不変条件を一貫して強制し追跡するのがより難しく、コード変更時にはなおさらだと思う
    不変条件を型にエンコードする関数型プログラミングが、現実的にスケールする解決策なのかも分からない。モデル検査? LLMファジング? 明確な境界を持つ、より少ない基本要素? seLinuxはそういう形で「検査」されたのだろうか?

    • Cの欠点は見えているし、新規プロジェクトに一般的に勧めもしないが、この特定のバグがRustのborrow checkerや他言語の型システムで見つけられる好例だとは思わない。静的解析器でも見つけられない気がする
      本質的にはこういうことだ:
      original: DoTheThing()
      new: DoTheThingSlightlyDifferentButKeepMyCredentialsAlive()
      fix: DoTheThingSlightlyDifferentButDoInFactNOTKeepMyCredentialsAlive()
      経験上、厄介なバグのかなりの部分は上位レベルのシステム不変条件の違反から出てくるもので、これは自動化できる性質のものには見えない
      Leanのようなものでも、プログラムが特定の性質を満たすことは証明できるが、その性質をまず思いついていなければならない。証明が不変条件を代わりに発見してくれるわけではない
      関連するセキュリティ性質を思いついていたなら、回帰テストを書くのは難しくなかったはずだ。本当に難しい部分は、実装を安全に表現することではなく、実装が保存すべき性質があるという事実に気づくことだと思う
    • 安全な公開コードベースという前提そのものは問題ない
      問題は、監査可能性が高いからといって自動的により多く監査されるわけではない点にある
      十分な実力を持つ人たちが、十分な時間をかけて作業する必要がある
    • Rustに訳しても「Rustのチェック1行を見落とした」になっただけだろう
      これは関心事が交差し、ドメイン横断の知識が不足していたために生じたバグだ。Lispやアセンブリ言語でもおそらく同じだったはずだ
    • ここから得られる教訓は、ある機能に最低限関連するテストケースがないなら、それは実際の機能ではないということだ
    • 「巨大で安全なオープンソースのCコードベース」という前提が疑わしく見える理由は、コードレビューが時には、仕様の形式化された版にアクセスできる理想化された停止問題と大差ないからだ
      言い換えれば、何がセキュリティ問題なのかについて厳密な定義がない
  • 連邦機関が鍵を得る方法をどうしても必要としていたのか? これはバグドアなのか? コミットは追跡されたのか?
    最近こういうパターンをよく見るので、少し疑わしく思い始めている。人々がこれにより敏感になって、より多く投稿しているからかもしれない

    • これはリグレッションだ。ユーザー空間アプリケーションも静かに失敗していたはずで、いくつもの不注意が連なった結果だ
      暗号化キーがメモリ上にあることが、ただちに抽出できることを意味するわけではない。本来あるべきでない場所に、不必要に無期限で残された、というのに近い
  • こういうリグレッションは、すべてが引き続き「動作」するため見落としやすい。セキュリティバグは自ら姿を現さないことが多い

    • その通り。だからこういう機能では統合テストがより重要になる
      書くのも楽しかったし、このバグを導入した具体的なカーネルのリファクタリングを見つけるために git-bisect を走らせられるようにもしてくれた: https://github.com/NixOS/nixpkgs/pull/532499
  • FedoraのノートPCでは、suspendから15分後にディスクへhibernateするようLinuxを設定している。メモリの電源を切ってしまえば、こういうDebian固有のバグは問題にならない
    DebianのLinuxツール拡張は理論上は良いが、実際にコールドブート攻撃を心配するなら、LUKSキーだけでなくすべてのキーと重要な文書がメモリから消去される必要がある
    だからコールドブートを防ぐまともな方法は、結局hibernateだけだ

    • 同意する。あるいはFridgeLockを復活させる方法もある: https://www.sec.in.tum.de/i20/publications/fridgelock-preven...
    • ところでresumeするとき、メモリを復号する鍵はどこから持ってくるのか?
      知る限り、TPMを使わなければ実用的ではない。そしてTPMを使うなら、実質的にTPMに運命を委ねることになる
  • この脆弱性が商用OSにあったら、このHNスレッドがどう見えたか想像してみればいい
    最上位コメントは間違いなく、Applosoftはもはやソフトウェア品質を気にしていないとか、「OSにバイブコーディングのゴミを許すとこうなる」という内容だったはずだ
    その下のコメントは監視産業複合体とNSAに関する陰謀論だっただろう。他の場所なら狂った話だが、HNではそうではなかったはずだ

  • こんなに重要なものが、なぜ毎回のビルドでテストされていないのか分からない