ESET-Forscher haben drei Sicherheitslücken entdeckt und analysiert, die verschiedene Lenovo-Laptop-Modelle für Privatanwender betreffen. Die ersten beiden dieser Schwachstellen - CVE-2021-3971 und CVE-2021-3972 - betreffen UEFI-Firmware-Treiber. Die Backdoors stammen von Lenovo selbst und sollten eigentlich nur während des Fertigungsprozesses zugänglich sein. Durch einen Fehler wurden sie jedoch in die BIOS Images übernommen, die an die Kunden ausgeliefert wurden. Diese betroffenen Firmware-Treiber können von Angreifern dazu missbraucht werden, um den SPI-Flash-Schutz (BIOS-Kontrollregister-Bits und Protected-Range-Register) abzuschalten, oder die UEFI-Secure-Boot-Funktion von einem privilegierten Benutzermodus-Prozess während der Laufzeit des Betriebssystems direkt deaktivieren zu lassen. Die Ausnutzung dieser Schwachstellen würde es Angreifern ermöglichen, SPI-Flash- oder ESP-Implantate wie LoJax oder unsere jüngste UEFI-Malware-Entdeckung ESPecter auf den betroffenen Geräten einzusetzen und erfolgreich auszuführen.

Diese Schwachstellen zu finden fiel uns nicht allzu schwer. Die von CVE-2021-3971 betroffenen Firmware-Treiber fielen uns sofort durch ihre sehr unglücklichen (aber überraschend ehrlichen) Namen auf: SecureBackDoor und SecureBackDoorPeim. Nach einer ersten Analyse entdeckten wir weitere Lenovo-Treiber, die einige gemeinsame Merkmale mit den SecureBackDoor*-Treibern aufweisen: ChgBootDxeHook und ChgBootSmm. Wie sich herausstellte, war ihre Funktionalität sogar noch interessanter und konnte zur Deaktivierung von UEFI Secure Boot (CVE-2021-3972) missbraucht werden.

Außerdem entdeckten wir bei der Untersuchung der Binärdateien der "sicheren" Hintertüren die dritte Sicherheitslücke: SMM-Speicherbeschädigung innerhalb der SW SMI-Handler-Funktion (CVE-2021-3970). Diese Schwachstelle ermöglicht beliebiges Lesen/Schreiben von/auf SMRAM, was zur Ausführung von bösartigem Code mit SMM-Privilegien und möglicherweise zum Einsatz eines SPI-Flash-Implantats führen kann.

Wir haben alle entdeckten Schwachstellen am 11. Oktober 2021 an Lenovo gemeldet. Insgesamt umfasst die Liste der betroffenen Geräte mehr als hundert verschiedene Laptop-Modelle mit Millionen von Nutzern weltweit, von erschwinglichen Modellen wie dem Ideapad-3 bis hin zu fortschrittlicheren Modellen wie dem Legion 5 Pro-16ACH6 H oder dem Yoga Slim 9-14ITL05. Die vollständige Liste der betroffenen Modelle mit aktiver Entwicklungsunterstützung ist im entsprechenden Lenovo Advisory veröffentlicht.

Zusätzlich zu den im Advisory aufgeführten Modellen sind auch mehrere andere Geräte betroffen, die wir Lenovo gemeldet haben, die aber nicht mehr gefixt werden, da sie das Ende des Entwicklungssupports (EODS) erreicht haben. Dazu gehören Geräte, bei denen wir die gemeldeten Schwachstellen zum ersten Mal entdeckt haben: Ideapad 330-15IGM und Ideapad 110-15IGR. Die Liste der EODS-Geräte, die wir identifizieren konnten, ist in ESETs Repository für Schwachstellenmeldungen verfügbar.

Lenovo bestätigte die Schwachstellen am 17. November 2021 und wies ihnen die folgenden CVEs zu:

  • CVE-2021-3970 LenovoVariableSmm - SMM beliebiges Lesen/Schreiben
  • CVE-2021-3971 SecureBackDoor - Deaktivieren des SPI-Flash-Schutzes
  • CVE-2021-3972 ChgBootDxeHook - Deaktivieren von UEFI Secure Boot

Zeitplan für die Offenlegung von Schwachstellen:

  • 11.10.2021: Die Schwachstellen wurden Lenovo gemeldet
  • 12.10.2021: Lenovo antwortet und bestätigt, dass es die Probleme untersucht
  • 17.11.2021: Lenovo bestätigt die Schwachstellen und informiert uns über das geplante Datum für die Veröffentlichung des Hinweises - 8. Februar 2022
  • 20.01.2022: Lenovo bittet darum, die Veröffentlichung auf den neuen Termin - 18. April - zu verschieben, da es Probleme bei der Entwicklung gibt
  • 18.04.2022: Lenovo Sicherheitshinweis veröffentlicht
  • 19.04.2022: ESET Research Blogpost veröffentlicht

UEFI Firmware - technische Hintergründe

Bevor wir mit der Analyse der gemeldeten Schwachstellen beginnen, möchten wir eine Einführung in die grundlegende Theorie der UEFI-Protokolle, den Systemverwaltungsmodus, die UEFI-NVRAM-Variablen, UEFI Secure Boot und den grundlegenden SPI-Flash-Schreibschutz geben.

Beachten Sie, dass es unser Ziel ist, nur das notwendige Minimum zu erklären, das für das Verständnis der Analyse der hier gemeldeten Schwachstellen erforderlich ist. Diejenigen, die mit diesen Themen bereits vertraut sind, sollten direkt zum Abschnitt Technische Analyse springen. Denjenigen, die diese Einführung für unzureichend halten, empfehlen wir einen Blick in die Blogpost-Reihe, die von Forschern von SentinelOne geschrieben wurde und der UEFI-Spezifikation.

UEFI-Dienste und -Protokolle

UEFI definiert zwei Arten von Diensten, die von UEFI-Treibern und -Anwendungen verwendet werden können - Boot (EFI_BOOT_SERVICES) und Runtime (EFI_RUNTIME_SERVICES). Beide werden dem Einstiegspunkt des Treibers oder der Anwendung über das Argument EFI_SYSTEM_TABLE übergeben.

Sie stellen die grundlegenden Funktionen und Datenstrukturen bereit, die die Treiber und Anwendungen für ihre Arbeit benötigen. Das gilt z.B. für die Installation von Protokollen, das Auffinden vorhandener Protokolle, die Speicherzuweisung, die Manipulation von UEFI-Variablen usw.

UEFI-Boot-Treiber und -Anwendungen verwenden Protokolle in großem Umfang. Im Kontext von UEFI sind Protokolle einfach Gruppen von Funktionen, die durch eine GUID identifiziert werden. Sobald diese Protokolle installiert sind, befinden sie sich im Speicher und können von anderen Treibern oder Anwendungen verwendet werden, bis EFI_BOOT_SERVICES.ExitBootServices aufgerufen wird. Die UEFI-Spezifikation definiert viele solcher Protokolle, um die häufigsten Anwendungsszenarien für Firmware abzudecken, aber Firmware-Entwickler können dennoch ihre eigenen Protokolle definieren, um diese Grundfunktionalität zu erweitern.

