Verwenden von Intel Processor Trace zur Verfolgung des Systemverwaltungsmoduscodes


Dieser Artikel befasst sich mit dem Testen der Möglichkeit, mithilfe der Intel Processor Trace-Technologie (Intel PT) Tracks im SMM-Modus (System Management Mode) aufzuzeichnen. Diese Arbeit wurde im Rahmen von Summer Of Hack 2019 durchgeführt . Gepostet von @sysenter_eip .


Die meisten verwendeten Tools wurden von anderen Personen geschrieben (insbesondere @d_olex , @aionescu ). Das Ergebnis ist nur eine Kombination der verfügbaren Tools, um den Codeausführungspfad im SMM-Modus für ein bestimmtes Motherboard abzurufen. Das Material kann jedoch für diejenigen interessant sein, die dies für ihre Plattform wiederholen möchten oder einfach nur an der Arbeit von SMM interessiert sind.


Systemverwaltungsmodus


SMM ist ein spezieller privilegierter Modus des x86-Architekturprozessors, der verfügbar ist, während das Betriebssystem ausgeführt wird, aber für diesen vollständig unsichtbar ist. Es wurde für die Interaktion mit Eisen auf niedriger Ebene, für die Energieverwaltung, die Emulation älterer Geräte, den Übergang in den Ruhemodus (S3), den Zugriff auf TPM und mehr entwickelt. Es funktioniert völlig isoliert vom Betriebssystem. Während der Ausführung von SMM stoppt das Betriebssystem vollständig. Der in diesem Modus ausgeführte Programmcode wird im SPI-Flash-Speicher des Motherboards gespeichert und ist Teil der UEFI-BIOS-Firmware.


Das Umschalten in den SMM-Modus erfolgt über spezielle SMI-Unterbrechungen (System Management Interrupt). Eine der Optionen für diesen Interrupt ist für die Verwendung im Nullring (d. H. Vom Betriebssystemkern) verfügbar - SMI-Interrupt auf Anwendungsebene (Software-SMI). Weiter werden wir uns auf diese Unterbrechungen konzentrieren.


Aufgrund seines hohen Privilegs ist SMM für die Sicherheitsforschung von besonderem Interesse. Die Kompromittierung von SMM führt zu schwerwiegenden Verstößen gegen die Integrität und Vertraulichkeit des gesamten Systems. In den meisten Fällen können Sie bösartigen Code, der nicht gelöscht werden kann und vom Betriebssystem nicht erkannt wird, in die UEFI-BIOS-Firmware einfügen.


Intel-Prozessor-Trace


Eine der Tücken des Debugging-Prozesses für verschiedene stark ausgelastete Anwendungen ist der Overhead - die Kosten für das Debuggen von Tools. Sie können mit einer hardwarebasierten Lösung reduziert werden.


Die fünfte Generation von Prozessoren von Intel (Broadwell) hat der Welt Technologien wie Intel Processor Trace vorgestellt. Wie ist es nützlich? Mit Intel PT können Sie den gesamten Ausführungsfluss (Kontrollfluss) der debuggten Anwendung mit minimalem Overhead (<5%) abrufen. Gleichzeitig unterstützt es Multithreading und kann bei der Behebung von Fehlern wie "Race Condition" aufgrund der Zeitstempel beim Aufzeichnen des Application Trace helfen. Zweifellos bietet die Intel PT-Technologie großartige Möglichkeiten zum Schreiben von Schwachstellensuchwerkzeugen in Anwendungen.


Heutzutage wird diese Technologie in verschiedenen Tools zum Nachverfolgen, Debuggen und Auswerten der Codeabdeckung verwendet - sowohl in Anwendungen auf Benutzer- als auch auf Kernelebene. Beispiele für Tools finden Sie auf der Intel- Website. Eine AFL-Fuzzer-Option, die Intel PT nutzt, ist im PTfuzzer- Repository verfügbar. Achten Sie bei aktuellen Projekten auf iptanalyzer .


Wir haben jedoch noch keine Arbeit über die Verwendung von Intel PT im SMM-Modus gesehen. Da uns in diesem Zusammenhang nichts daran hindert, Intel PT zu verwenden, haben wir uns entschlossen, herauszufinden, ob es möglich ist, Code aus dem Systemverwaltungsmodus damit zu verfolgen.


