崩れた安全神話:Lenovoのホーム用ノートパソコンに、深刻なUEFIの脆弱性を発見

ESETの研究者は、攻撃者が管理者権限を持つ場合、ファームウェアレベルのマルウェア感染に直結する複数の脆弱性がLenovoのホーム用ノートパソコンの複数のモデルに存在することを発見しました。

ESETの研究者は、Lenovoのホーム用ノートパソコンの複数のモデルに影響を与える3つの脆弱性を発見し、分析しました。これらの脆弱性の最初の2つ(CVE-2021-3971CVE-2021-3972)は、本来Lenovoのコンシューマー向けノートパソコンの製造工程でのみ使用されているUEFIファームウェアドライバに影響します。残念ながら、これらのドライバは正しく無効化されない状態で製品のBIOSイメージにも含まれています。これらの影響を受けるファームウェアドライバは、攻撃者によって直接操作され、OSの実行中に特権ユーザーモードのプロセスから直接、SPIフラッシュへの書き込み保護機能(BIOS制御レジスタビットと保護範囲レジスタ)またはUEFIセキュアブート機能が無効にされる恐れがあります。これらの脆弱性が攻撃されると、UEFIのルートキットであるLoJaxやESETが最近検出したUEFIマルウェアESPecterなどがSPIフラッシュやESPに埋め込まれて、影響を受けるデバイスに展開され実行される可能性があります。

これらの脆弱性を検出した経緯を説明するために、CVE-2021-3971の影響を受けるファームウェアドライバについて最初に説明します。これらのドライバにはSecureBackDoorとSecureBackDoorPeimという残念な(また、意外にも実際の機能を正直に反映した)名前が付けられており、ESETの研究者の注意を引くこととなりました。最初の分析の結果、SecureBackDoor*ドライバといくつかの共通する特徴があるLenovoの他のドライバChgBootDxeHookとChgBootSmmも存在することがわかりました。さらに、このドライバの機能を調査したところ、UEFIセキュアブート(CVE-2021-3972)を無効化するために悪用される可能性があることが判明しました。 また、上記の脆弱性が存在するドライバを調査していたところ、SW SMIハンドラ関数内部でSMMメモリが破壊される第3の脆弱性(CVE-2021-3970)が見つかりました。この脆弱性により、任意のデータをSMRAMから読み取ったり、SMRAMに書き込んだりすることが可能となり、SMMの権限で悪意のあるコードが実行され、SPIフラッシュにマルウェアが展開される可能性があります。

ESETは、発見したすべての脆弱性を2021年10月11日にLenovoに報告しました。IdeaPad-3のような手頃なモデルからLegion 5 Pro-16ACH6 HYoga Slim 9-14ITL05などの高機能モデルまで、世界中で数百万人のユーザーが使用している百種類以上のコンシューマー向けノートパソコンがこれらの脆弱性の影響を受けます。現在サポートされている対象機種の全リストは、Lenovoのアドバイザリに掲載されています。

このアドバイザリに記載されている機種以外にも、影響を受けるデバイスがあることをLenovoに報告していますが、EODS(開発サポート終了)になっており、今後修正されることはありません。この中には、これらの脆弱性が初めて発見されたデバイス(Ideapad 330-15IGMおよびIdeapad 110-15IGR)も含まれます。ESETが、脆弱性の存在を確認したこれらのEODSデバイスの一覧は、ESETの脆弱性公開リポジトリで参照できます。

Lenovoは、2021年11月17日にこれらの脆弱性を確認し、以下のCVEを割り当てました。

  • CVE-2021-3970 LenovoVariableSmm - SMMの任意の読み取り/書き込み
  • CVE-2021-3971 SecureBackDoor - SPIフラッシュ保護の無効化
  • CVE-2021-3972 ChgBootDxeHook - UEFIセキュアブートの無効化

脆弱性が開示されるまでのタイムライン:

  • 2021年10月11日:Lenovoに脆弱性が報告された。
  • 2021年10月12日:Lenovoから回答があり、問題を調査していることが確認された。
  • 2021年11月17日:Lenovoが脆弱性の存在を確認し、アドバイザリの公開予定日(2022年2月8日)を通知した。
  • 2022年1月20日:Lenovoは、開発上の問題が発生したことから、アドバイザリの公開予定日を4月18日に延期するよう要請した。
  • 2022年4月18日:Lenovoのセキュリティアドバイザリが公開された。
  • 2022年4月19日:ESET Researchがこれら脆弱性に関するブログを公開した。

UEFIファームウェアの基本理論

これらの脆弱性の詳細な分析結果を説明する前に、UEFIプロトコルの基本理論、システム管理モード、UEFI NVRAM変数、UEFIセキュアブート、基本的なSPIフラッシュへの書き込み保護について説明します。

このブログでは、これらの脆弱性の分析結果を理解するために必要な最低限の説明だけを記載します。これらの内容に精通されている方は、「技術的な分析」セクションを直接参照してください。このブログのUEFIに関する説明では不十分だと思われる方は、SentinelOneの研究者によるブログ連載とUEFIの仕様を参照されることを強くお勧めします。

UEFIサービスとプロトコル

UEFIは、UEFIドライバとアプリケーションが使用する2種類のサービスである、ブート(EFI_BOOT_SERVICES) サービスとランタイム(EFI_RUNTIME_SERVICES)サービスを定義しています。いずれのサービスも、EFI_SYSTEM_TABLE 引数を介してドライバまたはアプリケーションのエントリポイントに渡されます。

これらは、プロトコルのインストール、既存のプロトコルの検索、メモリ割り当て、UEFI変数の操作など、ドライバやアプリケーションが処理を実行するために必要な基本機能やデータ構造を提供します。