UEFI Variablen

UEFI-Variablen sind ein spezieller Firmware-Speichermechanismus, der von UEFI-Modulen verwendet wird, um verschiedene Konfigurationsdaten zu speichern. Das schließt Boot-Konfiguration, UEFI Secure Boot-Einstellungen, Zertifikate und ähnliche Daten ein. Diese Variablen werden immer durch einen Variablennamen und einen Namespace (GUID) identifiziert.

Bei der Erstellung einer UEFI-Variablen werden Attribute verwendet, um anzugeben, wie die Variable vom System gespeichert und verwaltet werden soll. Auf diese Weise kann man Variablen persistent (überlebt Reboots), vorübergehend oder sogar authentifiziert machen. Authentifiziert bedeutet im Zusammenhang mit NVRAM-Variablen, dass der Variableninhalt nur dann geändert werden kann, wenn die neuen Variablendaten korrekt mit dem autorisierten privaten Schlüssel signiert sind - Lesezugriff auf die Variable ist für jeden erlaubt.

Es folgen Beispiele für einige dieser Attribute; hier werden nur die wichtigsten aufgeführt:

  • VARIABLE_ATTRIBUTE_NON_VOLATILE (NV)(0x00000001)
    Variablen, die dieses Attribut verwenden, bleiben über die Reboots hinweg bestehen und werden in einem festen Hardwarespeicher (NVRAM) mit begrenzter Kapazität (in der Regel etwa 64 MB) gespeichert.
  • VARIABLE_ATTRIBUTE_BOOTSERVICE_ACCESS (BS)(0x00000002)
    Wenn das BS-Attribut gesetzt ist, sind Variablen, für die das RT-Attribut nicht gesetzt ist, nach der Ausführung von ExitBootServices für die Funktion GetVariable nicht sichtbar.
  • VARIABLE_ATTRIBUTE_RUNTIME_ACCESS (RT)(0x00000004) 
    Um nach der Ausführung von EFI_BOOT_SERVICES.ExitBootServices über die Funktion GetVariable auf eine Variable zuzugreifen, muss das Attribut RT gesetzt sein.
  • VARIABLE_ATTRIBUTE_AUTHENTICATED_WRITE_ACCESS (AW)(0x00000010)
  • VARIABLE_ATTRIBUTE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS (AWT)(0x00000020)

Eine vollständige Liste der Attribute und ihrer Verwendungsregeln finden Sie in der UEFI Spezifikation.

Es ist wichtig zu beachten, dass seit Windows 8 eine API den Zugriff auf UEFI-Variablen von einem privilegierten Userland-Prozess (Administrator mit SE_SYSTEM_ENVIRONMENT_NAME-Recht) erlaub. Diese API-Funktionen sind:

System Management Mode (SMM)

SMM ist ein hoch privilegierter Ausführungsmodus von x86-Prozessoren, der oft als "Ring -2" bezeichnet wird.

SMM-Code wird im Kontext der System-Firmware geschrieben und in der Regel für verschiedene Aufgaben verwendet, darunter erweiterte Energieverwaltung, Ausführung von OEM-eigenem Code und sichere Firmware-Aktualisierungen. Er bietet eine unabhängige Ausführungsumgebung, die für das laufende Betriebssystem völlig unsichtbar ist. Der in SMM verwendete Code und die Daten werden in einem hardwaregeschützten Speicher, dem SMRAM, gespeichert, auf den nur von SMM aus zugegriffen werden kann.

Um in das SMM zu gelangen, muss ein spezieller Prozessorinterrupt namens SMI (System Management Interrupt) ausgelöst werden. SMIs können durch Software oder durch die Hardware der Plattform ausgelöst werden. Für die Zwecke dieses Blogposts reicht es aus, zu verstehen, dass eine der Möglichkeiten, einen SMI (insbesondere einen Software-SMI - SW SMI) zu erzeugen und das SMM auf Systemen mit Intel-Architektur zu aktivieren, darin besteht, an den E/A-Port 0xB2 zu schreiben (unter Verwendung der OUT-Anweisung). Dies wird häufig von Software verwendet, um Firmware-Dienste während der Systemlaufzeit aufzurufen.

Definition von SW SMI-Handlern in der UEFI-Firmware

Um zu verstehen, was unter der Haube passiert, wenn wir die oben erwähnte Methode zum Auslösen eines SW SMI verwenden, müssen wir einen Blick in Band 4 der UEFI Platform Initialization Specification, Version 1.4 (ZIP-Archiv), werfen und die Definition der EFI_SMM_SW_DISPATCH2_PROTOCOL finden (siehe Abbildung 1).

Abbildung 1. EFI_SMM_SW_DISPATCH2_PROTOCOL Definition

Wie in der Spezifikation beschrieben, kann dieses Protokoll von SMM-Modulen verwendet werden, um Handler-Funktionen zu installieren, die auf bestimmte Software-Interrupts reagieren.

Um eine solche Handler-Funktion zu installieren, wird die Dienstfunktion EFI_SMM_SW_DISPATCH2_PROTOCOL.Register verwendet; die Definition des Funktionstyps ist in Abbildung 2 dargestellt.

Abbildung 2. EFI_SMM_SW_DISPATCH2_PROTOCOL.Register Funktionsdefinition

Dieser Dienst installiert eine DispatchFunction-Funktion, die aufgerufen wird, wenn die durch RegisterContext->SwSmiInputValue angegebene Software-SMI-Quelle erkannt wird. Mit anderen Worten, diese DispatchFunction wird aufgerufen, wenn wir einen SW-SMI-Interrupt erzeugen, indem wir denselben SwSmiInputValue, der bei der Installation des Handlers in RegisterContext->SwSmiInputValue angegeben wurde, an den I/O-Port 0xB2 schreiben.

Bei den SW-SMI-Interrupts werden die Parameter meist über CPU-Register an den SMI-Handler übergeben. Wenn der SW SMI-Interrupt ausgelöst wird, wird der Kontext für alle CPUs zum Zeitpunkt der Auslösung des Interrupts im SMRAM gespeichert. Der aufgerufene SMI-Handler kann diesen Kontext mit den EFI_SMM_CPU_PROTOCOL-Funktionen ReadSaveState bzw. WriteSaveState leicht lesen und ändern.

Primäre SPI-Flash-Schutzmechanismen

Die UEFI-Firmware befindet sich normalerweise im eingebetteten Flash-Speicherchip auf dem Mainboard des Computers. Es handelt sich um einen nichtflüchtigen Speicher, der über die serielle Peripherieschnittstelle (SPI) mit dem Prozessor verbunden ist.

Dieser Speicher wird durch eine Neuinstallation des Betriebssystems nicht beeinträchtigt und stellt daher ein verlockendes Ziel für Bedrohungsakteure dar, die ihre Implantate einsetzen wollen - wie im Fall von LoJax, MosaicRegressor und MoonBounce.