Vorbereitung auf die Arbeit


Aus dem Intel Developer Manual geht hervor, dass es unmöglich ist, die Intel PT-Ablaufverfolgung in SMM mit normalen Mitteln von außen zu aktivieren. Wenn es zum Zeitpunkt der Auslösung des SMI aktiv war, deaktiviert der Prozessor es, bevor die Steuerung an den SMI-Handler-Einstiegspunkt übertragen wird. Die einzige Aktivierungsmethode ist die freiwillige Einbeziehung des SMI-Handlers durch den Code selbst.


Auch wenn der Prozessor eine solche Gelegenheit zunächst nicht bietet, können wir sie abfangen und Intel PT manuell aktivieren. Sie müssen jedoch irgendwie feststellen, dass das System bereit ist, die Ablaufverfolgung aufzuzeichnen (die Adresse des Ausgabepuffers ist festgelegt), und die Ablaufverfolgung am Ende der Prozessorausführung deaktivieren (Ausführung der RSM-Anweisung). Andernfalls fährt der Prozessor das gesamte System herunter.


Zunächst müssen Sie auf SMRAM zugreifen (den RAM-Bereich, in dem sich der im SMM-Modus ausgeführte Code befindet). Da dieser RAM-Bereich geschützt ist, können wir vom Betriebssystem aus nicht darauf zugreifen (auch dies ist mit DMA nicht möglich). Es gibt verschiedene Szenarien:


  1. eine bekannte Sicherheitslücke in SMM ausnutzen und das R / W-Grundelement daraus holen. Dies kann entweder ein Softwarefehler sein (eine Sicherheitsanfälligkeit im SMI-Prozessor selbst; in der Regel ist in SMM genügend Code vorhanden, der vom OEM hinzugefügt wurde, sodass Sicherheitsanfälligkeiten keine Seltenheit sind), oder eine anfällige Plattformkonfiguration (Entsperren / Verschieben von SMRAM).
  2. das UEFI-Image so zu patchen, dass wir eine Schnittstelle zum Lesen und Schreiben an beliebige Adressen haben - eine Hintertür. Um diese Option zu implementieren, müssen Sie ein Motherboard finden, auf dem Intel Boot Guard deaktiviert ist, oder es gibt Schwachstellen, die dies umgehen können.

Betten Sie Ihren Code in die Firmware ein


Trotz der Tatsache, dass von Zeit zu Zeit SMM-Schwachstellen im Code verschiedener Hersteller gefunden werden , ist es besser, wenn wir uns nicht auf sie verlassen. Für uns ist es interessanter, den Code für neue Firmware zu verfolgen und dementsprechend zu versuchen, Schwachstellen darin zu finden. Wir hatten bereits das GIGABYTE GA-Q270M-D3H-Motherboard mit deaktiviertem Intel Boot Guard. Alles, was wir tun mussten, war, SMM eine Hintertür hinzuzufügen.



Abbildung 1. Prüfstand


Es gibt bereits ein Framework, um SMM zu "infizieren" und mit einer Hintertür zu arbeiten . Es besteht aus drei Komponenten: dem UEFI-Treiber in C, dem "Infector" und dem Client-Skript in Python. Für den Betrieb müssen Sie einen beliebigen DXE-Treiber extrahieren (dies können Sie mit UEFITool tun) und ihn mit einem Infector verarbeiten. Das ursprüngliche Modul wurde durch "Verbessert" ersetzt und die Firmware in den SPI-Speicher hochgeladen (um das Flashen zu vereinfachen, wurde das SPI-Flash-Laufwerk von der Platine entfernt).



Abbildung 2. SPI-Flash-Speicherchip


Das System wurde erfolgreich gestartet, und jetzt haben wir vollen Zugriff auf SMRAM von Python aus (ein Anwendungsbeispiel wird mit der Hintertür bereitgestellt). Da das Client-Skript für die Backdoor auf CHIPSEC basiert, müssen Sie ihm Zugriff auf den Kernel-Modus gewähren (wir haben den RWEverything-Treiber verwendet; es ist praktisch, wenn jemand seinen eigenen CHIPSEC-Treiber mit deaktivierter Signaturüberprüfung im System verwendet).