UEFIブートドライバとアプリケーションは広範亘ってってプロトコルを使用します。UEFI環境におけるプロトコルとは、単にGUIDで識別される機能のグループです。これらのプロトコルは一度インストールされると、EFI_BOOT_SERVICES.ExitBootServicesが呼び出されるまでメモリに常駐し、他のドライバやアプリケーションによって使用されます。UEFIの仕様では、一般的なファームウェアの使用を想定して、このようなプロトコルが多く定義されていますが、ファームウェア開発者は独自のプロトコルを定義してこの基本機能を拡張できます。

UEFI変数

UEFI変数とは、UEFIモジュールによって使用される特殊なファームウェアのストレージメカニズムであり、ブート設定、UEFIセキュアブート設定、証明書、および同様のデータを含むさまざまな構成データが保存されます。これらの変数は、常に変数名と名前空間(GUID)で識別されます。

UEFI変数を作成するとき、変数がシステムによってどのように保存および保守されるかを示すための属性が使用されます。属性を使用することで、変数を永続化する(電源をオフにしてオンにしてもそのまま残る)、一時的に利用する、あるいは認証される場合のみ使用できます。NVRAM変数に認証が設定されている場合、新しい変数データが認証された秘密鍵によって正しく署名されている場合にのみ、変数の内容を変更できます。変数を読み取る権限はすべてのユーザーに付与されます。

これらの属性の中で重要な例のみを以下に示します。

  • VARIABLE_ATTRIBUTE_NON_VOLATILE (NV) (0x00000001) この属性を使用する変数は、電源を一度オフにしてオンにした後でも持続し、容量が制限されている(通常は約64MB)の固定のハードウェアストレージ(NVRAM)に保存されます。
  • VARIABLE_ATTRIBUTE_BOOTSERVICE_ACCESS (BS) (0x00000002) BS属性が設定されている場合、EFI_BOOT_SERVICES.ExitBootServicesの実行後、RT属性が設定されていない変数はGetVariable関数に表示されなくなります。
  • VARIABLE_ATTRIBUTE_RUNTIME_ACCESS (RT) (0x00000004) EFI_BOOT_SERVICES.ExitBootServicesの実行後にGetVariable関数から変数にアクセスするには、RT属性を設定する必要があります。
  • VARIABLE_ATTRIBUTE_AUTHENTICATED_WRITE_ACCESS (AW) (0x00000010) • VARIABLE_ATTRIBUTE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS (AWT) (0x00000020)

属性の詳細なリストと属性の使用に関するルールは、UEFIの仕様書に記載されています。

Windows 8以降から、特権ユーザーランドプロセス( SE_SYSTEM_ENVIRONMENT_NAME特権がある管理者)からUEFI変数にアクセスできるAPIが登場していることに留意してください。

システム管理モード(SMM)

SMMは、x86プロセッサの高位の特権のある実行モードであり、「ring 2」と呼ばれこともあります。

SMMコードは、システムファームウェアのコンテキストで記述され、通常、高度な電源管理、OEM独自のコードの実行、安全なファームウェアの更新など、さまざまなタスクに使用されます。SMMは、実行中のオペレーティングシステムには表示されることのない完全に独立した実行環境を提供しており、SMMで使用されるコードとデータは、SMRAMと呼ばれるハードウェアで保護されたメモリ(SMMからのみアクセス可能)に保存されます。

SMMに入るには、SMI(システム管理割り込み)と呼ばれる特殊なプロセッサ割り込みを発生させる必要があります。SMIは、ソフトウェアまたはプラットフォームのハードウェアによって発生されます。このブログで説明する脆弱性を理解するためには、IntelアーキテクチャのシステムでSMI(特にソフトウェアSMI:SW SMI)を生成してSMMに入る方法の1つとしてI/Oポート0xB2への書き込み(OUT命令を使用)があることを理解すれば十分です。これは、システムの実行中にファームウェアサービスを呼び出すソフトウェアで多く使用されます。

UEFIファームウェアにおけるSW SMIハンドラの定義

上記のようにSW SMIを発生させる方法で内部的に何が起こっているかを理解するには、UEFI Platform Initialization Specification, Version 1.4(ZIPファイル)のボリューム4にあるEFI_SMM_SW_DISPATCH2_PROTOCOLの定義(図1に表示)を参照する必要があります。

このプロトコルは、仕様に書かれているように、SMMモジュールが特定のソフトウェア割り込みに応答するハンドラ関数をインストールするために使用できます。

このようなハンドラ関数をインストールするには、EFI_SMM_SW_DISPATCH2_PROTOCOL.Registerサービス関数を使用します。この関数タイプの定義を図2に示します。

このサービスは、DispatchFunction関数をインストールします。この関数は、RegisterContext >SwSmiInputValueによって指定されるソフトウェアSMIのソースが検出されると呼び出されます。つまり、ハンドラのインストール時にRegisterContext >SwSmiInputValueで指定されたのと同じSwSmiInputValueをI/Oポート0xB2に書き込んでSW SMI割り込みを生成すると、このDispatchFunctionが呼び出されます。

SW SMI割り込みの場合、パラメータは通常CPUのレジスタを使用してSMIハンドラに渡されます。SW SMI割り込みが発生すると、割り込みの発生時における全CPUのコンテキストがSMRAMに保存されます。呼び出されたSMIハンドラは、EFI_SMM_CPU_PROTOCOL関数であるReadSaveStateとWriteSaveStateをそれぞれ使用して、簡単にこのコンテキストを読み取って変更できます。