Zur Verhinderung unerwünschter Änderungen am SPI-Flash stehen mehrere Sicherheitsmechanismen zur Verfügung, wobei die wichtigste Verteidigungslinie die speziellen, vom Chipsatz selbst bereitgestellten, speicherbezogenen Konfigurationsregister sind - das BIOS-Kontrollregister und fünf geschützte Bereichsregister ("Protected Range").

BIOS Kontrollregister

In diesem Register werden drei spezifische Bits für die SPI-Flash-Zugriffssteuerung verwendet. Beachten Sie, dass sie bei anderen Chipsätzen zwar anders benannt sein können, das Prinzip aber dasselbe ist.

  1. BIOSWE (bit 0)

Wenn diese Option gesetzt ist, ist der Zugriff auf den BIOS-Speicher sowohl für Lese- als auch für Schreibzyklen freigegeben, andernfalls ist der Zugriff nur lesend möglich.

  1. BLE (bit 1)

Wenn es gesetzt ist, kann BIOSWE nur durch SMM-Code von 0 auf 1 gesetzt werden. Jeder Versuch, BIOSWE durch Nicht-SMM-Code zu setzen, löst ein SMI aus. Dies bietet dem OEM die Möglichkeit, einen SMI-Handler zu implementieren, um das BIOSWE-Bit zu schützen, indem es auf 0 zurückgesetzt wird.

  1. SMM_BWP (bit 5)

Wenn dieses Bit gesetzt ist, ist der BIOS-Bereich nicht beschreibbar, es sei denn, alle Prozessoren befinden sich im SMM und BIOSWE ist 1. Das Setzen dieses Bits behebt die Speed Racer Race Condition-Schwachstelle (ein Exploit für diese Schwachstelle war im ReWriter_binary-Tool vorhanden, das von der Sednit-Gruppe zur Verteilung von LoJax verwendet wurde).

Protected Range Registers (PR0-PR4)

Jedes Register legt unabhängige Lese-/Schreibberechtigungen für einen bestimmten Bereich des SPI-Flash-BIOS-Bereichsspeichers fest. Sie können nur gesetzt werden, wenn das Flash Configuration Lock-Down (FLOCKDN) Bit im Hardware Sequencing Flash Status (HSFS) Register nicht gesetzt ist.

Dieses FLOCKDN-Bit sollte von der Plattform-Firmware während der Initialisierung der Plattform gesetzt werden - direkt nach dem Setzen der Protected Range (PR)-Register. Sobald FLOCKDN gesetzt ist, wird es erst nach dem nächsten Hardware-Reset wieder gelöscht. Das bedeutet, dass Speicherbereiche, die durch Protected Range-Register geschützt sind, nach ihrer Sperrung nicht mehr durch Laufzeitcode (einschließlich SMM-Code) verändert werden können. Selbst legitime Firmware-Updates müssen vor der Sperrung der PR-Register durchgeführt werden.

Verfügbare Lösungen für aktuelle Systeme

Moderne Systeme verfügen heutzutage in der Regel über Lösungen, die hardwarebasierte Boot-Integrität bieten (z. B. Intel Boot Guard), die bei ordnungsgemäßer Konfiguration und ohne zusätzliche Schwachstellen vor dem Booten von nicht vertrauenswürdigem Firmware-Code schützen - selbst wenn die oben genannten, vom Chipsatz bereitgestellten Schutzmechanismen aufgrund einer Fehlkonfiguration oder Schwachstelle nicht in der Lage sind, den SPI-Flash zu schützen.

Wie man auf den PCI/PCIe-Konfigurationsbereich zugreift

Das BIOS-Kontrollregister, die Schutzbereichsregister und viele andere Konfigurationsregister können durch Zugriff auf den PCI(e)-Konfigurationsraum gelesen oder geschrieben werden. Der Ort (oder die Adresse) des PCI(e)-Konfigurationsraums für die spezifischen PCI-kompatiblen Geräte (z. B. SPI-Flash) wird durch die drei Werte angegeben:

  • Bus
  • Device
  • Function

Konfigurationen, die sich auf dieses Gerät beziehen, befinden sich an den Offsets innerhalb dieses Konfigurationsraums. Es gibt zwei übliche Wege, um auf den PCI(e)-Konfigurationsraum zuzugreifen:

  • Verwendung von Port I/O

In diesem Fall werden die Maschinen-I/O-Befehle IN und OUT in Kombination mit den I/O-Ports 0xCF8 (CONFIG_ADDRESS) und 0xCFC (CONFIG_DATA) verwendet, um auf bestimmte Konfigurationsdaten im PCI-Konfigurationsbereich zuzugreifen. Da dies für den Zweck unseres Blogposts nicht notwendig ist, werden wir hier nicht auf die Details eingehen.

  • Verwendung von Memory Mapped I/O (MMIO)

Bei der Verwendung von Memory-Mapped I/O wird der PCI-Konfigurationsbereich direkt auf den Adressbereich des Hauptspeichers abgebildet; daher kann auf die Konfigurationsdaten fast auf die gleiche Weise zugegriffen werden wie auf alle anderen Daten. Alles, was man wissen muss, ist die PCI-Adresse der gewünschten Daten. Woher kann man diese Adresse also nehmen? Wir können sie selbst konstruieren, wenn wir sie kennen:

  1. die MMIO-Basisadresse (zu finden in der MCFG ACPI-Tabelle)
  2. Bus-, Device-, Function- und Offset-Werte (auch als Register bezeichnet), die die Daten identifizieren, auf die wir zugreifen wollen (Sie finden sie im Datenblatt eines Chipsatzes).

Ein Beispiel für das Makro, das diese Werte in PCI-Adressen kodiert, findet sich in der EDK2-Header-Datei PciLib.h. Alternativ dazu ist in Abbildung 3 eine Python-Implementierung der Funktionen zur Umwandlung der MMIO-PCI-Adresse in individuelle Bezeichner und umgekehrt dargestellt.

 

Abbildung 3. PCI-Adresskodierung/-dekodierung in Python

UEFI Secure Boot

UEFI Secure Boot ist in der UEFI-Spezifikation definiert und sein Hauptzweck besteht darin, die Integrität der Boot-Komponenten zu überprüfen, um sicherzustellen, dass nur von der Plattform vertrauenswürdige Komponenten ausgeführt werden dürfen. Welche Komponenten in diesen Überprüfungsprozess einbezogen werden, hängt von der Implementierung der UEFI-Secure-Boot-Richtlinie in der jeweiligen Plattform ab. In den meisten Fällen werden nur UEFI-Treiber, Anwendungen und OPROMs von Drittanbietern überprüft, und die Treiber auf dem SPI-Flash werden implizit als vertrauenswürdig angesehen.