Sie können die Backdoor überprüfen, indem Sie einen SMRAM-Dump anfordern.


$ python SmmBackdoor.py -d


Nach dem Ausführen dieses Befehls wird die Datei SMRAM_dump_cb000000_cb7fffff.bin erstellt, die den aktuellen SMRAM-Status enthält. Die Werte cb000000 und cb7fffff sind die physikalischen Adressen am Anfang und am Ende des SMRAM.


Arbeiten Sie mit Dump-SMRAM


Der SMRAM-Speicherauszug kann in einen Disassembler geladen oder zur Analyse an das Skript smram_parse.py übergeben werden , das viele nützliche Informationen für uns extrahiert. Am wichtigsten sind für uns die Adressen der SMI-Einstiegspunkte. Dies sind die Adressen der Funktionen, an die die Steuerung übertragen wird, wenn SMI ausgelöst wird. Jede CPU hat einen eigenen Einstiegspunkt.



Abbildung 3. Die Ausgabe des Skripts smram_parse


Schauen wir uns ihren Code an. Da SMM seine Ausführung im 16-Bit-Real-Modus startet (die ersten 4 GB RAM spiegeln sich im virtuellen Raum wider), wechselt der Code zunächst in den 64-Bit-Modus. Gleichzeitig ist der gesamte SMRAM mit Schreib- und Ausführungsrechten verfügbar, da nur ein Segment erstellt wurde (gibt es Anbieter, die dies anders machen?).


Wir möchten keinen 16-Bit-Code schreiben oder alles Notwendige vorbereiten, um in den 64-Bit-Modus zu wechseln. Deshalb platzieren wir unseren Interceptor direkt vor dem Aufruf der SMI-Manager-Funktion (diese Funktion legt fest, von welchem ​​SMM-Modul die Ausführung übertragen werden soll) welcher Dienst wurde angerufen oder welches Ereignis geschah).



Abbildung 4. Platzierung zum Einhängen


Die einfachste Möglichkeit, die Kontrolle zu übernehmen, besteht darin, die Absenderadresse durch unsere zu ersetzen. Alle Einstiegspunkte haben den gleichen Code, daher muss der Patch für jeden wiederholt werden.


Hinweis: Bezüglich der Position des Interceptor-Codes. Da uns die Struktur des SMRAM nicht vollständig bekannt ist, haben wir einen zufälligen Teil des Nullspeichers in der Nähe eines der Eintrittspunkte ausgewählt, an dem wir den Interceptor-Code platziert haben. Die beste Option wäre, Ihr SMM-Modul der Firmware hinzuzufügen, die UEFI legal in SMRAM ablegen würde, um nicht zu befürchten, dass etwas Wichtiges mit unserem Code überschrieben wird.


Implementierung eines SMI Manager Interceptors


Lassen Sie uns bestimmen, was genau wir in unserem Abfangjäger tun werden. Zuerst müssen wir feststellen, ob Intel PT eingeschaltet war, bevor wir zu SMM wechseln. Aus der Intel-Dokumentation ist bekannt, dass jeder Prozessor zum Zeitpunkt des Übergangs zu SMM über eine eigene SMBASE-Basis (MSR 0x9E) und einen eigenen Speicherbereich für den Prozessorstatus (SMM-Speicherstatusbereich) verfügt.



Abbildung 5. SMBASE-Layout


Wir ermitteln den Status von Intel PT


Im SMM Save State muss der Wert des MSR-Registers IA32_RTIT_CTL gespeichert werden, das für die Verwaltung der Intel PT-Ablaufverfolgung zuständig ist. Leider gibt Intel Manual nicht an, wo der Prozessor den Status des IA32_RTIT_CTL.TraceEn-Bits zum Zeitpunkt des Übergangs zu SMM speichert (ob die Ablaufverfolgung aktiviert ist, Null-Bit). Sie können dies jedoch selbst feststellen, indem Sie den SMM-Speicherstatus zweimal sichern: mit und ohne aktivierter Ablaufverfolgung.


