1 ポイント 投稿者 GN⁺ 2024-01-27 | 1件のコメント | WhatsAppで共有
  • rhboot/shim の commit 0226b56 は、ファイル受信処理で HTTP ヘッダーのサイズ値 をそのまま信頼したことにより発生した CVE-2023-40547 を修正
  • 細工されたヘッダーが実際の受信データより小さいサイズを指定すると、shim は必要なバッファより小さな領域を確保してしまう可能性がある
  • 既存コードは確保時にはヘッダー値を使い、コピー時には プロトコルメタデータ を使用していたため、out-of-bounds write につながる可能性があった
  • パッチは httpboot.creceive_http_response()*buf_size < rx_message.BodyLength を検査し、失敗時には EFI_BAD_BUFFER_SIZEInvalid Content-Length として処理する
  • 変更範囲は httpboot.c 1ファイルの 7行追加・1行削除 で、Content-Lenght の誤記も Content-Length に修正された

脆弱性の発生フロー

  • CVE-2023-40547 は、shim が HTTP または関連プロトコルでファイルを取得する際に発生する問題
  • 受信データを保存するバッファを確保する過程で HTTP ヘッダーのサイズ値 が使われていた
  • HTTP ヘッダーは改ざん可能であり、実際の受信データより小さいサイズを指定できる
  • 既存フローでは、バッファ確保にはヘッダー値を使い、rx バッファからデータをコピーする際には プロトコルメタデータ を基準にしていた
  • この差異により、確保されたバッファより大きなデータをコピーできてしまい、その結果 out-of-bounds write が発生する可能性があった

パッチ内容

  • パッチは httpboot.creceive_http_response(EFI_HTTP_PROTOCOL *http, VOID **buffer, UINT64 *buf_size) に防御チェックを追加
  • *buf_size == 0 の場合は既存のエラーメッセージの誤記を修正し、goto error に移動
    • Failed to get Content-LenghtFailed to get Content-Length
  • 新しいチェックでは *buf_size < rx_message.BodyLength 条件を確認
    • 条件が真なら efi_status = EFI_BAD_BUFFER_SIZE を設定
    • Invalid Content-Length エラーを出力
    • その後 goto error に移動

変更範囲

  • 変更されたファイルは httpboot.c 1つ
  • 変更量は 7行追加1行削除
  • 核心は、受信本文長である rx_message.BodyLength が確保サイズである *buf_size より大きいかを確認するロジック

関連記録

  • この commit は CVE-2023-40547 を解決する変更として示されている
  • commit メッセージには、問題が HTTP ヘッダーを誤って信頼したことに起因すると明記されている
  • 脆弱性の報告者は Microsoft Security Response Center の Bill Demirkapi と記録されている