主なSPIフラッシュの保護メカニズム

UEFIファームウェアは通常、コンピュータのマザーボードに組み込まれているフラッシュメモリチップ(SPIフラッシュチップ)に存在します。このフラッシュメモリチップは、不揮発性メモリであり、SPI(シリアルペリフェラルインタフェース)を介してプロセッサと接続されます。

このメモリは、オペレーティングシステムを再インストールしても影響を受けないため、LoJax、MosaicRegressor、MoonBounceなどの攻撃でも見られたように、特殊なマルウェア(インプラント)を展開する場合の恰好の標的になります。

SPIフラッシュが改変されないように、いくつかのセキュリティ保護の仕組みが用意されています。主な防御機能は、チップセットが公開している特別なメモリマップ構成レジスタ(BIOS制御レジスタと5つの保護範囲レジスタ)によって提供されます。

BIOS制御レジスタ

このレジスタでは、3つの特定のビットがSPIフラッシュへのアクセス制御に使用されます。なお、他のチップセットではレジスタの名前が異なる場合がありますが、基本的な原理は同じです。

  1. BIOSWE(ビット0)
    このビットを設定すると、BIOS空間への読み取りおよび書き込みアクセスの両方が有効になります。設定されないと場合は、読み取りアクセスのみが有効になります。
  2. BLE(ビット1)
    このビットを設定すると、SMMコードによってのみBIOSWEを0から1に設定できます。SMM以外のコードからBIOSWEを設定しようとすると、SMIが発生します。これにより、OEMがBIOSWEビットを0に戻して保護するSMIハンドラを実装できるようになります。
  3. SMM_BWP(ビット5)
    このビットを設定すると、すべてのプロセッサがSMMにあり、BIOSWEが1に設定されない限り、BIOS空間に書き込むことはできなくなります。このビットを設定すれば、Speed Racerの競合状態の脆弱性(SednitグループがLoJaxを展開するために使用したReWriter_binaryツールにこの脆弱性を攻撃するエクスプロイトが存在)が解消されます。

保護範囲レジスタ(PR0-PR4)

各レジスタは、SPIフラッシュBIOS領域のメモリの特定範囲について個別に読み取り/書き込み権限を指定します。HSFS(Hardware Sequencing Flash Status)レジスタのFLOCKDN(フラッシュ構成ロックダウン)ビットが設定されていない場合にのみ、これらのレジスタを設定できます。

このFLOCKDNビットは、プラットフォームの初期化時(保護範囲(PR)レジスタを設定した直後)にプラットフォームのファームウェアによって設定される必要があります。FLOCKDNは一度設定されると、次回ハードウェアがリセットされるまでクリアされません。保護範囲レジスタで保護されるメモリ範囲は、ロックされた後は、どのようなランタイムコード(SMMコードを含む)でも変更できなくなります。正規のファームウェアアップデートでも、保護範囲(PR)レジスタがロックされる前に実行する必要があります。

現在のシステムで利用可能なソリューション

最近のシステムは通常、ハードウェアベースでブート環境の整合性を検証するソリューション(Intel Boot Guardなど)を実装しています。このようなソリューションが適切に設定され、別の脆弱性が存在していなければ、これらのチップセットが提供する保護機能がSPIフラッシュを設定ミスや脆弱性から保護できない場合でも、信頼できないファームウェアコードが実行されないように保護されます。

PCI/PCIeのコンフィグ空間へのアクセス方法

BIOS制御レジスタ、保護範囲レジスタ、その他多くの設定レジスタは、PCI(e)のコンフィグ空間にアクセスすることで読み取りまたは書き込みが可能になります。特定のPCI互換デバイス(SPIフラッシュなど)のPCI(e)コンフィグ空間の位置(アドレス)は、次の3つの値で指定されます。

  • バス
  • デバイス
  • 関数

このデバイスに関連する構成は、このコンフィグ空間にあるオフセットに配置されます。PCI(e)のコンフィグ空間へのアクセス方法は、通常は2つあります。

ポートI/Oの使用

このケースでは、マシンI/O命令のINOUTが0xCF8(CONFIG_ADDRESS)と0xCFC(CONFIG_DATA)I/Oポートと組み合わさって、PCIコンフィグ空間の特定のコンフィグデータにアクセスするために使用されます。このブログでは詳細な説明は不要であるため省略します。

メモリマップドI/O(MMIO)の使用

メモリマップドI/Oを使用すると、PCIコンフィグ空間がメインメモリのアドレス空間に直接マッピングされるため、他のデータとほぼ同じようにコンフィグデータにアクセスできるようになります。これらのデータにアクセスするために必要なのはこのデータがあるPCIアドレスだけですが、このアドレスはどのように見つけることができるのでしょうか。以下を把握していれば、ユーザー自身がこのアドレスを構築できます。

  1. MMIOのベースアドレスMCFG ACPIテーブルにあります)
  2. アクセスするデータを特定するためのバス、デバイス、関数、オフセット(レジスタとも呼ばれます)の値(チップセットのデータシートで確認できます)。

これらの値をPCIアドレスにエンコードするマクロの例は、EDK2のPciLib.hヘッダーファイルで参照できます。また、MMIOのPCIアドレスを個別の識別子に変換する関数とその逆の関数をPythonで実装したものを図 3に示します。

UEFIセキュアブート