Um zu entscheiden, was vertrauenswürdig ist und was nicht, verwendet UEFI Secure Boot spezielle Datenbanken, die in den authentifizierten NVRAM-Variablen gespeichert sind, nämlich db und dbx.

  • Die db-Datenbank enthält eine Liste vertrauenswürdiger Public-Key-Zertifikate, die zur Authentifizierung der Signaturen von Boot-Komponenten berechtigt sind, oder sie kann zusätzlich zu den Zertifikaten auch eine Liste von Hashes der Komponenten enthalten, die unabhängig davon, ob sie signiert sind oder nicht, ausgeführt werden dürfen.
  • Die dbx-Datenbank enthält Public-Key-Zertifikate oder Hashes von UEFI-Executables, die nicht ausgeführt werden dürfen - um die Ausführung von signierten Executables mit bekannten Sicherheitslücken, widerrufenen Zertifikaten usw. zu verhindern.

Technische Analyse

Wir beginnen mit unserer Analyse der Treiber, die von CVE-2021-3971 und CVE-2021-3972 betroffen sind und fahren dann mit der SMM-Schwachstelle (CVE-2021-3970) fort.

Zu Beginn unseres Blogposts haben wir erwähnt, dass die SecureBackDoor*- und ChgBoot*-Treiber einige Gemeinsamkeiten aufweisen. Wie ist also die Verbindung zwischen ihnen? Beide verwenden die UEFI-Variablen innerhalb des 6ACCE65D-DA35-4B39-B64B-5ED927A7DC7E-Namensraums als Kontrollmechanismus, um zu entscheiden, ob ihre Funktionalität aktiviert werden soll (wir werden diese GUID als LENOVO_BACKDOOR_NAMESPACE_GUID bezeichnen).

CVE-2021-3971 – SecureBackdoor Treiber

Wir beginnen mit der Analyse der Sicherheitslücke CVE-2021-3971, die es einem Angreifer ermöglicht, die SPI-Flash-Schreibschutzmechanismen zu deaktivieren, indem er einfach die NVRAM-Variable erstellt. Wenn die Plattform-Firmware diese NVRAM-Variable während des Bootvorgangs erkennt, überspringt sie die Ausführung des Codes, der für die Einrichtung der auf dem BIOS-Kontrollregister und dem Protected Range-Register basierenden SPI-Flash-Schutzmechanismen verantwortlich ist.

Infolgedessen erlaubt das kompromittierte System die Änderung des SPI-Flashs, selbst wenn dies von einem Nicht-SMM-Code aus geschieht, und ermöglicht es einem Angreifer, bösartigen Code direkt in den Firmware-Speicher einzubringen.

Dieses "Deaktivieren des SPI-Flash-Schutzes Feature" wird von den folgenden Treibern in der Firmware der betroffenen Laptops implementiert:

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

Um die oben erwähnten SPI-Flash-Schutzmechanismen unter Ausnutzung dieser Schwachstelle zu deaktivieren, muss der Benutzer lediglich eine NVRAM-Variable erstellen mit:

  • Name: cE!
  • Namespace GUID: LENOVO_BACKDOOR_NAMESPACE_GUID
  • Attribute: NV + BS + RT (0x00000007)
  • Wert: Jedes Non-Null Byte

und dann das betroffene Gerät neustarten.

Um zu verstehen, wie einfach dies ist, können Windows-Benutzer diese Schutzmaßnahmen deaktivieren, indem sie die Sicherheitslücke CVE-2021-3971 direkt vom privilegierten Userland-Prozess (Administrator mit dem Privileg SE_SYSTEM_ENVIRONMENT_NAME) unter Verwendung der Windows-API-Funktion SetFirmwareEnvironmentVariable ausnutzen.

In unserer Analyse werden wir mit dem Firmware-Image (Version 1GCN25WW) des Lenovo 110-15IBR arbeiten, das eines der Geräte ist, die von der Sicherheitslücke CVE-2021-3971 betroffen sind.

SecureBackDoorPeim Analyse

Um auf die Theorie zurückzukommen: Die UEFI-Boot-Sequenz besteht aus verschiedenen Phasen, von denen eine der frühesten als Pre-EFI-Initialisierungsphase (PEI) bezeichnet wird. In dieser Phase werden Pre-EFI-Initialisierungsmodule (PEIMs) ausgeführt, um verschiedene Aufgaben zu erfüllen, einschließlich der Initialisierung des permanenten Speichers und des Aufrufs der nächsten Boot-Phase - Driver Execution Environment (DXE). Um Informationen von der PEI-Phase an die DXE-Phase weiterzugeben, werden spezielle Datenstrukturen, die so genannten Hand-Off-Blöcke (HOBs), verwendet.

SecureBackDoorPeim ist ein PEI-Modul, das sowohl für das Lesen des Inhalts der UEFI-Variablen cE!, die zum Namensraum LENOVO_BACKDOOR_NAMESPACE_GUID gehört, als auch für die Vorbereitung der korrekten HOB-Datenstruktur verantwortlich ist, um ihren Wert an den SecureBackDoor-Treiber der DXE-Phase zu übergeben.

Dies geschieht in drei einfachen Schritten, wie in Abbildung 4 dargestellt.

Abbildung 4. Hex-Rays-dekompilierter Code des SecureBackDoorPeim-Moduls

  1. Verwendung der Funktion EFI_PEI_READ_ONLY_VARIABLE2_PPI.GetVariable mit den Parametern VariableName und VariableGuid auf die Werte cE! bzw. LENOVO_BACKDOOR_NAMESPACE_GUID.
  2. Um diese Informationen an den SecureBackDoor DXE-Treiber weiterzugeben, wird eine HOB-Datenstruktur mit der folgenden GUID AD7934E7-D800-4305-BF6F-49ED9918E1AB erstellt. Der Einfachheit halber nennen wir diese HOB-Datenstruktur GUID SECURE_BACKDOOR_HOB_GUID.
  3. Schließlich wird der aus der Variable cE! abgerufene Wert im Offset 0x18 des neu erstellten HOB gespeichert.

SecureBackDoor Analyse

SecureBackDoor ist ein DXE-Treiber, der für die Deaktivierung des SPI-Flash-Schutzes zuständig ist, wenn er in der HOB-Liste ein HOB findet, das mit SECURE_BACKDOOR_HOB_GUID gekennzeichnet ist. In Abbildung 5 ist zu sehen, dass er, um dieses HOB zu finden, die Liste der HOBs durchgeht und nach dem zuvor vom SecureBackDoorPeim-Modul erstellten HOB sucht, indem er es mit der SECURE_BACKDOOR_HOB_GUID abgleicht.

Abbildung 5. Hex-Rays-dekompilierter SecureBackDoor-Code, der für das Auffinden der von SecureBackdoorPeim erstellten HOB verantwortlich ist

Wenn dieses HOB gefunden wird, ruft der Treiber den Byte-Wert an Offset 0x18 ab - das ist der Wert, der zuvor von der cE! UEFI-Variable von SecureBackDoorPeim abgerufen wurde - und wenn dieser Wert von 0x00 abweicht, registriert er die Protokollbenachrichtigungsfunktion DXE_PCH_PLATFORM_POLICY_PROTOCOL, die das BiosLock-Bit in der Bitmaske DXE_PCH_PLATFORM_POLICY_PROTOCOL.LockDownConfig auf Null setzt (für zugehörige Typdefinitionen siehe TianoCores Header-Datei PchPlatformPolicy.h auf GitHub).