Wir haben das WinIPT- Tool verwendet, um die Ablaufverfolgung für den Python-Interpreterprozess zu aktivieren (pid 1337 ), dem Ablaufverfolgungspuffer 2 ^ 12 (4096) Bytes zuzuweisen und dann das Skript SmmBackdoor.py im Interpreter auszuführen (Argument 0 ist ein Flag, für uns ist dies nicht der Fall) wichtig, weil Sie in SMM noch Ihre Trace-Einstellungen erzwingen müssen).


$ ipttool.exe --start 1337 12 0


Durch Vergleichen der SMRAM-Snapshots haben wir die Position des IA32_RTIT_CTL-Registers in der SMM-Sicherungsstatusstruktur ermittelt. Es wird am Offset SMBASE + 0xFE3C gespeichert. Der Status des IA32_RTIT_CTL.TraceEn-Bits ist die Hauptbedingung für die Intel PT-Reaktivierung in SMM. Das Feld mit diesem Versatz ist im Intel Developer Manual als Reserviert markiert.



Abbildung 6. Markieren, dass Felder reserviert sind


Shellcode schreiben


Wir wollten Intel PT nicht in SMM selbst konfigurieren, da dies unseren Shellcode komplizieren würde (zum Beispiel wäre es in SMM schwierig, einen großen Teil des Arbeitsspeichers zuzuweisen, damit er nicht vom Betriebssystem selbst verwendet wird). Aus diesem Grund haben wir uns entschlossen, den bereits konfigurierten Tracer zu verwenden und ihn einfach in SMM zu "überspringen", zumal er bereits die Funktion hat, den Trace in einer Datei zu speichern.


Da wir zu diesem Zweck WinIPT verwendeten, das zu diesem Zeitpunkt das Tracen des Kernel-Codes (CPL == 0) nicht unterstützte, war es offensichtlich, dass selbst wenn der Trace in SMM enthalten war, nichts im Protokoll angezeigt wurde, da der SMM-Code mit CPL = 0 ausgeführt wurde . Wir müssen einige Filter modifizieren, damit der Tracer während der gesamten in SMM verbrachten Zeit arbeiten kann. Wir listen alles auf, was überprüft und installiert werden muss:


  1. Das Tracing mit CPL = 0 muss aktiviert sein.
  2. Die Ablaufverfolgung für CPL> 0 muss aktiviert sein (optional).
  3. Die gültigen IP-Bereiche für die Aufzeichnung von Ereignissen müssen deaktiviert sein.
  4. IA32_RTIT_STATUS.PacketByteCnt muss zurückgesetzt werden.
  5. Die CR3-Filterung muss deaktiviert sein.

Ein paar Worte sollten über PacketByteCnt gesagt werden. Dieser Zähler bestimmt, an welchem ​​Punkt Sie Synchronisationspakete (eine Folge von mehreren PSB-Befehlen) in den Trace einfügen müssen. Wir müssen diesen Zähler zurücksetzen, da sonst während der Verarbeitung der Ablaufverfolgung der Moment des Eintritts in den SMM verpasst wird und die Ablaufverfolgung an einer zufälligen Stelle beginnt, wenn der PSB auf natürliche Weise generiert wird.