UEFIセキュアブートはUEFIの仕様で定義されています。この主な目的はブートコンポーネントの整合性を検証し、プラットフォームが信頼するコンポーネントのみの実行を許可することです。この検証プロセスの対象となるコンポーネントは、特定のプラットフォームにおけるUEFIセキュアブートポリシーの実装によって異なります。通常は、サードパーティのUEFIドライバ、アプリケーション、OPROMのみが検証され、SPIフラッシュにあるドライバは暗黙的に信頼されます。 UEFIセキュアブートは、信頼できるコンポーネントと信頼できないコンポーネントを決定するために、認証されたNVRAM変数に保存されている特別なデータベースであるdbとdbxを使用します。

  • dbデータベースには、ブートで使用されるコンポーネントの署名を認証するために許可されている信頼できる公開鍵証明書のリストが含まれます。また、証明書の他に、署名されているかどうかに関わらず実行が許可されているコンポーネントのハッシュリストも含まれる場合があります。
  • dbxデータベースには、実行が許可されないUEFI実行可能ファイルの公開鍵証明書またはハッシュが含まれます。これは、脆弱性が存在することがわかっている、または失効した証明書が使用されている署名済みの実行可能ファイルの実行を防ぐために使用されます。

技術的な分析

最初に、CVE-2021-3971CVE-2021-3972の影響を受けるLenovoドライバの分析について説明し、次にSMMの脆弱性CVE-2021-3970について説明します。

このブログの最初に、SecureBackDoor*と ChgBoot*のドライバには共通する特徴があると説明しましたが、両者に具体的にどのような関係があるのかを説明します。どちらのドライバも、その機能を有効にするかどうかを決定するための制御メカニズムとして、6ACCE65D-DA35-4B39-B64B-5ED927A7DC7E名前空間にあるUEFI変数を使用します(このGUIDをLENOVO_BACKDOOR_NAMESPACE_GUIDと呼ぶことにします)。

CVE-2021-3971 - SecureBackDoorドライバ - SPIフラッシュ保護の無効化

最初に、CVE-2021-3971の脆弱性の分析結果から説明します。この脆弱性によって攻撃者はNVRAM変数を作成するだけで、SPIフラッシュの書き込み保護のメカニズムを無効にできます。プラットフォームのファームウェアがデバイスの起動中にこのNVRAM変数を検出すると、BIOS制御レジスタと保護範囲レジスタに基づくSPIフラッシュ保護を設定するコードの実行がスキップされます。

この結果、攻撃されたシステムは、SMM以外のコードが実行された場合でも、SPIフラッシュが変更されることになり、攻撃者はファームウェアストレージに直接悪意のあるコードを展開できます。

この「SPIフラッシュ保護を無効化する機能」は、影響を受けるノートパソコンのファームウェアに含まれている以下のドライバに実装されています。

  • SecureBackDoorPeim (16F41157-1DE8-484E-B316-DDB77CB4080C)
  • SecureBackDoor (32F16D8C-C094-4102-BD6C-1E533F8810F4)

この脆弱性を攻撃すると、以下が設定されたNVRAM変数を作成するだけで、上記のSPIフラッシュの書き込み保護を無効化または解除できます。

  • 名前:cE!
  • 名前空間のGUID: LENOVO_BACKDOOR_NAMESPACE_GUID
  • 属性: NV + BS + RT (0x00000007)
  • 値:NULL以外の任意のバイト

次に、影響を受けるデバイスを再起動します。

この攻撃は簡単に実行できます。たとえば、Windowsユーザーは、Windows API関数SetFirmwareEnvironmentVariableを使用して、特権ユーザーランドプロセス(SE_SYSTEM_ENVIRONMENT_NAME権限のある管理者)から直接CVE-2021-3971の脆弱性を攻撃して、これらの保護を無効化できます。

ESETの分析では、Lenovo 110-15IBRのファームウェアイメージ(バージョン 1GCN25WW)を使用しています。このノートパソコンは、CVE-2021-3971の脆弱性の影響を受けるデバイスの1つです。

SecureBackDoorPeimの分析

少し理論の説明に戻りますが、UEFIのブートシーケンスはさまざまな段階から構成されており、最も早く実行される段階の1つはPEI(EFI前初期化)段階と呼ばれます。この段階では、PEIM(EFI前初期化モジュール)が実行され、永続的メモリの初期化や次のブート段階であるDXE(ドライバ実行環境)の起動などの各種のタスクが実行されます。PEIの段階からDXEの段階に情報を渡すために、HOB(Hand-Off Blocks)と呼ばれる特別なデータ構造が使用されます。

SecureBackDoorPeimは、PEIモジュールであり、UEFI変数cE!(名前空間LENOVO_BACKDOOR_NAMESPACE_GUIDに属する)の内容を読み取り、その値をDXE段階のドライバであるSecureBackDoorに渡すための正しいHOBデータ構造を準備する処理を行います。

これは、図 4に示すように、3つの簡単なステップで実行されます。

  1. EFI_PEI_READ_ONLY_VARIABLE2_PPI.GetVariable関数のVariableNameとVariableGuidパラメータを、それぞれcE!とLENOVO_BACKDOOR_NAMESPACE_GUIDに設定します。
  2. この情報を、SecureBackDoor DXE ドライバに渡すには、GUID「AD7934E7-D800-4305-BF6F-49ED9918E1AB」で識別されるHOBデータ構造を作成します。説明を簡略化するため、このHOBデータ構造のGUIDを SECURE_BACKDOOR_HOB_GUIDと命名します。
  3. 最後に、変数cE!から取得した値を、新しく作成したHOBのオフセット0x18に保存します。

SecureBackDoorの分析

SecureBackDoorは、HOBリストのSECURE_BACKDOOR_HOB_GUIDで識別されるHOBを見つけると、SPIフラッシュの保護を無効にするDXEドライバです。図5は、このHOBを見つけるために、HOBのリストを確認し、SECURE_BACKDOOR_HOB_GUIDと照合してSecureBackDoorPeimモジュールによって以前に作成されたHOBを検索していることを示しています。