Um zu verstehen, wie der SPI-Flash-Schutz tatsächlich deaktiviert wird, müssen wir uns zwei verschiedene Firmware-Treiber ansehen:

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

PchBiosWriteProtect Analyse

PchBiosWriteProtect ist ein SMM-Modul. In Abbildung 6 ist zu sehen, dass es prüft, ob das BiosLock-Bit in DXE_PCH_PLATFORM_POLICY_PROTOCOL.LockDownConfig (Typ PCH_LOCK_DOWN_CONFIG) gesetzt ist, indem es eine bitweise UND-Verknüpfung mit dem Wert 0x08 durchführt (was in binärer Darstellung 0b1000 entspricht).

Abbildung 6. Hex-Rays-dekompilierter Code von PchBiosWriteProtect, der für die Initialisierung des BIOS-Kontrollregisters verantwortlich ist - mit SPI-Flash-Schutzfunktionen

Wenn sie nicht gesetzt ist, werden einige Codezeilen übersprungen, die für die Ausführung verantwortlich sind:

  • Ausführen einer MMIO-Leseoperation (Zeile 15 in Abbildung 6) durch Lesen eines 32-Bit-Wertes von der Adresse 0xE00F8054. Wenn wir diese Adresse als Argument an die Python-Funktion pci_decode_from_addr übergeben, die wir in Abbildung 3 eingeführt haben, wird diese Funktion die Adresse ausgeben, die als Bus: 0, Device: 0x1F, Function: 0, Versatz: 0x54 entschlüsselt wird.

Ausgehend von diesen Werten und dem Wissen, dass der betroffene Laptop (in unserem Fall Lenovo 110-15IBR) den Intel System-on-a-Chip (SoC) der N-Serie verwendet, können wir in seinem Datenblatt (Abschnitt 33.14.13, Seite 2258) feststellen, dass er auf die SPI-Basisadresse (SBASE) zugreift, ein 32-Bit-PCI-Konfigurationsregister, das die Adresse des SPI-Konfigurationsraums enthält (wir nennen es SPIBAR), und sie in der globalen Variablen speichert.

  • Registrierung der SW SMI-Handler-Funktion DispatchFunction (Zeile 22 in Abbildung 6)

Dieser registrierte SW SMI-Handler DispatchFunction wird während der Initialisierung der Plattform aufgerufen, und wenn wir uns den Code dieser Funktion in Abbildung 7 unten ansehen, sehen wir, dass sie für das Setzen von Bit 5 in dem Register verantwortlich ist, das sich im SPIBAR-Offset 0xFC befindet. Ein Blick in die Dokumentation (Abschnitt 33.10.48 BCR) zeigt, dass dies dem Bit SMM_BWP (oder EISS für unseren Chipsatz) des BIOS-Steuerregisters entspricht.

Abbildung 7. Hex-Rays-dekompilierter Code der DispatchFunction von PchBiosWriteProtect

Schließlich ist DispatchFunction auch für die Registrierung der SMI-Handler-Funktion (ClearBIOSWE in Abbildung 7) verantwortlich, die den SMI-Interrupt behandelt, der ausgelöst wird, wenn jemand versucht, das BIOSWE-Bit im BIOS-Steuerregister von einem Nicht-SMM-Code zu setzen, während das BLE-Bit im BIOS-Steuerregister gesetzt ist. In diesem Fall setzt der installierte Handler das BIOSWE-Bit (oder WPD für unseren Chipsatz) zurück auf 0.

Das Überspringen dieses Codes führt zu einer Fehlkonfiguration der BIOS-Kontrollregister, wodurch das System dem Risiko einer SPI-Flash-Veränderung ausgesetzt wird.

BiosRegionLock Analyse

Der zweite Treiber, BiosRegionLock, ist für die Einrichtung der Schutzbereichsregister PR0-PR4 zuständig. Ähnlich wie der oben beschriebene PchBiosWriteProtect verwendet er das BiosLock-Bit in DXE_PCH_PLATFORM_POLICY_PROTOCOL.LockDownConfig (Typ PCH_LOCK_DOWN_CONFIG), um zu entscheiden, ob SPI-Flash-Schutzfunktionen - in diesem Fall Protected Range-Register - gesetzt werden sollen oder nicht.

Wie in Abbildung 8 zu sehen ist, überspringt er, wenn er feststellt, dass das BiosLock-Bit nicht gesetzt ist, einfach den Code, der für das Setzen dieser Register verantwortlich ist, und lässt so den SPI-Flash ungeschützt.

Abbildung 8. Hex-Rays-dekompilierter Code, der für das Setzen der Protected Range-Register verantwortlich ist

CVE-2021-3972 – ChgBootDxeHook Treiber

Die nächste Schwachstelle, die wir uns ansehen werden, trägt die CVE-2021-3972. Diese Schwachstelle ermöglicht es einem Angreifer mit erhöhten Rechten, verschiedene UEFI-Firmware-Einstellungen zu ändern, einschließlich des UEFI-Secure-Boot-Status, oder z. B. die UEFI-Firmware-Werkseinstellungen wiederherzustellen, indem er einfach eine UEFI-Variable erstellt.

Es ist unnötig zu erwähnen, dass eine solche Aktion schwerwiegende Auswirkungen auf die Systemsicherheit haben würde. Die Deaktivierung von UEFI Secure Boot würde bedeuten, dass die Firmware keine Integritätsprüfung der UEFI-Treiber und -Anwendungen während des Bootvorgangs durchführt und somit das Laden von nicht vertrauenswürdigen oder bösartigen Anwendungen zulässt. Andererseits würde das Wiederherstellen der Werkseinstellungen UEFI Secure Boot nicht direkt deaktivieren, sondern könnte ein System dem Risiko aussetzen, dass einige UEFI-Anwendungen, wie z. B. Bootloader, mit bekannten Schwachstellen (siehe z. B. BootHole) eingesetzt werden und so eine Umgehung von UEFI Secure Boot ermöglichen.

In unserer Analyse werden wir mit dem Firmware-Image (Version 7XCN41WW) des Lenovo 330-15IGM arbeiten, das von der Sicherheitslücke CVE-2021-3972 betroffen ist.

Um zum Beispiel UEFI Secure Boot auf dem Lenovo 330-15IGM zu deaktivieren, muss der Benutzer nur eine UEFI-Variable erstellen mit:

  • Name: ChgBootSecureBootDisable oder ChgBootChangeLegacy
  • Namespace GUID: LENOVO_BACKDOOR_NAMESPACE_GUID
  • Attribute: NV + BS + RT (0x00000007)
  • Wert: Jedes Non-Null Byte

und den Laptop neustarten.

Um zu veranschaulichen, wie einfach das ist, können Windows-Benutzer diese Variablen vom privilegierten Userland-Prozess (Administrator mit SE_SYSTEM_ENVIRONMENT_NAME-Recht) aus mit der Windows-API-Funktion SetFirmwareEnvironmentVariable erstellen (siehe oben).

