
Beacon Object File-Implementierung eines UAC-Bypasses durch Event Viewer-Deserialisierung
Dies ist eine Beacon Object File-Implementierung des Event Viewer-Deserialisierungs-UAC-Bypasses, der von @orange_8361 entdeckt wurde, und des POC, der von CsEnox zusammengestellt wurde.

Getestet auf x64 Win10/Win11
Dieser UAC-Bypass führt die folgenden Aktionen aus, die im Hinblick auf OPSEC berücksichtigt werden sollten:
-1. Schreibt eine Binärdatei nach %LOCALAPPDATA%\Microsoft\Event Viewer\RecentViews
-2. Ruft ShellExecute() auf, um mmc.exe / Event Viewer zu starten
--A. Der Event Viewer öffnet sich im GUI!
--B. Der Event Viewer ruft cmd.exe auf
---a. Cmd.exe ruft Taskkill auf, um mmc.exe zu schließen
---b. Cmd.exe gibt eine Fehlermeldung in das manchmal offen gehaltene Konsolenfenster aus, um den Benutzer glauben zu lassen, dass alles in Ordnung ist
---c. Cmd.exe führt alle weiteren vom Benutzer bereitgestellten Befehle aus
-3. Die Binärdatei wird aus %LOCALAPPDATA%\Microsoft\Event Viewer\RecentViews gelöscht
Nach der ursprünglichen Demonstration und CsEnox' POC wurde ysoserial verwendet, um eine erste Payload zu erstellen:

ysoserial.exe -o raw -f BinaryFormatter -g TextFormattingRunProperties -c "taskkill /f /im mmc.exe >nul && echo Windows has Recovered from an Unexpected Error. You may Close this Window. &&" > cmd.bin
Dieser UAC-Bypass funktioniert, indem mmc.exe aufgerufen wird, das sich auf dem Bildschirm öffnet und geschlossen werden muss. Leider habe ich keinen Weg gefunden, mmc.exe zu starten, ohne dass sich das GUI öffnet; ich habe CreateProcess() und das Flag CREATE_NO_WINDOW untersucht, aber CreateProcess() spielt nicht gut mit UAC. StackOverflow-Beiträge zu diesem Thema haben mich zur ShellExecuteA()-API geführt, die das Flag SW_HIDE (und andere) hat, aber das hat sich nicht als fruchtbar erwiesen. Das Einblenden von mmc.exe auf dem Bildschirm scheint unvermeidlich.
Ysoserial ruft cmd.exe /c auf, daher wird dies verwendet, um taskkill aufzurufen, um mmc.exe zu schließen. && wird verwendet, um Befehle zu verketten, damit die gewünschte Aktion, die als Administrator ausgeführt werden soll, ausgeführt werden kann. Bei Tests habe ich festgestellt, dass in einigen Fällen diese Aktion die von mmc.exe gestartete cmd.exe-Eingabeaufforderung offen hält; dies ist der Fall bei Local Injector (normal) Payloads, wie der standardmäßigen Cobalt Strike ausführbaren Payload. Um dies zu beheben, habe ich die Ausgabe des taskkill-Befehls (der zuvor in die offen gehaltene cmd.exe gedruckt wurde) nach /dev/null umgeleitet und eine Meldung auf dem Bildschirm über einen behandelten Fehler ausgegeben, um zu versuchen, dieses verdächtige Verhalten für einen ungebildeten Benutzer nicht alarmierend wirken zu lassen.
Payloads, die einen Prozess starten, darin injizieren und dann beenden, halten das cmd.exe-Fenster nicht offen, daher ist die Meldung unnötig. Ebenso halten Befehle, die ausgeführt werden und beenden (z.B. net user Administrator neuespasswort), das Fenster nicht offen.
Sie können PowerShell-codierte Befehle über diesen BOF ausführen:



Der taskkill-Befehl und die Meldung an den Benutzer sind in den ysoserial-Befehl eingebettet. Wenn Sie dieses Verhalten ändern möchten, müssten Sie Ihren eigenen ysoserial-Befehl ausführen und der folgenden Demonstration folgen, wie der Code erstellt wurde.
Mit einem benutzerdefinierten Tool wurde die Binärdatei eingelesen und als Hex ausgegeben:

Wenn Sie dies in den Editor kopieren und nach '&' im Hex suchen, finden Sie die gewünschten Bytes (beachten Sie, dass es mehrere Sätze davon gibt, wir brauchen den letzten):

Die Payload wird unmittelbar nach diesen Bytes in zwei Teile aufgeteilt:

Die Länge jedes Teils wird berechnet und für später notiert.
Hier wird es knifflig.
Um ehrlich zu sein, weiß ich nicht viel über Deserialisierung oder ysoserial, aber ich habe herausgefunden, dass es in der Payload einen Satz von Bytes gibt, die die Länge des "wichtigen" Teils der Payload darstellen, einschließlich des benutzerdefinierten Befehls. Dies ist im folgenden Screenshot eines nebeneinander angeordneten Hex-Dumps von zwei verschiedenen Payloads zu sehen, die von ysoserial erstellt wurden und jeweils einen anderen auszuführenden Befehl enthielten:

Aus Gründen, die ich nicht erklären kann, spiegeln diese Bytes die Länge der Payload in Basis-128 wider; die 0x05 wird mit 128 multipliziert, um 640 zu ergeben; dies wird zum Wert des anderen Bytes (0xb0 oder 0xd4) minus 128 addiert; ich habe festgestellt, dass, wenn das zweite Byte (0x05) überläuft (0x06), das erste Byte wieder bei 0x80 beginnt, anstatt bei 0x00, wie man erwarten würde.
Die in diesem Tool verwendete Payload sieht wie folgt aus:

Wenn alle Werte in Dezimal umgerechnet werden, sieht die Mathematik so aus:
(6 x 128) + (180-128) = 820
Die Dateigröße beträgt 1042; ich habe bei Tests festgestellt, dass (zumindest mit diesem Gadget von ysoserial) immer eine Differenz von 222 Bytes zwischen der Dateigröße und der in diesem Bytesatz dargestellten Länge besteht. Diese 222 Bytes scheinen der "Header" der Payload zu sein.
Beide Teile der Payload werden in main.c kopiert, wo die ursprüngliche Länge von 820 auch als Variable gesetzt wird. Die Länge des Benutzerbefehls wird berechnet, und dieser Wert wird zu den ursprünglichen 820 addiert (zusätzlich zu einem einzelnen zusätzlichen Byte für ein Leerzeichen, das eingefügt werden muss), um den endgültigen Wert zu berechnen, der in diesem Bytesatz dargestellt werden muss. Das zweite Byte wird berechnet, indem LenBytes durch 128 geteilt wird, und das erste Byte wird berechnet, indem der Rest von LenBytes ermittelt und 128 addiert wird (um zu berücksichtigen, dass es bei 0x80 beginnt).

Sobald diese Bytes gesetzt sind, werden sie an der richtigen Stelle in den Before-Puffer kopiert, um die Länge der Payload entsprechend dem Inhalt auf den richtigen Wert zu setzen. Der endgültige Puffer wird dann zusammengesetzt:

Die fertige Payload wird dann auf die Festplatte geschrieben und mmc.exe aufgerufen, um sie auszuführen. Danach wird die Payload bereinigt.