ドライバがこのHOBが見つけると、オフセット0x18にあるバイト値を取得します。これは、以前にcE!(SecureBackDoorPeimが使用するUEFI変数)から取得した値です。この値が0x00と異なる場合、DXE_PCH_PLATFORM_POLICY_PROTOCOLプロトコル通知関数を登録して、DXE_PCH_PLATFORM_PROTOCOL.LockDownConfigビットマスク内のBiosLockビットをゼロにします(関連する型定義は、GitHubにあるTianoCoreの PchPlatformPolicy.hヘッダーファイルを参照)。

実際にSPIフラッシュの保護機能を無効にする方法を理解するためには、次の2つのファームウェアドライバを調査する必要があります。

  • PchBiosWriteProtect(B8B8B609-0B6C-4B8C-A731-DE03A6C3F3DC)
  • BiosRegionLock(77892615-7C7A-4AEF-A320-2A0C15C44B95)

PchBiosWriteProtectの分析

PchBiosWriteProtectは、SMMモジュールです。図 6は、DXE_PCH_PLATFORM_POLICY_PROTOCOL.LockDownConfig (型はPCH_LOCK_DOWN_CONFIG)内のBiosLockビットが設定されているかどうかを、値0x08(2進法の表記では0b1000)を使用するビット単位AND演算で確認していることを示しています。

このビットが設定されていない場合、以下の処理を実行する数行のコードをスキップします。

  • アドレス0xE00F8054から32ビット値を読み取るMMIOの読み取り演算の実行(図 6の15行目)このアドレスを図 3で説明したPython関数pci_decode_from_addrの引数に渡すと、その関数はバス:0、デバイス:0x1F、関数:0、オフセット:0x54のようにデコードされたアドレスとして表示します。
    これらの値と、これらの脆弱性の影響を受けるノートパソコン(このケースではLenovo 110-15IBR)がNシリーズのIntel SoC(システム・オン・チップ)を使用していることがわかったため、そのデータシート(セクション33.14.13、2258ページ)で、このSoCがSPIベースアドレス(SBASE)、SPIコンフィグ空間(ここではSPIBARで参照)のアドレスが含まれる32ビットPCIコンフィグレジスタにアクセスして、グローバル変数に保存することがわかります。
  • SW SMIハンドラ関数 DispatchFunctionの登録(図 6の行22)
    登録されたこのSW SMIハンドラのDispatchFunctionは、プラットフォームを初期化するときに呼び出されます。図 7にあるこの関数コードを見ると、SPIBARオフセット0xFCにあるレジスタでビット5を設定しています。このドキュメント(33.10.48 BCRのセクション)を見ると、これはBIOS制御レジスタのSMM_BWP(このチップセットではEISS)ビットに相当します。

最終的に、DispatchFunctionは、BIOS制御レジスタでBLEビットが設定されているときに、SMM以外のコードからBIOS制御レジスタ内のBIOSWEビットを設定すると発生するSMI割り込みを処理するSMIハンドラ関数(図 7のClearBIOSWE)を登録する処理も行います。その場合、インストールされたハンドラがBIOSWE(このチップセットではWPD)ビットを0に戻します。

このコードをスキップすると、BIOS制御レジスタが不正に設定され、SPIフラッシュが改変されるリスクが生じます。

BiosRegionLockの分析

2番目のドライバであるBiosRegionLockは、保護範囲レジスタPR0-PR4を設定します。前述のPchBiosWriteProtectと同様に、DXE_PCH_PLATFORM_POLICY_PROTOCOL.LockDownConfig(型PCH_LOCK_DOWN_CONFIG)内のBiosLockビットを使用して、SPIフラッシュの保護(この場合は保護範囲レジスタ)を設定するかどうかを決定します。

図 8に示すように、BiosLockビットが設定されていないことがわかると、これらのレジスタを設定するコードを単にスキップし、SPIフラッシュが保護されない状態になります。

CVE-2021-3972 – ChgBootDxeHookドライバ – UEFIセキュアブートの無効化

次に、CVE-2021-3972の脆弱性について説明します。権限を昇格している攻撃者がこの脆弱性を悪用すると、UEFI変数を1つ作成するだけで、UEFIセキュアブートの状態など、さまざまなUEFIファームウェアの設定を変更したり、UEFIファームウェアの工場出荷時設定を復元したりできます。

このような操作は、システムのセキュリティに重大な影響を与えることは言うまでもありません。UEFIセキュアブートを無効にすると、ファームウェアがブートプロセスでUEFIドライバとアプリケーションの整合性を検証しなくなり、信頼できないあるいは悪意のあるドライバやアプリケーションがロードされます。工場出荷時設定が復元されると、UEFIセキュアブートは直接無効になるわけではあませんが、脆弱性のあるブートローダーなどのいくつかのUEFIアプリケーション(例、BootHoleを参照)がシステムに展開され、UEFIセキュアブートがバイパスされるリスクがあります。

ESETの分析では、Lenovo 330-15IGMのファームウェアイメージ(バージョン 7XCN41WW)を使用しています。このノートパソコンは、CVE-2021-3972の脆弱性の影響を受けます。

たとえば、次のようなUEFI変数を作成するだけでLenovo 330-15IGMでUEFIセキュアブートを無効にできます。

  • 名前:ChgBootSecureBootDisableまたはChgBootChangeLegacy
  • 名前空間のGUID:LENOVO_BACKDOOR_NAMESPACE_GUID
  • 属性:NV + BS + RT (0x00000007)
  • 値:NULL以外の任意のバイト