Dieses Backdoor-"Feature" wird von den folgenden zwei Treibern in der Firmware der betroffenen Geräte implementiert:

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

ChgBootSmm Analyse

ChgBootSmm ist ein SMM-Modul, das für die Registrierung der SW SMI-Handler-Funktion zuständig ist. Wie in Abbildung 9 dargestellt, registriert es diesen SMI-Handler mithilfe der Funktion EFI_SMM_SW_DISPATCH2_PROTOCOL.Register und setzt den SwSmiInputValue auf 0xCA. Das bedeutet, dass man die Ausführung dieser Funktion durch Schreiben des Wertes 0xCA am E/A-Port 0xB2 auslösen kann.

Abbildung 9. Hex-Rays-dekompilierter Code - ChgBootSmm-Register SW SMI-Handler Nummer 0xCA

Wenn wir uns diesen installierten SMI-Handler in Abbildung 10 ansehen, können wir sehen, dass er die EFI_SMM_VARIABLE_PROTOCOL-Funktionen SmmGetVariable und SmmSetVariable verwendet, um aus verschiedenen UEFI-Variablen zu lesen und in sie zu schreiben, wobei die Entscheidung, welche Variable erstellt oder geändert wird, auf der Grundlage des im RBX-Register gespeicherten Werts getroffen wird.

Abbildung 10. Hex-Rays-dekompilierter Code des vom ChgBootSmm-Treiber installierten SW SMI-Handlers

 

Darüber hinaus hat jede geschriebene Variable die gleiche Attribut-Bitmaske - 0x00000007 (NV|BS|RT) - was bedeutet, dass alle erstellten Variablen im nichtflüchtigen Speicher abgelegt werden und somit einen Reboot überstehen.

Die vollständige Liste der Variablen, auf die mit diesem SW SMI-Handler zugegriffen werden kann, sind:

  • Namespace: LENOVO_BACKDOOR_NAMESPACE_GUID
    • ChgBootSecureBootDisable
    • ChgBootSetPxeToFirst
    • ChgBootSetEfiPxeOneTime
    • ChgBootRestoreFactory
    • ChgBootFullRese
    • ChgBootSecureBootEnable
    • ChgBootBootOrderSetDefault
    • ChgBootChangeLegacy
    • ChgBootLegacyLoadDefault
    • ChgBootUefiLoadDefault
    • @Rm
    • OneTimeDisableFastBoot
  • Namespace: A04A27F4-DF00-4D42-B552-39511302113D
    • BootType
    • Setup

Beachten Sie die Variablen, die mit der Zeichenkette ChgBoot beginnen: Diese Variablen werden als "Befehle" für den DXE-Treiber ChgBootDxeHook verwendet und geben an, ob eine bestimmte Aktion ausgeführt werden soll oder nicht. In den meisten Fällen sind ihre Namen recht selbsterklärend, und vom Standpunkt der Sicherheit sind die folgenden die interessantesten:

  • ChgBootSecureBootDisable und ChgBootChangeLegacy
    Wenn eine von beiden erstellt wird, deaktiviert ChgBootDxeHook das UEFI Secure Boot Feature beim nächsten Bootborgang.
  • ChgBootRestoreFactory
    Wenn er erstellt wird, stellt ChgBootDxeHook beim nächsten Booten die werkseitigen Standardwerte für die UEFI Secure Boot-Variablen PK, KEK, db und dbx wieder her. Dies kann zu verschiedenen Problemen führen, von der Beschädigung der vom Opfer verwendeten benutzerdefinierten Secure Boot-Schlüssel, wodurch das System nicht mehr gebootet werden kann, bis hin zum Laden der dbx, die möglicherweise nicht die neuesten Sperrinformationen enthält. Letzteres könnte das System dem Risiko aussetzen, dass einige UEFI-Anwendungen, wie z. B. Bootloader, mit bekannten Schwachstellen (z. B. BootHole) eingesetzt werden und somit einem Angreifer ermöglichen, auch die UEFI Secure Boot-Verifizierung zu umgehen.

Alle oben genannten ChgBoot* UEFI-Variablen können auch ohne die Hilfe dieses SW SMI-Handlers erstellt werden - zum Beispiel mit Hilfe von Windows-APIs -, da sie nicht gegen Laufzeitzugriff geschützt sind. Dies bedeutet, dass Angreifer wichtige Sicherheitsmechanismen von einem Prozess im Benutzermodus mit Administratorrechten deaktivieren können.

Falls es Leser gibt, die daran interessiert sind, wie dies durch den Aufruf des SW SMI-Handlers erreicht werden kann, finden sie hier den CHIPSEC-Befehl, der zur Erstellung der ChgBootSecureBootDisable-Variable verwendet werden kann:

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

ChgBootDxeHook Analyse

Bisher wissen wir, dass Angreifer die entsprechende ChgBoot*-UEFI-Variable erstellen müssen, um UEFI Secure Boot zu deaktivieren oder die werkseitigen UEFI Secure Boot-Schlüssel während des Bootvorgangs wiederherzustellen. Wie funktioniert es aber unter der Haube? Diese Funktionalität wird vom DXE-Treiber ChgBootDxeHook gehandhabt, und alle ChgBoot*-UEFI-Variablen werden in seiner Funktion sub_3370 überprüft (siehe Abbildung 11).

Abbildung 11. Hex-Rays-dekompilierte Funktion von ChgBootDxeHook, die ChgBoot-Variablen überprüft.

Um zu sehen, wie UEFI Secure Boot deaktiviert wird, können wir einen Blick in die Funktion ChgBootSecureBootDisableCheck werfen (genau dasselbe geschieht auch in der Funktion ChgBootChangeLegacyCheck, nur dass eine andere Variable geprüft wird).

Wie in Abbildung 12 zu sehen ist, prüft die Funktion das Vorhandensein der UEFI-Variable ChgBootSecureBootDisable mithilfe der Funktion GetVariable der Laufzeitdienste und führt im Falle ihres Vorhandenseins - unabhängig von ihrem Wert - eine Funktion namens DisableSecureBoot aus.

Abbildung 12. Hex-Rays-dekompilierte Funktion von ChgBootDxeHook, die die Existenz der NVRAM-Variable ChgBootSecureBootDisable überprüft

Und jetzt wird es spannend. Wie Sie in Abbildung 13 sehen können, erfüllt diese Funktion zwei Aufgaben:

  • Setzt den Byte-Wert der Setup-UEFI-Variable bei Offset 0x4D9 auf Null; dieses Byte scheint ein Indikator für den UEFI-Secure-Boot-Status innerhalb des BIOS-Setup-Dienstprogramms zu sein.
  • Ruft die Protokollfunktion (Protokoll-GUID C706D63F-6CCE-48AD-A2B4-72A5EF9E220C oder LENOVO_SECURE_BOOT_SERVICES_PROTOCOL_GUID in Abbildung 13) auf, die vom SecureBootService DXE-Treiber mit einem Parameter installiert wurde, der eine auszuführende Operation identifiziert, in diesem Fall die Operation mit dem Wert 0x02 - was die Deaktivierung von UEFI Secure Boot bedeutet.