Unten ist der von uns verwendete Shellcode:


  sub rsp, 0x18 ; this will align stack at 16 byte boundary (in case SMM ; code uses align dependent instructions) mov qword ptr ss:[rsp+0x10], rcx ; need to save rcx for SMI_Dispatcher mov ecx, 0x9E ; MSR_IA32_SMBASE rdmsr test byte ptr ds:[rax+0xFE3C], 0x1 ; Save State area contains saved ; IA32_RTIT_CTL.TraceEn je short @NoTrace call @Trace_Enable mov rcx, qword ptr ss:[rsp+0x10] ; SMI_Dispatcher is __fastcall ; (first argument in rcx) mov eax, 0xCB7DDAA4 ; original SMI_Dispatcher !!!!!!!!!!!!!!!!!!!!! call rax call @Trace_Disable add rsp, 0x18 ret @NoTrace: mov rcx, qword ptr ss:[rsp+0x10] ; SMI_Dispatcher is __fastcall mov eax, 0xCB7DDAA4 ; original SMI_Dispatcher !!!!!!!!!!!!!!!!!!!!! call rax add rsp, 0x18 ret @Trace_Disable: mov ecx, 0x570 ; IA32_RTIT_CTL rdmsr mov rax, qword ptr ss:[rsp+0x10] ; restore IA32_RTIT_STATUS wrmsr mov ecx, 0x571 ; IA32_RTIT_STATUS rdmsr mov rax, qword ptr ss:[rsp+0x8] ; restore IA32_RTIT_CTL wrmsr ret @Trace_Enable: mov ecx, 0x571 ; IA32_RTIT_STATUS rdmsr mov qword ptr ss:[rsp+0x8], rax ; save IA32_RTIT_STATUS and edx, 0xFFFF0000 ; IA32_RTIT_STATUS.PacketByteCnt = 0 wrmsr mov ecx, 0x570 ; IA32_RTIT_CTL rdmsr mov qword ptr ss:[rsp+0x10], rax ; save IA32_RTIT_CTL and eax, 0xFFFFFFBF ; IA32_RTIT_CTL.CR3Filter = 0 or eax, 0x5 ; IA32_RTIT_CTL.OS = 1; IA32_RTIT_CTL.User = 1; and edx, 0xFFFF0000 ; IA32_RTIT_CTL.ADDRx_CFG = 0 wrmsr ret 

Dieser Code muss in SMRAM abgelegt werden, und der Übergang zum SMI-Manager muss gepatcht werden, um zu unserem Code zu gelangen. All dies geschieht mit SmmBackdoor.


Arbeite mit der Strecke


Mit dem SMI Manager Interceptor konnten wir den ersten Code-Trace von SMM schreiben. Der folgende Befehl kann WinIPT auffordern , den Trace in einer Datei zu speichern:


$ ipttool.exe --trace 1337 trace_file_name


Deaktivieren der Ablaufverfolgung für einen Prozess:


$ ipttool.exe --stop 1337


Sie können versuchen, den Trace mit dem Dienstprogramm dumppt aus libipt zu disassemblieren .


$ ptdump.exe --no-pad ./examples/trace_smm_handler_33 > ./examples/trace_smm_handler_33_pt_dump.txt


Ausgabebeispiel:



Abbildung 7. Der erste SMM-Befehlspfad


Wir können einige Adressen sehen, es ist jedoch äußerst schwierig, diese Informationen zu verwenden, da sie sehr niedrig sind.


Um ein besser lesbares Aussehen zu erhalten, gibt es ein ptxed- Dienstprogramm (von libipt), das den Trace in ein Protokoll der ausgeführten Assembler-Anweisungen konvertiert. Natürlich müssen wir dem Dienstprogramm einen SMRAM-Speicherauszug bereitstellen, da das IPT-Protokoll keine Informationen über die Werte von Speicherzellen oder welche Anweisungen ausgeführt wurden enthält. Es enthält nur Informationen darüber, welche Änderungen im Kontrollfluss aufgetreten sind.


$ ptxed.exe --pt tracesmm_12 --raw SMRAM_dump_cb000000_cb7fffff.bin:0xcb000000 > tracesmm_12_ptasm



Abbildung 8. Assembler-Liste entsprechend dem IPT-Protokoll


Es sieht schon viel besser aus, aber wenn der Code eine Schleife enthält, wird die Ausgabe mit den gleichen Anweisungen verstopft.


Definieren Sie die Codeabdeckung mithilfe der Ablaufverfolgung


Für die Visualisierung der Berichterstattung haben wir das Lighthouse- Plugin für IDA Pro ausgewählt, das das DRCOV-Format verwendet.


Es wurden keine vorgefertigten Tools gefunden. Daher haben wir ptxed so geändert, dass dabei auch eine Coverage-Datei generiert wird. Gepatchtes ptxed ist im Repository verfügbar. Sehen Sie sich den Commit-Verlauf an, um festzustellen, was genau hinzugefügt wurde.


Nach Abschluss von ptxed wird die Datei SMRAM_dump_cb000000_cb7fffff.bin.log angezeigt, die Abdeckungsinformationen im DRCOV-Format enthält.