そして、ノートパソコンを再起動します。

この攻撃は簡単に実行できます。Windowsユーザーは、Windows API関数 SetFirmwareEnvironmentVariableを使用して、特権ユーザーランドプロセス(SE_SYSTEM_ENVIRONMENT_NAME権限を持つ管理者)からこれらの変数を簡単に作成できます。

このバックドアの機能は、影響を受けるデバイスのファームウェアに含まれる以下の2つのドライバによって実装されています。

  • ChgBootSmm(4CA0062A-66FE-4BE7-ACE6-FDE992C1C5EC)
  • ChgBootDxeHook(C9C3D147-9A92-4D00-B3AE-970E58B5C3AC)

ChgBootSmmの分析

ChgBootSmmは、SW SMIハンドラ関数を登録するSMMモジュールです。図9に示すように、EFI_SMM_SW_DISPATCH2_PROTOCOL.Register関数を使用してこのSMIハンドラを登録し、SwSmiInputValueを0xCAに設定します。つまり、I/Oポート0xB2に値 0xCAを書き込むことで、この関数の実行をトリガーできます。

図 10にあるインストールされたSMIハンドラを見てみると、EFI_SMM_VARIABLE_PROTOCOL関数のSmmGetVariableとSmmSetVariableを使用して、さまざまなUEFI変数から読み取りと書き込みを行っています。また、作成または変更する変数は、RBXレジスタに保存された状態の値に基づいて決定されています。

さらに、書き込まれた各変数には同じ属性のビットマスク「0x00000007 (NV|BS|RT)」が設定されていますが、これは、作成されたすべての変数は不揮発性ストレージに保存され、デバイスを再起動してもそのまま残ります。

このSW SMIハンドラを使用してアクセスできる変数の全リストを以下に示します。

名前空間: LENOVO_BACKDOOR_NAMESPACE_GUID

  • ChgBootSecureBootDisable
  • ChgBootSetPxeToFirst
  • ChgBootSetEfiPxeOneTime
  • ChgBootRestoreFactory
  • ChgBootFullRese
  • ChgBootSecureBootEnable
  • ChgBootBootOrderSetDefault
  • ChgBootChangeLegacy
  • ChgBootLegacyLoadDefault
  • ChgBootUefiLoadDefault
  • @Rm
  • OneTimeDisableFastBoot

名前空間:A04A27F4-DF00-4D42-B552-39511302113D

  • BootType
  • Setup

ChgBootという文字列で始まる変数に注目すると、これらの変数がDXEドライバChgBootDxeHookの「コマンド」として使用されており、いくつかのアクションを実行するかしないかを示していることがわかります。これらの多くの変数には分かりやすい名前が付けられています。セキュリティの観点からは、以下の変数は特に重要です。

  • ChgBootSecureBootDisableおよびChgBootChangeLegacy
    これらのいずれか作成されると、ChgBootDxeHookは次回ブートするときに、UEFIセキュアブート機能を無効にします。
  • ChgBootRestoreFactory
    この変数が作成されると、ChgBootDxeHookは、次回ブートするときに、UEFIセキュアブートの変数PK、KEK、db、およびdbxを工場出荷時のデフォルト値に戻します。これによって、ユーザーが使用している独自のセキュアブートキーが破損してシステムが起動できなくなったり、最新の失効情報が含まれていない可能性のあるdbxをロードしたりなど、いくつかの問題を引き起こす恐れがあります。不正なDBXがロードされる問題が発生すると、脆弱性があることがわかっている(たとえばBootHole)ブートローダーなど、いくつかのUEFIアプリケーションがシステムに展開されるリスクにさらされる可能性があります。この場合、攻撃者はUEFIセキュアブートの検証もバイパスできるようになります。

上記のすべてのUEFI変数ChgBoot*は、ランタイムアクセスから保護されていないため、このSW SMIハンドラを使用しなくても、Windows APIなどを使用して作成できます。つまり、管理者権限が付与されているユーザーモードのプロセスから、重要なセキュリティ機構を無効化できます。

SW SMIハンドラを起動して無効化する場合には、CHIPSECコマンドを使用してChgBootSecureBootDisable変数を作成できます。

chipsec_util.py smi send 0 0xCA 0x53 0x53CA 0x1D 0 0xB2

ChgBootDxeHookの分析

これまでの分析によって、攻撃者は適切なUEFI変数ChgBoot*を作成して、ブート時にUEFIセキュアブートを無効にしたり、工場出荷時のUEFIセキュアブートキーを復元したりしり必要があることが分かりました。では、内部的な処理はどうなっているのでしょうか?この機能は DXEドライバChgBootDxeHookによって処理され、すべてのChgBoot*のUEFI変数は 図 11に示されているように、sub_3370関数でチェックされます。

UEFIセキュアブートが無効化される仕組みは、ChgBootSecureBootDisableCheck関数の内部を調べることでわかります(ChgBootChangeLegacyCheck関数でも全く同じ仕組みになっており、チェックされる変数が異なるだけです)。

図12にあるように、この関数はランタイムサービスのGetVariable関数を使用して、UEFI変数ChgBootSecureBootDisableが存在しているかどうかをチェックし、存在する場合は(その値が何であれ)、DisableSecureBootという名前の関数を実行します。

