Mehrmals im Jahr haben die Forscher der Elastic Security Labs die Freiheit, Projekte nach ihren Wünschen auszuwählen und zu vertiefen – entweder allein oder im Team. Diese Zeit wird intern als "On-Week"-Projekte bezeichnet. Dies ist der erste Teil einer Reihe, die sich auf die von Microsoft entwickelte Time Travel Debugging (TTD)-Technologie konzentriert, die kürzlich in einer On-Week-Sitzung ausführlich untersucht wurde.
Obwohl TTD und sein Potenzial bereits seit mehreren Jahren öffentlich gemacht werden, wird das Bewusstsein für TTD und sein Potenzial in der Infosec-Community stark unterschätzt. Wir hoffen, dass diese zweiteilige Serie dazu beitragen kann, etwas Licht ins Dunkel zu bringen, wie TTD für das Debuggen von Programmen, die Erforschung und Ausnutzung von Schwachstellen sowie die Analyse von Malware nützlich sein kann.
Diese Forschung beinhaltete zunächst das Verständnis der inneren Funktionsweise von TTD und dann die Bewertung einiger interessanter anwendbarer Anwendungen, die daraus gemacht werden können. Dieser Beitrag konzentriert sich darauf, wie Forscher tief in TTD eintauchen und ihre Methodik zusammen mit einigen interessanten Ergebnissen teilen. Im zweiten Teil wird die anwendbare Verwendung von TTD zum Zwecke der Malware-Analyse und der Integration mit Elastic Security detailliert beschrieben.
Hintergrund
Time Travel Debugging ist ein von Microsoft Research entwickeltes Tool, mit dem Benutzer die Ausführung aufzeichnen und frei in die Benutzermoduslaufzeit einer Binärdatei navigieren können. TTD selbst stützt sich auf zwei Technologien: Nirvana für die binäre Übersetzung und iDNA für den Lese-/Schreibprozess von Spuren. Die TTD-Interna sind seit Windows 7 verfügbar und wurden erstmals in einem öffentlich zugänglichen Dokument ausführlich beschrieben. Seitdem haben sowohl Microsoft als auch unabhängige Forscher sehr detailliert darüber berichtet. Aus diesem Grund werden wir die Interna beider Technologien nicht näher untersuchen. Stattdessen untersuchten die Elastic-Forscher das Ökosystem – oder die ausführbaren Dateien, DLLs und Treiber –, die dafür sorgen, dass die TTD-Implementierung funktioniert. Dies führte zu einigen interessanten Erkenntnissen über TTD, aber auch über Windows selbst, da TTD einige (undokumentierte) Techniken nutzt, um in speziellen Fällen, wie z.B. geschützten Prozessen, wie vorgesehen zu funktionieren.
Aber warum überhaupt gegen TTD ermitteln? Abgesehen von der reinen Neugier ist es wahrscheinlich, dass eine der möglichen Einsatzmöglichkeiten der Technologie darin besteht, Fehler in Produktionsumgebungen zu entdecken. Wenn Fehler schwer auszulösen oder zu reproduzieren sind, hilft eine Umgebung vom Typ "Einmal aufzeichnen, immer wiedergeben", diese Schwierigkeit zu kompensieren, was genau das ist, was TTD in Verbindung mit WinDbg implementiert.
Debugtools wie WinDbg waren schon immer eine immense Informationsquelle beim Umkehren von Windows-Komponenten, da sie zusätzliche verständliche Informationen liefern, in der Regel im Klartext. Debugtools (insbesondere Debugger) müssen mit dem zugrunde liegenden Betriebssystem zusammenarbeiten, was das Debuggen von Schnittstellen und/oder zuvor nicht offengelegten Funktionen des Betriebssystems umfassen kann. TTD entspricht diesem Muster.
Überblick auf hoher Ebene
TTD erstellt zunächst eine Aufzeichnung, die jede von einer Anwendung ausgeführte Anweisung verfolgt und in einer Datenbank (mit dem Suffix .run) speichert. Aufgezeichnete Ablaufverfolgungen können nach Belieben mit dem WinDbg-Debugger wiedergegeben werden, der beim ersten Zugriff die .run indiziert -Datei, die eine schnellere Navigation durch die Datenbank ermöglicht. Um die Ausführung beliebiger Prozesse verfolgen zu können, injiziert TTD eine DLL, die für die Aufzeichnung von Aktivitäten bei Bedarf verantwortlich ist, was es ihm ermöglicht, Prozesse durch Erzeugen aufzuzeichnen, aber auch an einen bereits laufenden Prozess anzuhängen.
TTD kann als Teil des WinDbg Preview-Pakets im MS Store kostenlos heruntergeladen werden . Es kann direkt aus WinDbg Preview (auch bekannt als WinDbgX) verwendet werden, ist aber eine eigenständige Komponente, die sich in C:\Program Files\WindowsApps\Microsoft.WinDbg_<version></version>_<arch>__8wekyb3d8bbwe\amd64\ttd
für die x64-Architektur befindet, auf die wir uns in diesem Beitrag konzentrieren werden. x86- und arm64-Versionen stehen auch im MS Store zum Download zur Verfügung.
Das Paket besteht aus zwei EXE-Dateien (TTD.exe und TTDInject.exe) und eine Handvoll DLLs. Diese Forschung konzentriert sich auf die Haupt-DLL, die für alles verantwortlich ist, was nicht mit Nirvana/iDNA zu tun hat (d.h. verantwortlich für das Sitzungsmanagement, die Treiberkommunikation, die DLL-Injektion und mehr): ttdrecord.dll
_Note: Der größte Teil dieser Recherchen wurde mit zwei Versionen der ttdrecord-DLL durchgeführt: hauptsächlich mit einer 2018 Version (1.9.106.0 SHA256=aca1786a1f9c96bbe1ea9cef0810c4d164abbf2c80c9ecaf0a1ab91600da6630) und frühe 2022 Version (10.0.19041.1 SHA256=1FF7F54A4C865E4FBD63057D5127A73DA30248C1FF28B99FF1A43238071CBB5C). Es wurde festgestellt, dass die älteren Versionen mehr Symbole enthielten, was dazu beitrug, den Reverse-Engineering-Prozess zu beschleunigen. Anschließend haben wir Strukturen und Funktionsnamen wieder an die aktuellste Version angepasst. Daher sind einige der hier erläuterten Strukturen möglicherweise nicht identisch, wenn Sie versuchen, sie in neueren Versionen zu reproduzieren. _
Untersuchen der TTD-Funktionen
Parameter der Befehlszeile
Die Leser sollten beachten, dass TTD.exe im Wesentlichen als Wrapper für ttdrecord fungiert! ExecuteTTTracerCommandLine:
HRESULT wmain()
{
v28 = 0xFFFFFFFFFFFFFFFEui64;
hRes = CoInitializeEx(0i64, 0);
if ( hRes >= 0 )
{
ModuleHandleW = GetModuleHandleW(L"TTDRecord.dll");
[...]
TTD::DiagnosticsSink::DiagnosticsSink(DiagnosticsSink, &v22);
CommandLineW = GetCommandLineW();
lpDiagnosticsSink = Microsoft::WRL::Details::Make<TTD::CppToComDiagnosticsSink,TTD::DiagnosticsSink>(&v31, DiagnosticsSink);
hRes = ExecuteTTTracerCommandLine(*lpDiagnosticsSink, CommandLineW, 2i64);
[...]
Die letzte Zeile des obigen Codeauszugs zeigt einen Aufruf von ExecuteTTTracerCommandLine , der eine ganze Zahl als letztes Argument annimmt. Dieses Argument entspricht den gewünschten Ablaufverfolgungsmodi: - 0 -> FullTracingMode, - 1 -> UnrestrictedTracing und - 2 -> Standalone (der hartcodierte Modus für die öffentliche Version von TTD.exe)
Wenn Sie TTD zwingen, im vollständigen Ablaufverfolgungsmodus ausgeführt zu werden, werden verfügbare Optionen angezeigt, die einige versteckte Funktionen wie Process Reparenting (-parent) und automatische Ablaufverfolgung bis zum Neustart (-onLaunch) für Programme und Dienste enthalten.
Das Dumpen des kompletten Optionssatzes von TTDRecord.dll interessante versteckte Befehlszeilenoptionen aufgedeckt, wie zum Beispiel:
-persistent Trace programs or services each time they are started (forever). You must specify a full path to the output location with -out.
-delete Stop future tracing of a program previously specified with -onLaunch or -persistent. Does not stop current tracing. For -plm apps you can only specify the package (-delete <package>) and all apps within that package will be removed from future tracing
-initialize Manually initialize your system for tracing. You can trace without administrator privileges after the system is initialized.
Der Prozess der Einrichtung von Nirvana erfordert, dass TTD das Feld InstrumentationCallback im Ziel-_EPROCESS einrichtet. Dies wird durch den (nicht dokumentierten, aber bekannten) NtSetInformationProcess(ProcessInstrumentationCallback) syscall (ProcessInstrumentationCallback) erreicht, der den Wert 40 hat. Aufgrund der potenziellen Auswirkungen auf die Sicherheit sind für das Aufrufen dieses Systemaufrufs erhöhte Berechtigungen erforderlich. Interessanterweise deutete das -initialize-Flag auch darauf hin, dass TTD als Windows-Dienst bereitgestellt werden könnte. Ein solcher Dienst wäre für das Proxying von Ablaufverfolgungsanforderungen an beliebige Prozesse verantwortlich. Dies kann bestätigt werden, indem Sie es ausführen und die resultierende Fehlermeldung sehen:
Obwohl es leicht ist, Beweise für die Existenz von TTDService.exe zu finden, wurde die Datei nicht als Teil des öffentlichen Pakets bereitgestellt, so dass wir, abgesehen von dem Hinweis, dass TTD als Dienst ausgeführt werden kann, in diesem Beitrag nicht darauf eingehen werden.
TTD-Prozess-Injektion
Wie erläutert, kann eine TTD-Ablaufverfolgungsdatei entweder aus dem eigenständigen binären TTD.exe oder über einen Dienst TTDService.exe (privat) erstellt werden, die beide in einem privilegierten Kontext ausgeführt werden müssen. Dies sind jedoch nur Launcher und das Einfügen der Aufzeichnungs-DLL (mit dem Namen TTDRecordCPU.dll) ist die Aufgabe eines anderen Prozesses: TTDInject.exe.
TTDInject.exe ist eine weitere ausführbare Datei, die deutlich größer als TTD.exe ist, aber ein ziemlich einfaches Ziel hat: die Ablaufverfolgungssitzung vorzubereiten. In einer stark vereinfachten Ansicht startet TTD.exe den aufzuzeichnenden Prozess zunächst in einem angehaltenen Zustand. Er spawnt dann TTDInject.exe, Übergeben Sie ihm alle notwendigen Argumente, um die Sitzung vorzubereiten. Beachten Sie, dass TTDInject den Prozess auch direkt erzeugen kann, abhängig von dem zuvor erwähnten Ablaufverfolgungsmodus – daher beschreiben wir das häufigste Verhalten (d. h. wenn es von TTD.exe erzeugt wird).
TTDInject erstellt einen Thread zum Ausführen von TTDLoader! InjectThread im aufgezeichneten Prozess, der nach verschiedenen Validierungen wiederum die Bibliothek lädt, die für die Aufzeichnung aller Prozessaktivitäten verantwortlich ist, TTDRecordCPU.dll.
Von diesem Zeitpunkt an werden alle Anweisungen, Speicherzugriffe, ausgelösten Ausnahmen oder CPU-Zustände, die während der Ausführung auftreten, aufgezeichnet.
Nachdem man den allgemeinen Arbeitsablauf von TTD verstanden hatte, wurde klar, dass nach der Session-Initialisierung wenig bis gar keine Manipulationen möglich sind. Daher wurde den von ttdrecord.dll vertretenen Argumenten weitere Aufmerksamkeit geschenkt. Dank des C++-Mangling-Funktionsformats können viele wichtige Informationen aus den Funktionsnamen selbst abgerufen werden, was die Analyse des Befehlszeilenargument-Parsers relativ einfach macht. Ein interessantes Flag, das entdeckt wurde, war PplDebuggingToken. Dieses Flag ist ausgeblendet und nur im uneingeschränkten Modus verfügbar.
Die Existenz dieses Flags warf sofort Fragen auf: TTD wurde zuerst um Windows 7 und 8 herum und auf Windows 8.1+ entwickelt. Das Konzept der Schutzebene wurde Prozessen hinzugefügt, das vorschreibt, dass Prozesse Handles nur für einen Prozess mit einer Schutzebene öffnen können, die gleich oder niedriger ist. Es ist ein einfaches Byte in der _EPROCESS Struktur im Kernel und daher nicht direkt aus dem Benutzermodus änderbar.
Die Werte des Bytes für die Schutzebene sind gut bekannt und in der folgenden Tabelle zusammengefasst.
Das Subsystem der lokalen Sicherheitsautorität (lsass.exe) unter Windows kann so konfiguriert werden , dass es als Protected Process Light ausgeführt wird, was darauf abzielt, die Reichweite eines Eindringlings zu begrenzen, der maximale Privilegien auf einem Host erlangt. Durch das Handeln auf Kernel-Ebene kann kein Prozess im Benutzermodus ein Handle für lsass öffnen, unabhängig davon, wie privilegiert es ist.
Das PplDebuggingToken-Flag scheint jedoch etwas anderes zu vermuten. Wenn es eine solche Flagge gäbe, wäre es der Traum eines jeden Pentesters/Red Teamers: ein (magischer) Token, der es ihnen ermöglichen würde, in geschützte Prozesse einzuschleusen und sie aufzuzeichnen, ihren Speicher zu leeren oder vieles mehr. Der Befehlszeilen-Parser scheint zu implizieren, dass der Inhalt des Befehlsflags nur eine breite Zeichenkette ist. Könnte das eine PPL-Hintertür sein?
Jagd nach dem PPL-Debugtoken
Rückkehr nach ttdrecord.dll, Die Befehlszeilenoption PplDebuggingToken wird analysiert und in einer Kontextstruktur zusammen mit allen Optionen gespeichert, die zum Erstellen der TTD-Sitzung erforderlich sind. Der Wert kann auf mehrere Stellen zurückgeführt werden, wobei ein interessanter Ort in TTD::InitializeForAttach liegt, dessen Verhalten im folgenden Pseudocode vereinfacht wird:
ErrorCode TTD::InitializeForAttach(TtdSession *ctx)
{
[...]
EnableDebugPrivilege(GetCurrentProcess()); // [1]
HANDLE hProcess = OpenProcess(0x101040u, 0, ctx->dwProcessId);
if(hProcess == INVALID_HANDLE_VALUE)
{
goto Exit;
}
[...]
HMODULE ModuleHandleW = GetModuleHandleW(L"crypt32.dll");
if ( ModuleHandleW )
pfnCryptStringToBinaryW = GetProcAddress(ModuleHandleW, "CryptStringToBinaryW"); // [2]
if ( ctx->ProcessDebugInformationLength ) // [3]
{
DecodedProcessInformationLength = ctx->ProcessDebugInformationLength;
DecodedProcessInformation = std::vector<unsigned char>(DecodedProcessInformationLength);
wchar_t* b64PplDebuggingTokenArg = ctx->CmdLine_PplDebugToken;
if ( *pfnCryptStringToBinaryW )
{
if( ERROR_SUCCESS == pfnCryptStringToBinaryW( // [4]
b64PplDebuggingTokenArg,
DecodedProcessInformationLength,
CRYPT_STRING_BASE64,
DecodedProcessInformation.get(),
&DecodedProcessInformationLength,
0, 0))
{
Status = NtSetInformationProcess( // [5]
NtGetCurrentProcess(),
ProcessDebugAuthInformation,
DecodedProcessInformation.get(),
DecodedProcessInformationLength);
}
[...]
Nach dem Aktivieren des SeDebugPrivilege-Flags für den aktuellen Prozess ([1]) und dem Abrufen eines Handles für den Prozess, an den angehängt werden soll ([2]), löst die Funktion eine exportierte generische Funktion auf, die zum Ausführen von Zeichenfolgenoperationen verwendet wird: crypt32! CryptStringToBinaryW. In diesem Fall wird es zum Decodieren des base64-codierten Werts der PplDebuggingToken-Kontextoption verwendet, wenn er von der Befehlszeile ( [3], [4]) bereitgestellt wurde. Der decodierte Wert wird dann verwendet, um den Systemaufruf NtSetInformationProcess(ProcessDebugAuthInformation) ([5]) aufzurufen. Das Token scheint nirgendwo anders verwendet zu werden, was uns dazu veranlasste, diesen Systemaufruf zu überprüfen.
Die Prozessinformationsklasse ProcessDebugAuthInformation wurde in RS4 hinzugefügt. Ein kurzer Blick auf ntoskrnl zeigt, dass dieser Systemaufruf den Puffer einfach an CiSetInformationProcess in ci.dll übergibt, bei dem es sich um die Codeintegritätstreiber-DLL handelt. Der Puffer wird dann mit vollständig kontrollierten Argumenten an ci!CiSetDebugAuthInformation übergeben.
Das folgende Diagramm fasst auf einer hohen Ebene zusammen, wo dies im Ausführungsablauf von TTD geschieht.
Der Ausführungsablauf in CiSetDebugAuthInformation ist einfach genug: Der Puffer mit dem base64-dekodierten PplDebuggingToken und seine Länge werden als Argumente für das Parsen und Validieren an ci!SbValidateAndParseDebugAuthToken übergeben. Sollte die Validierung erfolgreich sein und nach einer zusätzlichen Validierung wird ein Handle für den Prozess erstellt, der den Systemaufruf ausführt (denken Sie daran, dass wir immer noch den Systemaufruf nt! NtSetInformationProcess) wird in ein Prozessdebuginformationsobjekt eingefügt und dann in einem globalen Listeneintrag gespeichert.
Aber wie ist das interessant? Da auf diese Liste nur an einem einzigen Speicherort zugegriffen wird: in ci!CiCheckProcessDebugAccessPolicy, und diese Funktion wird während eines NtOpenProcess-Systemaufrufs erreicht. Und, wie der Name des neu entdeckten Flags bereits vermuten lässt, würde jeder Prozess, dessen PID sich in dieser Liste befindet, die Erzwingung der Schutzebene umgehen. Dies wurde praktisch in einer KD-Sitzung bestätigt, indem ein Zugriffshaltepunkt auf dieser Liste gesetzt wurde (in unserer Version von ci.dll befand sich dieser unter ci+364d8). Wir haben auch PPL auf LSASS aktiviert und ein einfaches PowerShell-Skript geschrieben, das einen NtOpenProcess-Systemaufruf auslöst:
Indem man den Ruf nach nt! PsTestProtectedProcessInkompatibilität in nt! PspProcessOpen können wir bestätigen, dass unser PowerShell-Prozess versucht, lsass.exe was ein PPL-Prozess ist:
Um nun die ursprüngliche Theorie zu bestätigen, was das PplDebuggingToken-Argument tun würde, indem der Rückgabewert des Aufrufs an nt! PsTestProtectedProcessInkompatibilität:
Wir brechen bei der Anweisung ab, die dem Ruf nach nt folgt! PsTestProtectedProcessIncompatibility (das nur CI!CiCheckProcessDebugAccessPolicy aufruft) und erzwingen Sie den Rückgabewert auf 0 (wie bereits erwähnt, bedeutet ein Wert von 1 inkompatibel):
Erfolg! Wir erhielten einen Handle für LSASS, obwohl es PPL war, was unsere Theorie bestätigte. Zusammenfassend lässt sich sagen, dass, wenn wir einen "gültigen Wert" finden können (wir werden uns bald damit befassen), er die Prüfung von SbValidateAndParseDebugAuthToken() in ci!CiSetDebugAuthInformation() bestehen wird, und wir hätten einen universellen PPL-Bypass. Wenn das zu schön klingt, um wahr zu sein, dann liegt das vor allem daran, dass es so ist – aber um es zu bestätigen, muss man ein besseres Verständnis dafür entwickeln, was CI.dll tut.
Grundlegendes zu Codeintegritätsrichtlinien
Einschränkungen, die auf der Codeintegrität basieren, z. B. die von AppLocker verwendeten, können durch Richtlinien erzwungen werden, bei denen es sich in ihrer lesbaren Form um XML-Dateien handelt. Es gibt zwei Arten von Policen: Basis- und Zusatzpolicen. Beispiele dafür, wie Basisrichtlinien aussehen, finden Sie in ihrem XML-Format unter "C:\Windows\schemas\CodeIntegrity\ExamplePolicies". So sieht eine Basisrichtlinie in ihrer XML-Form aus (entnommen aus "C:\Windows\schemas\CodeIntegrity\ExamplePolicies\AllowAll.xml"), die die meisten Details, an denen wir interessiert sind, deutlich im Klartext anzeigt.
<?xml version="1.0" encoding="utf-8"?>
<SiPolicy xmlns="urn:schemas-microsoft-com:sipolicy">
<VersionEx>1.0.1.0</VersionEx>
<PolicyID>{A244370E-44C9-4C06-B551-F6016E563076}</PolicyID>
<BasePolicyID>{A244370E-44C9-4C06-B551-F6016E563076}</BasePolicyID>
<PlatformID>{2E07F7E4-194C-4D20-B7C9-6F44A6C5A234}</PlatformID>
<Rules>
<Rule><Option>Enabled:Unsigned System Integrity Policy</Option></Rule>
<Rule><Option>Enabled:Advanced Boot Options Menu</Option></Rule>
<Rule><Option>Enabled:UMCI</Option></Rule>
<Rule><Option>Enabled:Update Policy No Reboot</Option></Rule>
</Rules>
<!--EKUS-- >
<EKUs />
<!--File Rules-- >
<FileRules>
<Allow ID="ID_ALLOW_A_1" FileName="*" />
<Allow ID="ID_ALLOW_A_2" FileName="*" />
</FileRules>
<!--Signers-- >
<Signers />
<!--Driver Signing Scenarios-- >
<SigningScenarios>
<SigningScenario Value="131" ID="ID_SIGNINGSCENARIO_DRIVERS_1" FriendlyName="Auto generated policy on 08-17-2015">
<ProductSigners>
<FileRulesRef><FileRuleRef RuleID="ID_ALLOW_A_1" /></FileRulesRef>
</ProductSigners>
</SigningScenario>
<SigningScenario Value="12" ID="ID_SIGNINGSCENARIO_WINDOWS" FriendlyName="Auto generated policy on 08-17-2015">
<ProductSigners>
<FileRulesRef><FileRuleRef RuleID="ID_ALLOW_A_2" /></FileRulesRef>
</ProductSigners>
</SigningScenario>
</SigningScenarios>
<UpdatePolicySigners />
<CiSigners />
<HvciOptions>0</HvciOptions>
<Settings>
<Setting Provider="PolicyInfo" Key="Information" ValueName="Name">
<Value><String>AllowAll</String></Value>
</Setting>
<Setting Provider="PolicyInfo" Key="Information" ValueName="Id">
<Value><String>041417</String></Value>
</Setting>
</Settings>
</SiPolicy>
XML-formatierte Richtlinien können mit dem PowerShell-Cmdlet ConvertFrom-CiPolicy in ein Binärformat kompiliert werden:
Basisrichtlinien ermöglichen eine feine Granularität mit der Möglichkeit, nach Name, Pfad, Hash oder Unterzeichner (mit oder ohne spezifische EKU) einzuschränken. sondern auch in ihrem Aktionsmodus (Audit oder Enforced).
Ergänzende Richtlinien wurden als Erweiterung der Basisrichtlinien entwickelt, um mehr Flexibilität zu bieten, sodass Richtlinien beispielsweise auf eine bestimmte Gruppe von Workstations oder Servern angewendet werden können (oder nicht). Daher sind sie spezifischer, können aber auch freizügiger sein, als es die Basisrichtlinie sein sollte. Interessanterweise waren ergänzende Policen vor 2016 nicht an ein bestimmtes Gerät gebunden, sodass ansonsten abgemilderte Umgehungen möglich waren, die durch MS16-094 und MS16-100 behoben wurden und in den Medien umfassend behandelt wurden.
Wenn man diese Informationen im Hinterkopf behält, ist es möglich, mit mehr Klarheit zu ci!SbValidateAndParseDebugAuthToken zurückzukehren: Die Funktion folgt im Wesentlichen drei Schritten: 1. Rufen Sie ci!SbParseAndVerifySignedSupplementalPolicy auf, um den Eingabepuffer aus dem Systemaufruf zu analysieren und zu bestimmen, ob es sich um eine gültig signierte ergänzende Richtlinie 2 handelt. Rufen Sie ci!SbIsSupplementalPolicyBoundToDevice auf, um die DeviceUnlockId aus der ergänzenden Richtlinie mit der des aktuellen Systems zu vergleichen. solche Werte können einfach mit dem Systemaufruf NtQuerySystemEnvironmentValueEx mit der GUID {EAEC226F-C9A3-477A-A826-DDC716CDC0E3}
3 abgerufen werden. Extrahieren Sie abschließend zwei Variablen aus der Richtlinie: eine Ganzzahl (DWORD), die der Schutzstufe entspricht, und eine (UNICODE_STRING) Debug-Berechtigung.
Da es möglich ist, Richtliniendateien zu erstellen (über XML- oder PowerShell-Skripting), ist Schritt 3 kein Problem. Schritt 2 ist auch nicht der Fall, da die DeviceUnlockId mit dem syscall NtSetSystemEnvironmentValueEx({EAEC226F-C9A3-477A-A826-DDC716CDC0E3})
gefälscht werden kann, solange wir über das SeSystemEnvironmentPrivilege-Privileg verfügen. Es sollte jedoch beachtet werden, dass es sich bei der UnlockId um einen flüchtigen Wert handelt, der beim Neustart wiederhergestellt wird.
Das Umgehen von Schritt 1 ist jedoch praktisch unmöglich, da er Folgendes erfordert: - den privaten Schlüssel für ein Microsoft-eigenes Zertifikat mit der speziellen OID 1.3.6.1.4.1.311.10.3.6(d.h. - MS NT5 Lab (szOID_NT5_CRYPTO)) - und dass das oben genannte Zertifikat nicht widerrufen oder abgelaufen sein darf
Also, wo führt uns das hin? Wir haben nun bestätigt, dass PPL-Prozesse entgegen der landläufigen Meinung durch einen anderen Prozess geöffnet werden können, ohne dass ein zusätzlicher Schritt des Ladens eines Kernel-Treibers erforderlich ist. Es sollte jedoch auch betont werden, dass es sich bei einem solchen Anwendungsfall um eine Nische handelt, da nur Microsoft (buchstäblich) die Schlüssel zur Verwendung dieser Technik für sehr gezielte Maschinen in der Hand hält. Nichtsdestotrotz ist ein solcher Fall immer noch ein gutes Beispiel für eine Air-Gap-Verwendung von CI zu Debugging-Zwecken.
Beleidigende TTD
Hinweis: Zur Erinnerung: Zur Erinnerung: TTD.exe erfordert erhöhte Rechte, die alle unten beschriebenen Techniken voraussetzen.
Im Laufe dieser Recherche haben wir einige potenziell interessante offensive und defensive Anwendungsfälle von TTD entdeckt.
Ablaufverfolgung != Debuggen
TTD ist kein Debugger! Daher funktioniert es völlig unentdeckt für Prozesse, die eine grundlegende Anti-Debugging-Überprüfung durchführen, wie z. B. die Verwendung von IsDebuggerPresent() (oder eine andere Methode, die von PEB abhängt. BeingDebugged). Der folgende Screenshot veranschaulicht dieses Detail, indem TTD an einen einfachen Notepad-Prozess angehängt wird:
Von einem Debugger aus können wir das Feld BeingDebugged überprüfen, das sich im Notepad PEB befindet und anzeigt, dass das Flag nicht gesetzt ist:
Der kuriose Fall von ProcLaunchMon
Ein weiterer interessanter Trick, der von TTD zur Verfügung gestellt wird, ist der Missbrauch des integrierten Windows-Treibers ProcLaunchMon.sys. Bei der Ausführung als Dienst (d. h. TTDService.exe) erstellt ttdrecord.dll die Dienstinstanz, lädt den Treiber und kommuniziert mit dem Gerät, das unter .\com_microsoft_idna_ProcLaunchMon verfügbar ist, um neu verfolgte Clients zu registrieren.
Der Treiber selbst wird verwendet, um neue Prozesse zu überwachen, die vom TTD-Dienst erstellt wurden, und diese Prozesse dann direkt vom Kernel aus zu suspendieren, wodurch jeder Schutz umgangen wird, der ausschließlich die Prozesserstellung mit dem Erstellungsflag CREATE_SUSPENDED überwacht (wie hier zum Beispiel erwähnt). Für diese Studie haben wir einen grundlegenden Gerätetreiber-Client entwickelt, den Sie hier finden.
CreateDump.exe
Eine weitere interessante Tatsache: Obwohl es nicht streng genommen Teil von TTD ist, bietet das WinDbgX-Paket eine .NET-signierte Binärdatei, deren Name seine Funktionalität perfekt zusammenfasst: createdump.exe. Diese Binärdatei befindet sich unter "C:\Programme\WindowsApps\Microsoft.WinDbg_*\createdump.exe".
Diese Binärdatei kann verwendet werden, um den Kontext eines als Argument bereitgestellten Prozesses in der direkten Abstammung anderer LOLBAS zu erstellen und auszugeben.
Dies unterstreicht einmal mehr, wie wichtig es ist, sich nicht auf statische Signaturen und Einträge in Dateinamen-Blocklisten zu verlassen, um sich vor Angriffen wie dem Dumping von Anmeldeinformationen zu schützen, und robustere Ansätze wie RunAsPPL, Credential Guard oder Credential Hardening von Elastic Endpoint zu bevorzugen.
Defensive TTD
TTD blockieren
Obwohl TTD eine äußerst nützliche Funktion ist, sind Fälle, in denen sie auf Nicht-Entwicklungs- oder Testmaschinen (z. B. Produktionsservern oder Workstations) aktiviert werden müsste, selten. Auch wenn dies zum Zeitpunkt der Erstellung dieses Artikels weitgehend undokumentiert zu sein scheint, ermöglicht ttdrecord.dll ein vorzeitiges Beendigungsszenario, indem einfach ein Registrierungsschlüssel erstellt oder aktualisiert wird, der sich unter "HKEY_LOCAL_MACHINE\Software\Microsoft\TTD" befindet, und der DWORD32 Wert RecordingPolicy auf 2 aktualisiert wird. Weitere Versuche, einen TTD-Dienst (TTD.exe, TTDInject.exe, TTDService.exe) wird gestoppt und ein ETW-Ereignis wird generiert, um Versuche nachzuverfolgen.
Erkennen von TTD
Die Verhinderung der Verwendung von TTD könnte für alle Umgebungen zu extrem sein – es gibt jedoch mehrere Indikatoren, um die Verwendung von TTD zu erkennen. Ein Prozess, der verfolgt wird, hat die folgenden Eigenschaften:
- Ein Thread führt den Code von TTDRecordCPU.dll aus, die mit einem einfachen integrierten Windows-Befehl überprüft werden kann: tasklist /m TTDRecordCPU.dll
- Auch wenn dies umgangen werden kann, wäre die übergeordnete PID des aufgezeichneten Prozesses (oder die erste, falls die rekursive Ablaufverfolgung aktiviert ist) TTD.exe selbst:
- Auch die _KPROCESS. Der InstrumentationCallback-Zeiger wird so festgelegt, dass er im TTDRecordCPU.dll BSS-Abschnitt der ausführbaren Datei landet:
Daher kann die Erkennung der Ablaufverfolgung von TTD sowohl durch User-Mode- als auch durch Kernel-Mode-Methoden erreicht werden.
Fazit
Damit ist der erste Teil dieser "On-Week"-Studie, die sich auf TTD konzentriert, abgeschlossen. Ein Blick in die Interna des TTD-Ökosystems offenbarte einige sehr interessante, weniger bekannte Mechanismen, die in Windows integriert sind und die erforderlich sind, damit TTD für bestimmte Grenzfälle funktioniert – wie z. B. die Ablaufverfolgung von PPL-Prozessen.
Auch wenn diese Studie keine neue geheime Hintertür für die Ausrichtung auf PPL-Prozesse enthüllte, zeigte sie eine unerforschte Technik, die zu diesem Zweck in Windows integriert ist. Wenn überhaupt, dann unterstreicht diese Studie die Bedeutung eines Modells, das auf starker Kryptographie basiert (hier durch CI.dll), und wie es bei angemessener Implementierung viel Flexibilität bieten kann – bei gleichzeitiger Beibehaltung eines hohen Sicherheitsniveaus.
Der zweite Teil dieser Serie wird weniger forschungsorientiert und mehr praxisorientiert sein, mit der Veröffentlichung eines kleinen Tools, das wir ebenfalls im Rahmen der On-Week entwickelt haben. Dies unterstützt den Prozess der Binäranalyse über TTD unter Verwendung der Windows-Sandbox.
Anerkennung
Da diese Forschung bereits abgeschlossen war und der Artikel in Arbeit war, wurde der Autor auf Untersuchungen aufmerksam, die ein ähnliches Thema und Ergebnisse in Bezug auf dieselbe Technik (PPL-Debugging-Token) abdeckten. Diese Forschung wurde von Lucas George (von der Firma Synacktiv) durchgeführt, der seine Ergebnisse auf der SSTIC 2022 vorstellte.