1件のコメント

 
GN⁺ 2024-01-27
Hacker News の意見
  • shim は、Secure Boot を有効にしたい Linux ディストリビューションでよく使われる EFI ブートローダーです
    ディストリビューション側としては、ユーザーに自分で鍵を登録させるより、標準搭載されている Microsoft 署名鍵で Secure Boot を簡単に有効にしたいと考えます
    しかし Microsoft は一般に GRUB のような GPL ブートローダーには署名しないため、Microsoft 鍵で署名できる shim が作られました。shim は Machine Owner Key、つまり MOK という別の鍵で、自分が起動する対象の署名を検証します
    shim に起動する EFI バイナリを指定する際、HTTP URL を渡すことができますが、このとき HTTP サーバーが悪意あるものだと、範囲外書き込みを引き起こせます
    ただし通常は GRUB のようなローカルの第2段階ブートローダーを起動するために使うので、多くのインストール環境では問題になる可能性は低そうです
    Secure Boot は当初から、すでに署名済みのバイナリも DBX リストで失効できるよう設計されており、このリストを UEFI に入れると、有効な署名があっても該当バイナリを拒否します
    このバグを含む古い shim バイナリの署名がリストに追加されれば、各自の機器でリストを更新できます。LVFS のようなカプセル更新で配布されることもありますし、Secure Boot の鍵と変数を直接管理しているなら、https://uefi.org/revocationlistfile からリストをダウンロードして登録することもできます

    • 元記事のバグ発見者ですが、この問題が HTTP ブートを使うときだけ悪用可能だというのは、よくある誤解です
      そうであれば Critical 評価にはならなかったでしょう
      このバグは、ローカルで権限を持つマルウェアが EFI パーティションを上書きする場合、PXE ブートが有効な隣接ネットワークで中間者攻撃を行う場合、HTTP ブートを使う場合のリモート中間者攻撃で悪用できます
      権限のないリモート攻撃者が中間者の位置にいて、被害端末が HTTP ブートを使っていれば、直接アクセスなしでも悪用可能です
      被害端末で権限とコード実行を得たリモート攻撃者は、被害者が HTTP ブートを使っていなくても、ファームウェアが HTTP をサポートしていれば Secure Boot を回避できます
      例えば、ブート順序の変数を変更して攻撃者が制御するサーバーを指定したり、EFI パーティションのブートローダーを正規の shim と GRUB2 イメージで上書きしたうえで、grub.cfg から HTTP 経由で新しい shim をチェーンロードさせたりできます
      GRUB2 のデバイス構文では、HTTP を含むサポート済みデバイスを指定できるためです
      また、権限のない隣接攻撃者が中間者の位置にいて、被害端末が PXE ブートを使っている場合、PXE の shim → PXE の GRUB2 → HTTP の shim という形でつなげて悪用できます
    • Microsoft が GPLv3 ライセンスのブートローダーである GRUB に署名すると、GPLv3 により開発者へ署名鍵の提供を強制する権利が生じ得る、と Microsoft の法務部門が見ているためです
      出典: https://techcommunity.microsoft.com/t5/hardware-dev-center/u...
    • ブートローダーがなぜネットワーク通信をするのか疑問でしたが、EFI バイナリを HTTP URL で指定できるという説明で理解できました
    • shim が Microsoft の懸念する問題をどう回避しているのか気になります
      GPLv3 の反 Tivoization 条項が Secure Boot 署名鍵の提供を要求し得るなら、MOK 署名鍵も要求があれば提供しなければならないのでは、と思います
      そうだとすると、誰でも Secure Boot を通じて間接的に起動される任意のコードに署名できる鍵を得ることになるわけで、Microsoft が GRUB のような GPLv3 プロジェクトに単に署名鍵を発行する結果と、意味のある違いがあるのか分かりません
    • ローカルの第2段階ブートローダーを起動する目的なら、Windows Boot Manager も同じ役割を果たせるのではないかと思います
      古い BIOS 機では WBM に GRUB をチェーンロードさせたことがありますが、UEFI 機ではまだ試していないので、どこかで詰まる点があるのかは分かりません
  • 「なぜ信頼できない、または侵害されたサーバーから起動するのか?」「サーバーが侵害されたなら、単に悪意あるバイナリを送ればいいので意味がないのでは?」と思うかもしれませんが、短く言えば、shim が最終的に起動するバイナリは MOK で署名されていなければなりません
    したがって、侵害されたネットワーク内で起動する場合でも、HTTP で起動する場合でも、侵害されたサーバーから起動する場合でも、HTTPS かどうかに関係なく同じセキュリティ保証を維持する必要があります
    Secure Boot はダウングレードを防ぐものではないため、侵害されたサーバーがダウングレード攻撃に使われ得るという事実は、この脆弱性とは別の問題です
    ダウングレード攻撃の防止は、いずれにせよ、より堅牢な方式で別途実装する必要があります
    ただし、shim がなぜ HTTP ブートを直接サポートする必要があるのかは分かりません
    MOK で署名された2つ目のローカル EFI バイナリで処理してもよさそうですが、おそらく実装が比較的簡単な機能だと考えられていたのでしょう

  • このコードがなぜ本文の長さを二つの基準で扱っているのか分からない
    RFC上、HTTP/1.1における Content-Length はHTTPリクエスト/レスポンス本文の長さに関する権威ある情報である
    その長さを超えて回線上にあるデータは、定義上、別のメッセージの一部である
    逆にContent-Lengthが rx_message.BodyLength より大きいなら、まだメッセージ全体を受け取れていないという意味なので、さらに待つかタイムアウトエラーを出すべきである
    いずれにしても、rx_message.BodyLength がContent-Lengthと等しい保証がないなら、それは不正な値である
    もっと寛容に処理したいなら、Content-Lengthヘッダーを見る理由はなく、単に rx_message.BodyLength をバッファサイズとして、回線上のすべてのデータを受信メッセージとして解釈すればよい
    現在のコードは不必要に複雑で、こういう形でバグが入り込む

    • 該当コミットだけを単独で見ると誤解しやすい
      周辺のコード https://github.com/rhboot/shim/blob/0226b56513b2b8bd5fd281bc... を見ると、ループ内でデータ片を受け取り、そのたびに新しいデータがContent-Lengthで決まったバッファ容量を超えないか確認している
      しかし以前は、ループ外の 最初の読み取り についてその検査をしておらず、それがバグだった
      ただし、ダウンロードされたサイズが *buf_size、つまりContent-Lengthと同じかを最後に確認するコードは見当たらない
      この条件が崩れるなら、接続が早すぎるタイミングで閉じられたことを示すシグナルかもしれない
  • これは明らかにバグで、修正されてよかったが、そもそも信頼できないホストから自分の機器を起動する人がいるのかと思う
    攻撃者が悪意あるヘッダーを送れるほどHTTPサービスを掌握しているなら、このオーバーフローを回避することなど最も小さな問題で、証明書も侵害されているし、仕様に合ったペイロードにマルウェアを入れて送ることもできる
    バグなのは確かだが、Critical かどうかは確信が持てない

    • Secure Bootを推し進める人たちも似たような部類である
      彼らは、潜在的に侵害されうるすべてのものはSecure Bootで署名されていてはならない、というセキュリティ戦略を本気で信じている
      脆弱性のある署名済みの何かが一つでもあれば、それを利用して全員のSecure Boot+TPM暗号化ディスクを復号するのに使える
      このアプローチがなぜ有効なセキュリティモデルと見なされたのか理解しがたく、こうした脆弱性はすでに山ほどある
      さらに、部屋の中の象である Windows は完全に無視している
      こうした考え方の例: https://lkml.org/lkml/2018/4/3/767
      Linusの懸念にもかかわらず、多くのディストリビューションではSecure Bootで起動すると実際にインテグリティモードが有効になる
      おそらくMicrosoftのポリシーと、ディストリビューションがMicrosoft UEFI署名を得るためにそのスレッドで説明された手順に従わざるを得ない状況のためだろう
      その結果、Secure Bootを有効にすると通常ディストリビューションの機能が制限され、例えばハイバネーションが使えなくなる
    • 良い防御は 多層防御 のように複数の防御層を積み重ねる方式だけで、このバグはそのうちの一層に穴を開ける
    • 一部のロックダウンされたデバイスへ侵入するのに使われる可能性もある
    • 別スレッドの優れた説明 https://news.ycombinator.com/item?id=39135275 を見ると、攻撃ベクトルはHTTPに限られないため、Criticalと見なせる
  • 重要なことが起きるときはHTTPのSを使うべきだ
    デバイスの起動もこれに含まれ、HTTPSヘッダー は常に暗号化されていた
    それでも、よく見つけたバグだ

    • ここでは HTTPS は関係ない
      不正なヘッダーはどちらでも送れる
    • HTTPSがこの用途で可能なのかよく分からない
      暗号化には正確な時刻と日付が必要だからだ
      RTCが有効な場合もあるが、タイムゾーンをうまく扱うのかも分からないし、いずれにせよ時刻がずれている可能性もある
    • Sがあるかないかとは関係のない問題だ
      問題を正しく理解していないように思う
  • Content-length は実際の本文長ではなく、Content-encoding後の長さ である

    • 「HTTP/1.1は、大半を無視すれば実に楽しく単純なプロトコルである」
  • これらのshimビルドには httpboot が含まれているのか?
    私の理解では、shimはディスク上の別のEFIバイナリを実行するためのものにすぎず、shimのネットワークブート機能が実際に使われているのを見たことはない気がする

  • よく分かっていないのかもしれないが、ほとんどのHTTPクライアントは指定された Content-Length までしか読み取らず、読み取ったバイト数がContent-Lengthより少なければエラーと見なすものだと思っていた

    • HTTPクライアントはUEFIが EFIドライバー として提供する
      私の見る限り、UEFI仕様はContent-Lengthヘッダーとレスポンス本文長が一致しない場合の動作を具体的に定めていないようだ
      そのため、一部の実装は単に connection:close リクエストを作り、Content-Lengthを確認しない可能性も十分ある
      この脆弱性はMSRCが報告したもので、CVEの説明には実際の悪用に関する記述はない
      後で公開されるかもしれないし、理論上の問題かもしれない
  • 実際の本文長より少なく読むことがなぜ危険なのか説明してもらえる?
    むしろ逆のほうが危険だろうと予想していた

    • HTTPや関連プロトコルでファイルを取得する際、shimは受信データを保存するバッファを割り当てようとする
      ところがサイズを操作可能なHTTPヘッダーから取得しており、攻撃者は受信データより小さいサイズを指定できる
      この場合、コードは割り当てにはヘッダー値を使い、受信バッファからコピーするときにはプロトコルメタデータのサイズを使うため、範囲外書き込み が発生する
    • 説明によると、Content-Lengthを基準にバッファを割り当てるが、実際に受け取ったバッファのサイズ分だけコピーするため、割り当て範囲を超えて書き込むことになる