図13に示されているように、この関数は次の2つの処理を行っています。

  • オフセット0x4D9にあるSetup UEFI変数のバイト値をゼロにします。このバイトは、BIOSセットアップユーティリティ内のUEFIセキュアブートステータスのインジケータだと思われます。
  • DXEドライバSecureBootServiceによってインストールされるプロトコル関数(図 13にあるプロトコルGUID「C706D63F-6CCE-48AD-A2B4-72A5EF9E220C」または「LENOVO_SECURE_BOOT_SERVICES_PROTOCOL_GUID」)と実行する操作 (この場合は値0x02で指定されるUEFIセキュアブートを無効にする操作)を指定するパラメータと一緒に呼び出します。

では、LENOVO_SECURE_BOOT_SERVICES_PROTOCOLのこの関数はどのようにUEFIセキュアブートを無効化するのでしょうか?この無効化の操作は、SMMとDXEを複合したドライバVariableRuntimeDxe によって登録されるSW SMIハンドラ0xECを呼び出して実行されます。このときに、SecureBootEnforceという名前の認証済みのUEFI変数(名前空間EFI_GENERIC_VARIABLE_GUID)が0x00に設定されます。

しかし、この脆弱性の影響を受けるいくつかのモデル(たとえば、Lenovo V14-IIL)では、それほど簡単に無効化できるわけではなく、UEFI変数ChgBootSecureBootDisableを作成するだけでは、UEFIセキュアブートを無効にすることはできません。この操作の本当の狙いは何でしょうか?

図14に示しているように、ChgBootSecureBootDisable 変数がチェックされた後に、別の条件が存在しています。これは、GUID「C20E5755-1169-4C56-A48A-9824AB430D00」( 図 14のLENOVO_VARIABLE_PROTOCOL_GUID)で指定されるプロトコルによってアクセスされる特別な永続的ストレージLenovoVariableから取得した値に、値「Y (0x59)」が含まれる場合にのみ、UEFIセキュアブートを無効にする条件です。

このチェックを実行するモデルでは、OSからのセキュアブートを無効にするためにさらに高い権限が必要となりますが、SMMモジュールLenovoVariableSmmによって登録されるSW SMIハンドラを呼び出すことでこの操作を実行できます。

この永続的ストレージLenovoVariableについては、次のセクションで説明します。

CVE-2021-3970 - 任意のSMMの読み取り/書き込み

最後のセクションでは、SW SMIハンドラ関数における不適切な入力検証によって引き起こされる脆弱性CVE-2021-3970の分析結果について説明します。この脆弱性により、任意のSMRAMで読み取りおよび書き込みが行われ、その後のSMM実行モードで任意のコードが実行される可能性があります。

特権カーネルモードプロセスから、ソフトウェアSMI割り込みを発生させ、特別に細工したバッファーの物理アドレスをパラメータとして脆弱なSW SMIハンドラに渡すことで、この脆弱性が悪用される可能性があります。

ESETの分析では、Lenovo 330-15IGMのファームウェアイメージ(バージョン 7XCN41WW)を使用しています。このノートパソコンは、CVE-2021-3970の脆弱性の影響を受けます。

Lenovoの変数ストレージと脆弱なSW SMIハンドラ

Lenovoのコンシューマー向けノートパソコンの一部のモデルに搭載されているファームウェアは、特別な永続的ストレージLenovoVariableを実装しており、SPIフラッシュに最大4KBのデータを保存できます。

このストレージは、プラットフォームのファームウェアがLenovoの製品名、マザーボードモデル名とバージョン、OEM OSライセンスなどのさまざまな情報を保存するために使用されます。また、上記のセクションで説明したように、このストレージは、ChgBootDxeHookドライバを有効にして、UEFIセキュアブート機能を無効にするために使用される場合もあります。

ESETが分析したファームウェアでは、Lenovo変数機能のSMMバージョンは、BFD02359-8DFE-459A-8B69-A73A6BAFADC0プロトコル(LENOVO_VARIABLE_PROTOCOL_GUIDと命名)によって他のドライバに提供されており、 SMM モジュールLenovoVariableSmmによってインストールされます。

このlenovo_variable_protocolのインターフェースは、次の4つの関数を提供します。

  1. Read - Lenovo変数からデータを読み込む
  2. Write - Lenovo変数にデータを書き込む
  3. Lock - Lenovo変数の書き込みをロックする
  4. Unlock - Lenovo変数の書き込みロックを解除する

LenovoVariableSmmは、他のSMMモジュールからアクセスできるようにこのプロトコルをインストールしているだけでなく、SW SMIを呼び出してOSからもこのストレージにアクセスできるSW SMIハンドラ関数を登録していることが、セキュリティ上非常に重要な点になっています。最悪なのは、SW SMIハンドラに渡されるパラメータが適切に検証されておらず、SMRAMから情報を読み取ったり、任意の情報を書き込んだりできることです。

SW SMIハンドラ関数(図 15)を詳しく見てみると、最初に、CPU保存ステートレジスタ(SMIを呼び出すときに保存されるレジスタ)からユーザーパラメータを読み出しています。

このときには以下の2つの引数が読み取られています。

  1. ECXおよびEDIレジスタに格納された値で構築される64ビットの物理アドレス。
  2. BXレジスタから実行される動作(読み取り、書き込み、削除など)を示すコマンド。

SMIハンドラの引数として渡されるこの物理アドレスには、読み取りまたは書き込みが行われるLenovo変数を特定するデータが含まれている必要があります。このバッファーの構造を図 16に示します。

このバッファーは、0x20-byteヘッダー(LENV_HDR)から始まり、Lenovo変数を特定する一意のGUIDと、変数から取得または変数に書き込むデータ長を指定する32ビット値が含まれます。ヘッダーの直後に配置されるメモリ空間は、LENOVO_VARIABLE_PROTOCOLのReadおよびWrite関数の読み取り元または書き込み先として使用されます。