Hinweis: Bei der Disassembler-Synchronisation auf dem ersten PSB ist ein kleines Problem aufgetreten. Wenn der PSB vor PGE generiert wird (der Zähler wird auf Null zurückgesetzt, bevor der Trace erneut aktiviert wird), kann ptxed aus einem nicht ganz eindeutigen Grund nicht darauf synchronisiert werden. Um dieses Problem zu umgehen, haben wir einen kleinen Patch erstellt. Es ist nicht klar, ob dies ein Problem für ptxed selbst ist oder ob wir etwas falsch machen, indem wir IA32_RTIT_STATUS.PacketByteCnt zurücksetzen.



Abbildung 9. Ein Patch, mit dem Sie das PSB direkt vor dem PGE verwenden können


Generierte Coverage-Dateien können in IDA Pro heruntergeladen werden und erhalten schöne Hervorhebungen sowie Statistiken zur prozentualen Abdeckung für jede Funktion.



Abbildung 10. IDA Pro Lighthouse-Plugin mit Informationen zur Codeabdeckung


Hinweis: Das Lighthouse-Plugin funktioniert bei unvollständig analysierten Datenbanken etwas seltsam (ausführbarer Code ist nicht beschriftet, Funktionen wurden nicht erstellt). Wir haben dieses "Problem" auf die Funktion get_instructions_slice in der Datei \ lighthouse \ metadata.py zurückgeführt, in der auch für die Adresse, an der die Funktion manuell erstellt wurde, 0 Anweisungen zurückgegeben werden. Das Plugin scheint den Cache zu benutzen und den neuen spezifischen Code zu ignorieren. Dies kann umgangen werden, indem Sie Reanalyze im Programm aufrufen und IDB erneut öffnen. Erst danach kann das Plugin den neuen Code sehen und ihn berücksichtigen. Da dieses Problem bei einem SMRAM-Speicherauszug (der beim ersten Start fast ausschließlich aus undefiniertem Code besteht) sehr unpraktisch ist, haben wir eine kleine Änderung am Lighthouse-Code vorgenommen, damit wir neuen Code manuell schneller definieren können.



Abbildung 11. Es wurde eine Protokollmeldung hinzugefügt, um den neuen Code zu identifizieren


Linux-Unterstützung


Da alle unsere Tests unter Windows 10 x 64 durchgeführt wurden (wir benötigten ipt.sys, das im Windows October Creators Update 2018 veröffentlicht wurde), lassen Sie uns ein paar Worte über die Möglichkeit sagen, dies unter Linux zu implementieren.


  • Es gibt ein Linux-Kernel- Perf- Modul, das dieselben WinIPT-Aktionen (ipt.sys) ausführen kann, einschließlich der Möglichkeit, Code im Kernel-Modus zu verfolgen.
  • Da die Backdoor-SMM-Oberfläche auf dem plattformübergreifenden CHIPSEC-Framework basiert, funktioniert unser Patch auf einem Linux-System ohne Änderungen.

Fazit


Wir haben die Aufgabe, einen in SMM ausgeführten Code-Trace mithilfe der Intel Processor Trace-Technologie zu erhalten, erfolgreich bewältigt. Ein ähnliches Ergebnis könnte mit Hilfe teurer Geräte und Software erzielt werden, die nicht an alle verkauft werden. Es hat uns gereicht, ein Motherboard und einen SPI-Programmierer zur Hand zu haben. Die Geschwindigkeit der Entfernung der Spur ist wirklich beeindruckend, und es gibt keine Beschwerden über die Genauigkeit des Ergebnisses.


Wir hoffen, dass dieser Artikel anderen hilft, die Intel PT-Technologie zu nutzen, um Schwachstellen im SMM-Code zu untersuchen und zu suchen. Die Anpassung unserer Arbeit an andere Motherboards sollte keine Schwierigkeiten verursachen (Intel Boot Guard nicht vergessen). Die Hauptsache ist, vollständig zu verstehen, wie es funktioniert. Am schwierigsten ist es zu bestimmen, wie der SMI-Dispatcher abgefangen und ein Shellcode für den Interceptor geschrieben werden soll. In unserer Version wurden "verkabelte" Adressen verwendet, daher sollten Sie den Shellcode sorgfältig auf ein anderes System übertragen.


Alle verwendeten Tools und Skripte sind im Repository von GitHub verfügbar.

Source: https://habr.com/ru/post/de481692/


All Articles