Abbildung 13. Hex-Rays-dekompilierter Code von ChgBootDxeHook, der für die Deaktivierung von UEFI Secure Boot verantwortlich ist

Und wie deaktiviert diese Funktion von LENOVO_SECURE_BOOT_SERVICES_PROTOCOL den UEFI Secure Boot? Sie tut dies, indem sie den SW SMI-Handler 0xEC aufruft, der vom kombinierten SMM/DXE-Treiber VariableRuntimeDxe registriert wird, der wiederum die authentifizierte UEFI-Variable namens SecureBootEnforce (Namensraum EFI_GENERIC_VARIABLE_GUID) auf 0x00 setzt.

Bei einigen betroffenen Modellen (z. B. dem Lenovo V14-IIL) ist es jedoch nicht so einfach und das einfache Erstellen einer ChgBootSecureBootDisable UEFI-Variable reicht nicht aus, um UEFI Secure Boot zu deaktivieren. Wo ist also der Haken?

Wie in Abbildung 14 zu sehen ist, gibt es nach der Überprüfung der ChgBootSecureBootDisable-Variable eine weitere Bedingung - sie deaktiviert UEFI Secure Boot nur dann, wenn der aus dem speziellen LenovoVariable-Dauerspeicher abgerufene Wert, auf den über ein Protokoll zugegriffen wird, das durch die GUID C20E5755-1169-4C56-A48A-9824AB430D00 (LENOVO_VARIABLE_PROTOCOL_GUID in Abbildung 14) identifiziert wird, den Wert Y (0x59) enthält.

Abbildung 14. Hex-Rays-dekompilierter Code - Deaktivieren von UEFI Secure Boot mit zusätzlicher LenovoVariable Prüfung

Bei Modellen, die diese Prüfung enthalten, sind höhere Privilegien erforderlich, um das sichere Booten vom Betriebssystem aus zu deaktivieren, aber es ist immer noch möglich, indem der SW SMI-Handler aufgerufen wird, der vom LenovoVariableSmm SMM-Modul registriert wird.

Eine Beschreibung dieses persistenten LenovoVariable-Speichers finden Sie im nächsten Abschnitt.

CVE-2021-3970 – Beliebiges SMM Lesen/Schreiben

In diesem letzten Abschnitt befassen wir uns mit der Analyse der Sicherheitsanfälligkeit CVE-2021-3970, die durch eine unsachgemäße Eingabevalidierung in der SW SMI-Handler-Funktion verursacht wird, die zum willkürlichen Lesen/Schreiben von/auf das SMRAM und zur anschließenden willkürlichen Codeausführung im SMM-Ausführungsmodus führen kann.

Diese Schwachstelle kann von einem privilegierten Prozess im Kernel-Modus ausgenutzt werden, indem der Software-SMI-Interrupt ausgelöst und eine physikalische Adresse eines speziell präparierten Puffers als Parameter an den verwundbaren SW-SMI-Handler übergeben wird.

In dieser Analyse werden wir mit dem Firmware-Image (Version 7XCN41WW) des Lenovo 330-15IGM arbeiten, das von der Sicherheitslücke CVE-2021-3970 betroffen ist.

Lenovos Variablen-Speicher und ein anfälliger SW SMI-Handler

In der Firmware bestimmter Lenovo-Laptop-Modelle ist ein spezieller LenovoVariable Dauerspeicher implementiert, der eine Datenspeicherung von bis zu 4 KB im SPI-Flash ermöglicht.

Sie wird von der Plattform-Firmware verwendet, um verschiedene Informationen zu speichern, darunter den Lenovo-Produktnamen, den Namen und die Version des Motherboard-Modells, die OEM-Betriebssystemlizenz oder, wie im obigen Abschnitt erwähnt, in einigen Fällen zur Deaktivierung der UEFI Secure Boot-Funktion verwendet werden.

In der von uns analysierten Firmware wird die SMM-Version der Lenovo-Variablenfunktionalität anderen Treibern durch das Protokoll BFD02359-8DFE-459A-8B69-A73A6BAFADC0 (nennen wir es LENOVO_VARIABLE_PROTOCOL_GUID) zur Verfügung gestellt und durch das SMM-Modul LenovoVariableSmm installiert.

Die Schnittstelle von LENOVO_VARIABLE_PROTOCOL bietet vier Funktionen:

  1. Read – Daten einer Lenovo Variable lesen
  2. Write – Daten in eine Lenovo Variable schreiben
  3. Lock – Schreibzugriff auf eine Lenovo Variable sperren
  4. Unlock – Schreibzugriff auf eine Lenovo Variable entsperren

Es ist wichtig zu beachten, dass LenovoVariableSmm nicht nur dieses Protokoll installiert, damit andere SMM-Module darauf zugreifen können, sondern auch die SW SMI-Handler-Funktion registriert, die den Zugriff auf diesen Speicher vom Betriebssystem aus durch Aufrufen von SW SMI ermöglicht. Das Schlimmste daran ist, dass die an den SW SMI-Handler übergebenen Parameter nicht ordnungsgemäß validiert werden, was zu willkürlichen Lese-/Schreibzugriffen auf das SMRAM führen kann.

Abbildung 15. Hex-Rays-dekompilierter Code der SW SMI-Handler-Funktion, die vom LenovoVariableSmm-Modul registriert wurde

Wenn man sich die SW SMI-Handler-Funktion in Abbildung 15 genau ansieht, erkennt man, dass sie mit dem Lesen der Benutzerparameter aus den gespeicherten CPU-Zustandsregistern beginnt - das sind Register, die zum Zeitpunkt des SMI-Aufrufs gespeichert werden.

Diese beiden Argumente werden gelesen:

  1. 64-Bit physikalische Adresse, die aus den in ECX und EDI gespeicherten Werten gebildet wird
  2. Der Befehl vom BX, der die auszuführende Aktion (Lesen, Schreiben, Löschen usw.) angibt

Diese physikalische Adresse, die als Argument an den SMI-Handler übergeben wird, sollte Daten zur Identifizierung der zu lesenden oder zu schreibenden Lenovo-Variablen enthalten; die Struktur dieses Puffers ist in Abbildung 16 dargestellt.

Abbildung 16. Struktur des Puffers, der an den LenovoVariableSmm SW SMI-Handler übergeben wird

Der Puffer beginnt mit dem 0x20-Byte-Header (LENV_HDR), der die eindeutige GUID, die die Lenovo-Variable identifiziert, und einen 32-Bit-Wert enthält, der die Länge der Daten angibt, die aus der Variable abgerufen oder in sie geschrieben werden sollen. Der Speicherbereich, der sich unmittelbar nach dem Header befindet, wird als Quell- oder Zielort für die Lese- und Schreibfunktionen von LENOVO_VARIABLE_PROTOCOL verwendet.