呼び出し元から提供された物理アドレスに対する検証が一切行われず、LENOVO_VARIABLE_PROTOCOL関数の引数として直接渡されていることが問題です(図 15の41行目を参照)。これにより、攻撃者は、SMRAM範囲のアドレスを含む、任意の物理的なアドレスを渡すことが可能になります。

しかし、攻撃者はどのようにSMRAMからデータを読み取り、またデータを書き込むことが可能になるのでしょうか?SMRAMのデータを読み出すには、以下の手順を実行する必要があります。

  1. SMRAMの物理アドレスを見つけます。
  2. LENV_HDRヘッダーをSMRAMの32バイト前の物理アドレスにコピーします。ヘッダーには変数識別子(ランダムなGUIDである場合があります)とSMRAMから読み込むデータの長さ(最大4KB以下)が含まれる必要があります。
  3. LenovoVariableSmm(SwSmiNumber 0x80)によって登録されるSW SMIを呼び出し、BXレジスタにID 0x02コマンドを指定し(これはLenovo変数に書き込むという意味)、ECXとEDIレジスタに以前に作成したヘッダーのアドレスを指定します(これは、この変数に書き込む内容をSW SMIハンドラに指定するための操作です)。
  4. これで、この変数には指定された量のSMRAMデータが保存されているため、この変数を読み取るだけで操作は完了します。新しいバッファー(ヘッダーサイズに取得するデータのサイズを追加したもの)を割り当て、ステップ2で使用したように、同じヘッダーをバッファーにコピーします。
  5. BXレジスタにID 0x01コマンドを指定し(Lenovo変数からデータを読み込むことを意味します)、ECXとEDIレジスタにステップ4で新たに割り当てたバッファーのアドレスを指定し、SW SMIハンドラを再度呼び出します(これは、Lenovo変数のデータ(この時点でSMRAMのデータが含まれています)をコピーする場所をSW SMIハンドラに指定する操作です)。

CHIPSECフレームワークを用いた上記のステップの例を図17に示します。SMRAMの最初の0x100バイトを含むスクリプトの出力を図18に示します。

 

SMRAMにデータを書き込めるようにする処理も原理はほぼ同じです。最初に攻撃者が自身のデータをLenovoの変数に書き込み、その変数のデータを読み取って直接SMRAMにデータを保存する必要があります。

ここでは、SMRAMの最初の0x100バイトのみを読み取る例を説明しましたが、さらに変更することで、SMRAMのすべての範囲を読み取ることも書き込むことも可能です。SW SMIハンドラLenovoVariableがSPIフラッシュを変更できることから、このような操作が意図的に実行される場合、攻撃者はSMMで悪意のあるコードを実行したり、さらに悪い場合には、攻撃者が作成した悪意のあるファームウェアをSPIフラッシュに直接書き込んだりすることも可能になります。

結論

UEFIの脅威は、検出が非常に難しく危険です。UEFIへの攻撃は、OSに制御を渡す前のブートプロセスの早期の段階で実行されるため、OSを攻撃するペイロードの実行を防止するセキュリティ機能の上位層にあるほぼすべての対策や緩和策を回避できることを意味します。UEFIの脅威は、UEFIの脅威の展開や実行を防止するために構築されているすべてのセキュリティ保護機能をどのように回避または無効化するのでしょうか?

実環境で近年検出されたLoJaxMosaicRegressorMoonBounceESPecterFinSpyなどのUEFIの脅威はすべて、展開および実行するために何らかの方法でセキュリティメカニズムを回避または無効化していました。しかし、実環境で初めて検出されたUEFIルートキットである LoJax(2018年にESETが発見)のケースのみ、Speed Racerの脆弱性を攻撃できるReWriter_binaryを使用しており、その攻撃方法が明らかになっています。

ファームウェアのセキュリティ対策をオフにしたり回避したりする方法は、脆弱性だけではありません。しかし、さまざまなファームウェアが実装され、環境が複雑になっていることから、今後多くのファームウェアの脆弱性が検出されると考えられます。

この1年で、UEFIファームウェアの重大な多くの脆弱性が公開されるようになりました。Binarlyの研究者がブログ「 An In-Depth Look At The 23 High-Impact Vulnerabilities(深刻な影響を及ぼす23の脆弱性の詳解)」と「16 High Impact Vulnerabilities Discovered In HP Devices(HPデバイスで検出された影響を及ぼす脆弱性)」で公開した脆弱性と、SentinelOneの研究者がブログ「Another Brick in the Wall: Uncovering SMM Vulnerabilities in HP Firmware(HPファームウェアで見つかったSMMの脆弱性)」で公開した脆弱性は特に注意が必要です。

ESETが検出したこれらのUEFIの脆弱性や、他のセキュリティ企業が検出した上記の脆弱性は、UEFIの脅威を展開することが実際にはそれほど難しくないことを示しています。ここ数年間に実環境で検出されたUEFIの脅威の数が多くなっていることからも、サイバー攻撃者がこのことを認識している可能性があることを示唆しています。

このブログで説明した脆弱性に対応するため、Lenovoのノートパソコンを所有しているすべてのユーザーは、影響を受けるデバイスリストを確認し、できる限りメーカーの指示に従ってファームウェアを更新することを強くお勧めします。

CVE-2021-3972の影響を受けるデバイスがEODS(開発サポート終了)になっており、修正プログラムを利用できない場合、UEFIセキュアブートの状態が変更されないように保護するために、UEFIセキュアブートの設定が変更されてもディスクデータにアクセスできないように保護するTPM対応のフルディスク暗号化ソリューションを使用できます。