Das Problem ist, dass die vom Aufrufer angegebene physische Adresse in keiner Weise validiert oder überprüft wird und direkt als Argument an die LENOVO_VARIABLE_PROTOCOL-Funktionen übergeben wird (siehe Zeile 41 in Abbildung 15). Dadurch kann ein Angreifer jede beliebige physikalische Adresse übergeben - auch eine Adresse aus dem SMRAM-Bereich.

Aber wie könnte ein Angreifer dies nutzen, um vom/ins SMRAM zu lesen/schreiben? Um den SMRAM-Inhalt zu lesen, sind die folgenden Schritte erforderlich:

  1. Finden der physikalischen SMRAM Adresse.
  2. Kopieren der LENV_HDR-Header an die physikalische Adresse 32 Bytes vor dem SMRAM - der Header sollte einen Variablen-Identifikator (es kann eine zufällige GUID sein) und die Länge der Daten enthalten, die man aus dem SMRAM lesen möchte (das Maximum ist etwas unter 4KB).
  3. Aufruf der von LenovoVariableSmm (SwSmiNumber 0x80) registrierten SW SMI auf und Eingabe des Befehls mit der ID 0x02 im BX-Register an (was bedeutet, dass in die Lenovo-Variable geschrieben werden soll) und die Adresse des zuvor erstellten Headers in den ECX- und EDI-Registern (um dem SW SMI-Handler mitzuteilen, was in diese Variable geschrieben werden soll).
  4. Jetzt enthält diese Variable eine bestimmte Menge an SMRAM-Daten - wir müssen sie also nur noch lesen. Wir weisen einen neuen Puffer zu (gleich der Größe des Headers plus der Größe der abzurufenden Daten) und kopieren denselben Header hinein, wie in Schritt 2 verwendet.
  5. Erneuter Aufruf des SW SMI-Handlers und Eingabe des Befehls mit der ID 0x01 im BX-Register an (was bedeutet, dass aus der Lenovo-Variablen gelesen werden soll) und die Adresse unseres neu zugewiesenen Puffers aus Schritt 4 in den ECX- und EDI-Registern (um dem SW SMI-Handler mitzuteilen, wohin wir den Inhalt der Lenovo-Variablen kopieren wollen - der in diesem Moment Daten aus dem SMRAM enthält).

Ein Beispiel für die oben beschriebenen Schritte unter Verwendung des CHIPSEC-Frameworks ist in Abbildung 17 dargestellt; die Ausgabe des Skripts mit den ersten 0x100 Bytes des SMRAM ist in Abbildung 18 zu sehen.

Abbildung 17. CHIPSEC-Beispiel - Lesen von 0x100 Bytes aus SMRAM durch Ausnutzung von CVE-2021-3972

Abbildung 18. CHIPSEC-Skriptausgabe mit Dump der ersten 0x100 Bytes des SMRAM

Um in das SMRAM schreiben zu können, ist das Prinzip fast dasselbe - zuerst müssen die Angreifer ihre eigenen Daten in die Lenovo-Variable schreiben und dann die Daten aus der Variable direkt in das SMRAM lesen.

Wir haben ein Beispiel dafür gegeben, wie nur die ersten 0x100 Bytes des SMRAM gelesen werden können; mit zusätzlichen Änderungen ist es jedoch möglich, den gesamten SMRAM-Bereich zu lesen/schreiben. Dies könnte es Bedrohungsakteuren ermöglichen, ihren eigenen bösartigen Code im SMM auszuführen oder noch schlimmer - in Anbetracht der Fähigkeit des LenovoVariable SW SMI-Handlers, den SPI-Flash zu modifizieren - das eigene bösartige Firmware-Implantat der Angreifer direkt in den SPI-Flash zu schreiben.

Fazit

UEFI-Bedrohungen können extrem unauffällig und gefährlich sein. Sie werden früh im Boot-Prozess ausgeführt, bevor sie die Kontrolle an das Betriebssystem übergeben. Das bedeutet, dass sie fast alle Sicherheitsmaßnahmen und Abschwächungen weiter oben im Stack umgehen können, die die Ausführung ihrer Betriebssystem-Nutzdaten verhindern könnten. Wie überwinden also UEFI-Bedrohungen alle Sicherheitsmaßnahmen, die geschaffen wurden, um die Bereitstellung oder Ausführung von UEFI-Bedrohungen selbst zu verhindern?

Alle realen UEFI-Bedrohungen, die in den letzten Jahren entdeckt wurden (LoJax, MosaicRegressor, MoonBounce, ESPecter, FinSpy), mussten die Sicherheitsmechanismen auf irgendeine Weise umgehen oder deaktivieren, um bereitgestellt und ausgeführt werden zu können. Doch nur im Fall von LoJax, dem ersten UEFI-Rootkit in freier Wildbahn (das 2018 von ESET Research entdeckt wurde), wissen wir, wie es gemacht wurde - durch Verwendung des ReWriter_binary, das die Speed Racer-Schwachstelle ausnutzen kann.

Auch wenn Schwachstellen nicht die einzige Möglichkeit sind, Firmware-Sicherheitsmaßnahmen auszuschalten oder zu umgehen, gibt es viele solcher Schwachstellen, und aufgrund der Anzahl verschiedener Firmware-Implementierungen und ihrer Komplexität warten wahrscheinlich noch viele weitere darauf, entdeckt zu werden.

Allein im letzten Jahr wurden zahlreiche schwerwiegende Schwachstellen in der UEFI-Firmware öffentlich bekannt gegeben. Am bemerkenswertesten sind die von den Forschern von Binarly in ihren Blogposts An In-Depth Look At The 23 High-Impact Vulnerabilities und 16 High Impact Vulnerabilities Discovered In HP Devices und von den Forschern von SentinelOne in ihrem Blogpost Another Brick in the Wall: Uncovering SMM Vulnerabilities in HP Firmware.

Unsere Entdeckung zusammen mit den oben genannten zeigt, dass in einigen Fällen die Verteilung von UEFI-Bedrohungen nicht so schwierig ist wie erwartet, und die größere Anzahl der in den letzten Jahren entdeckten realen UEFI-Bedrohungen deutet darauf hin, dass sich die Angreifer dessen bewusst sind.

Im Hinblick auf die in diesem Blogpost beschriebenen Sicherheitslücken raten wir allen Besitzern von Lenovo-Laptops dringend, die Liste der betroffenen Geräte durchzugehen und ihre Firmware zu aktualisieren, am besten anhand der Anweisungen des Herstellers.

Für diejenigen, die End Of Development Support (EODS)-Geräte verwenden, die von CVE-2021-3972 betroffen sind und für die noch keine Updates verfügbar sind: Eine Möglichkeit, sich gegen unerwünschte Änderungen des UEFI Secure Boot-Status zu schützen, ist die Verwendung einer TPM-fähigen Lösung zur vollständigen Festplattenverschlüsselung, die Festplattendaten unzugänglich macht, wenn die UEFI Secure Boot-Konfiguration geändert wird.