Von der PowerShell zur Nutzlast: Eine Analyse von waffenfähiger Malware

Cyber Security News

John Hammond, Sicherheitsforscher bei Huntress, nimmt einen tiefen Einblick in die technischen und kodierenden Aspekte einer Malware.

Ein Klick, und bumm, ist Ihr Netzwerk kompromittiert. Alles, was ein Hacker braucht, ist ein erfolgreicher Exploit, und Sie könnten einen sehr schlechten Tag haben. Kürzlich haben wir ein Artefakt aufgedeckt, das wir gerne aufschlüsseln und vorstellen möchten. Wir werden hier “ins Unkraut” steigen und wirklich tief in die technischen Details eintauchen, also setzen Sie Ihren Gehörschutz auf und lassen Sie uns den Bereich ablaufen.

Die rauchende Waffe

Kürzlich hat das ThreatOps-Team von Huntress ein Malware-Artefakt aufgedeckt, das ich gerne aufschlüsseln und vorstellen möchte.

[Blocked Image: https://alltechnews.de/daten/2021/04/Von-der-PowerShell-zur-Nutzlast-Eine-Analyse-von-waffenfaehiger-Malware.png]

Während dies auf den ersten Blick wie Kauderwelsch aussieht, können wir es auseinandernehmen und verstehen, was hier wirklich passiert. Wir werden uns prozedural durch den Code bewegen, eine Zeile nach der anderen nehmen und die Syntax verstehen.

Das erste, was zu beachten ist, ist, dass dies die Form eines Windows-“Batch”-Skripts hat, oder eine Datei mit einer .bat-Erweiterung. Batch-Skripte werden von der Windows-Eingabeaufforderung oder dem Programm “cmd.exe” interpretiert und ausgeführt. cmd.exe ist der Standard-Befehlszeileninterpreter für Windows-Betriebssysteme, aber es ist ein älteres Dienstprogramm, das auf DOS (oder das Disk Operating System) zurückgeht. In der Welt, in der wir heute leben, arbeiten Entwickler und Sicherheitsexperten lieber in PowerShell, einer viel moderneren Befehlszeilen-Shell und Sprache.

PowerShell wird hier in Kürze vorgestellt, aber zunächst müssen wir die Unterschiede in der Syntax besprechen. Variablen werden in der PowerShell mit der Syntax “$varname” bezeichnet, wobei dem Variablennamen ein Dollar-Zeichen vorangestellt wird. In cmd.exe-Batch-Skripten werden Variablen mit “%varname%” angegeben, wobei der Variablenname auf beiden Seiten in Prozentzeichen eingeschlossen ist. In diesem Fall wird eine Umgebungsvariable referenziert, %COMSPEC%. Der Wert dieser Variable ist:

C:WindowsSystem32cmd.exe

Dieser Wert wird an der Stelle der %COMSPEC%-Syntax eingefügt. Wenn er ausgeführt wird, wird cmd.exe mit den folgenden Parametern und Argumenten gestartet. In unserer “waffentechnischen” Analogie können wir diese Anfangsstücke der Nutzlast, den Auslöser, nennen.

Der Auslöser

Das Argument /b bei cmd.exe bedeutet “Starte die Anwendung, ohne ein neues Fenster zu erstellen”, also versucht unser Hacker, sich zu verstecken. /c bedeutet “Einen einzelnen Befehl ausführen und beenden”, was erklärt, dass der Rest des Codes tatsächlich ausgeführt wird.

Der folgende Startbefehl wird ein neues Programm starten, wieder mit dem /b, um zu erzwingen, dass kein Fenster erstellt wird. Das /min-Argument scheint nur als zusätzliche Maßnahme hinzugefügt worden zu sein – die Anwendung würde minimiert starten (falls aus irgendeinem Grund ein Fenster mit dem /b-Argument erstellt werden sollte)

Danach sehen wir, dass powershell.exe die gestartete Anwendung ist. Sie enthält auch viele Argumente, wie -nop (nicht mit einem Startprofil instanziieren), -w hidden (noch einmal, kein Fenster erstellen), -noni (nicht im interaktiven Modus ausführen) und schließlich -c (einen einzelnen Befehl ausführen und beenden).

An diesem Punkt haben wir endlich die Codekette erstellt, die an PowerShell übergeben wird. Diese führt einige Überprüfungen durch, um sicherzustellen, dass die Nutzlast, die für das Ziel verwendet wird, geeignet ist.

Das Visier

Ganz am Anfang der PowerShell-Syntax sehen wir:

[Blocked Image: https://alltechnews.de/daten/2021/04/1617382449_109_Von-der-PowerShell-zur-Nutzlast-Eine-Analyse-von-waffenfaehiger-Malware.png]

Diese bedingte if-Anweisung ist interessant, weil sie prüft, ob die Größe des Datentyps “Integer-Zeiger” gleich der Zahl 4 ist. Dies mag wie eine zufällige Überprüfung erscheinen, aber es ist tatsächlich eine clevere Methode, um die Systemarchitektur des Ziels zu bestimmen. Ein 64-Bit-Computer würde eine IntPtr-Größe von 8 haben, was sich auf die Länge der Speicheradressen bezieht. Ein 32-Bit-System hätte eine IntPtr-Größe von 4, daher bestimmt der Code den Pfad der PowerShell anhand der Architektur. Das $b wird als PowerShell-Variable erstellt, um den Pfad der ausführbaren PowerShell-Datei zu speichern.

Direkt nach dieser if-Anweisung sehen wir das nächste Stück Code:

[Blocked Image: https://alltechnews.de/daten/2021/04/1617382449_24_Von-der-PowerShell-zur-Nutzlast-Eine-Analyse-von-waffenfaehiger-Malware.png]

Dies erstellt eine weitere PowerShell-Variable $s, die diesmal als neues Objekt definiert ist. In diesem Fall handelt es sich bei dem erstellten Objekt um einen neuen Prozess, dessen Dateiname auf $b (wie wir jetzt wissen, ist das der Pfad zur PowerShell) gesetzt wird, mit Argumenten, wie wir sie zuvor gesehen haben. Wiederum wird eine weitere PowerShell-Instanz erzeugt, ohne Profil und mit einem ausgeblendeten Fenster.

Der Bullet

Für den Befehl, der von der neuen, innersten PowerShell-Instanz ausgeführt wird, sehen wir diese Syntax:

[Blocked Image: https://alltechnews.de/daten/2021/04/1617382450_799_Von-der-PowerShell-zur-Nutzlast-Eine-Analyse-von-waffenfaehiger-Malware.png]

Die [scriptblock]::create-Aufruf definiert neuen Code, der ausgeführt werden soll. Das New-Object IO.StreamReader ermöglicht es uns, den Code “on-the-fly” zu lesen, gezogen aus den übergebenen Daten.

Die Daten, die wir sehen, sind in diesen Funktionen verpackt: IO.Compression.GzipStream, IO.MemoryStream und [Convert]::FromBase64String, wobei der GzipStream ein Decompress-Flag verwendet.

Dies zeigt an, dass der große Block mit scheinbar kauderwelschartigen und unsinnigen Zeichen tatsächlich Base64-kodierte GZIP-Daten sind.

Base64 ist ein Kodierungsschema, das lediglich Daten in einem anderen Format darstellt. Die Dekodierung der Daten ist trivial – Sie führen einfach die umgekehrte Operation durch. GZIP-Daten sind komprimierte, archivierte Daten, praktisch dasselbe wie ein .ZIP-Archiv, das Sie vielleicht als Datei auf Ihrem Computer sehen. Glücklicherweise können wir die umgekehrte Operation an diesem großen Datenbrocken durchführen, um besser zu verstehen, was er tut.

Aber lassen Sie uns zunächst die Analyse des restlichen Codes abschließen.

Der Schalldämpfer & der Schütze

Direkt nach dem Klecks Base64 sehen wir diese Codezeilen:

[Blocked Image: https://alltechnews.de/daten/2021/04/1617382450_701_Von-der-PowerShell-zur-Nutzlast-Eine-Analyse-von-waffenfaehiger-Malware.png]

Ich bezeichne dieses Segment scherzhaft als “den Schalldämpfer”, weil es wieder einmal versucht, die neue PowerShell-Instanz zu maskieren und zu verstecken. Dieses $s ist unser neuer Prozess, mit Konfigurationswerten, die so eingestellt sind, dass das Fenster versteckt wird, das Fenster nicht erstellt wird und die Standardausgabe nicht verfolgt oder eine neue Shell aufgerufen wird.

Und natürlich sehen wir gleich im Anschluss an diesen Ausschnitt, was die Waffe wirklich abfeuert.

[Blocked Image: https://alltechnews.de/daten/2021/04/1617382450_552_Von-der-PowerShell-zur-Nutzlast-Eine-Analyse-von-waffenfaehiger-Malware.png]

Mit dieser Zeile wird unser neuer Prozess gestartet und der dekodierte und unkomprimierte Code innerhalb des Base64-Blob wird ausgeführt. Jetzt, da wir ein besseres Verständnis davon haben, wie das funktioniert, können wir den Blob mit den Daten heranzoomen.

Im Inneren der Munition

Die eigentliche Substanz bei diesem Launcher kommt aus dem Base64-kodierten, GZIP-komprimierten Blob, der extrahiert und on the fly ausgeführt wird. Das ist dieser Brocken:

[Blocked Image: https://alltechnews.de/daten/2021/04/1617382450_33_Von-der-PowerShell-zur-Nutzlast-Eine-Analyse-von-waffenfaehiger-Malware.png]

Wir können die umgekehrten Operationen mit jedem beliebigen Toolkit durchführen, sei es auf der Kommandozeile, mit Python oder sogar mit CyberChef.

Der Einfachheit halber können wir das mit CyberChef machen.

Das Ergebnis ist, wenig überraschend, mehr PowerShell-Code. Wie wir bereits wissen, wird dieser vom Launcher ausgeführt. Der Ausgabe-Dump sieht so aus:

[Blocked Image: https://alltechnews.de/daten/2021/04/1617382451_615_Von-der-PowerShell-zur-Nutzlast-Eine-Analyse-von-waffenfaehiger-Malware.png]

[Blocked Image: https://alltechnews.de/daten/2021/04/1617382451_121_Von-der-PowerShell-zur-Nutzlast-Eine-Analyse-von-waffenfaehiger-Malware.png]

Offensichtlich gibt es hier eine Menge zu entpacken. Dieser PowerShell-Code ist zumindest einigermaßen lesbar, da es klare Zeilenumbrüche und Leerzeichen gibt – aber Variablennamen und einige der Logik sind immer noch verschleiert. Wir werden ihn Stück für Stück entschlüsseln.

Untersuchen des Schießpulvers

Die erste Funktion, die wir in diesem PowerShell-Code definiert sehen, heißt sOH, was nicht sehr aussagekräftig ist. Alle diese Funktions- und Variablennamen scheinen zufällig und verschleiert zu sein, aber wir können ihnen einen Sinn geben, wenn wir die Definition der Funktion lesen.

[Blocked Image: https://alltechnews.de/daten/2021/04/1617382451_363_Von-der-PowerShell-zur-Nutzlast-Eine-Analyse-von-waffenfaehiger-Malware.png]

Die soH-Funktion nimmt zwei Parameter auf. Sie verwendet eine Technik, um “reflektierend” nach der Adresse von Win32-API-Aufrufen zu suchen, damit PowerShell die Möglichkeit hat, diese zentralen, internen Prozeduren auszuführen, die dem Betriebssystem auf niedrigerer Ebene bekannt sind. Im aktuellen Kontext sucht es danach, wo die System.dll geladen sein könnte, und verwendet dies, um einen gewünschten Funktionsnamen innerhalb anderer DLLs zu finden, den es dann ausführen könnte. Der Name der DLL, zu der diese Funktion gehört, und die Win32-API-Funktion selbst, die aufgerufen werden soll, sind die beiden Werte, die als Parameter an diese sOH-Funktion übergeben werden. Dies alles geschieht unter Verwendung von “Reflection”, der Fähigkeit, die es PowerShell erlaubt, eine gewisse Introspektion durchzuführen und bereits definierte Prozeduren nachzuschlagen.

Letztendlich gibt dies PowerShell viel mehr Macht. Durch den Zugriff auf die Ausführung der Win32-API-Funktionen kann sie Dinge wie das Zuweisen von Speicher, das Kopieren und Verschieben von Speicher oder andere merkwürdige Dinge tun, die wir sehr bald im Code sehen werden. Für unser eigenes Verständnis sollten wir diese Funktion geistig in etwas wie umbenennen:

[Blocked Image: https://alltechnews.de/daten/2021/04/1617382451_789_Von-der-PowerShell-zur-Nutzlast-Eine-Analyse-von-waffenfaehiger-Malware.png]

Das, was wir bisher als sOH-Funktion kannten, fügt einen Teil dieser neuen Fähigkeit hinzu. Wenn Hacker dieses Handwerkszeug verwenden möchten, um Win32-API-Funktionsaufrufe innerhalb von PowerShell aufzurufen, benötigen sie auch die Funktionalität, mit “Delegaten” zu arbeiten. Die nächste Funktion, b9MW, vervollständigt den “Boilerplate”-Code, der dafür erforderlich ist.

[Blocked Image: https://alltechnews.de/daten/2021/04/1617382452_171_Von-der-PowerShell-zur-Nutzlast-Eine-Analyse-von-waffenfaehiger-Malware.png]

Wie Sie sehen können, ist dies überfüllt mit den notwendigen Windows-Interna und dem Fluff, der dies möglich macht. Wir werden diesen Code nicht ausführlich analysieren und jede einzelne Zeile und Variable erklären, aber diese Funktion bietet nun die Möglichkeit, Win32-API-Funktionsparameter und Rückgabewerte zu interpretieren.

Da unser Hacker die Funktionalität ausbaut, um Win32-API-Funktionen mit PowerShell aufrufen zu können, benötigte er diese sOH-Prozedur, um die Funktionen finden und lokalisieren zu können, und diese b9MW-Prozedur, um Parameter zu liefern und die Funktionsrückgabewerte zu verstehen.

Mit diesen beiden Funktionen verfügt der Code jetzt über die Primitive, um jede beliebige Win32-API-Funktion frei aufzurufen. Als nächstes werden wir dies in Aktion sehen.

Der Sprengstoff

Im Anschluss an diese Funktionsdefinitionen definiert dieses PowerShell-Snippet ein Array von Bytes, das durch Dekodieren von mehr kodiertem Base64 herausgezogen wird.

[Blocked Image: https://alltechnews.de/daten/2021/04/1617382452_951_Von-der-PowerShell-zur-Nutzlast-Eine-Analyse-von-waffenfaehiger-Malware.png]

Die Dekodierung dieses Base64 liefert uns leider eine Menge nicht druckbarer Zeichen.

[Blocked Image: https://alltechnews.de/daten/2021/04/1617382453_513_Von-der-PowerShell-zur-Nutzlast-Eine-Analyse-von-waffenfaehiger-Malware.png]

Wir können so weit gehen zu sagen, dass dies Shellcode ist, oder Prozessoranweisungen als Opcodes, die ausgeführt werden. Da es sich um binäre Daten handelt, können wir nicht schnell einen Sinn darin erkennen, aber wir wissen, dass diese Malware am Ende Shellcode verwendet.

Direkt darunter sehen wir:

[Blocked Image: https://alltechnews.de/daten/2021/04/1617382453_320_Von-der-PowerShell-zur-Nutzlast-Eine-Analyse-von-waffenfaehiger-Malware.png]

Jetzt ist eine Variable $sC6US im Spiel, die die Funktion GetDelegateForFunctionPointer aufruft, mit unseren neu definierten Funktionen sOH und b9MW. Erinnern Sie sich, diese Funktionen erlaubten dem Hacker, Win32-API-Funktionen zu laden – und in diesem Fall sehen wir, dass er die VirtualAlloc-Funktion herausgezogen hat.

Diese VirtualAlloc-Funktion weist das Betriebssystem an, Speicher zu allozieren. Wie wir aus den Funktionsparametern ersehen können, ruft es diese Funktion auf, um genügend Speicher für die Länge des Byte-Arrays $bUMJ (den Shellcode) zu allozieren! Die 0x3000 gibt an, dass “dieser Speicher reserviert und festgeschrieben werden soll”, und die 0x40 gibt an, dass “dieser Speicher lesbar, schreibbar und ausführbar sein soll.”

An diesem Punkt wird der zugewiesene Speicherplatz in der Variablen $sC6US gespeichert. Dann wird eine Copy-Funktion aufgerufen, um diesen Speicherbereich mit dem Shellcode-Byte-Array $bUMJ zu füllen. Das bösartige Skript hat nun Speicher für den Shellcode reserviert, und wir können leicht erraten, was es als nächstes tun wird: Den Shellcode ausführen.

[Blocked Image: https://alltechnews.de/daten/2021/04/1617382453_435_Von-der-PowerShell-zur-Nutzlast-Eine-Analyse-von-waffenfaehiger-Malware.png]

Als Nächstes wird eine $t6Y-Variable erstellt, die wiederum nach einem Win32-API-Aufruf greift und diesen aufruft, dieses Mal speziell CreateThread . Dieser CreateThread-Aufruf wird mit der Speicheradresse $sC6US aufgerufen – die, wie wir jetzt wissen, den Shellcode enthält. Letztendlich wird dadurch der Shellcode ausgeführt!

Danach sehen wir einen weiteren Aufruf zur Ausführung der Win32-API-Funktion WaitForSingleObject.

Damit wird die Ausführung “blockiert” und geduldig gewartet, bis der Shellcode fertig ausgeführt ist. Sie können sehen, dass die Variable $t6Y (der neue Thread, in dem der Shellcode ausgeführt wird) enthalten ist, und 0xFFFFFFFF bedeutet “für immer warten”.

Nach all diesen verschachtelten Schichten, Verschleierungen und Abstraktionen hat die Malware schließlich den Shellcode in den Speicher geladen und ausgeführt. Die nächste Frage ist, was genau macht dieser Shellcode?

Als Sicherheitsanalysten haben wir noch einiges zu tun. Wir können das Verhalten dieser Malware überwachen – beobachten, ob sie neue Dateien erstellt oder einen anderen externen Endpunkt aufruft. Der Shellcode selbst sieht sehr klein aus, also ist das vielleicht ein Stub, um noch mehr Malware zu laden. Während sich dieser Artikel ausschließlich auf das Verständnis des PowerShell-Launchers konzentrierte, könnte der nächste Artikel vielleicht den Shellcode in einem Debugger wie “scdbg” analysieren oder die Malware bei der Ausführung in einer eingeschränkten Sandbox beobachten.

Wir sind hier unter die Haube getaucht, um besser zu verstehen, was die Hacker getan haben und wie ihre Nutzlast funktioniert. Von der Offensive zu lernen ist der beste Weg, um eine stärkere Verteidigung zu haben. Einige Abschwächungstaktiken, wie das Aktivieren von AppLocker oder PowerShell Constrained Language Mode, würden zumindest die Ausführung dieses anfänglichen Launchers blockieren, und die Hacker müssten sich mehr anstrengen. Am Ende des Tages ist das unser Ziel: Die Hacker müssen sich jeden Zentimeter ihres Zugangs verdienen.

John Hammond ist Sicherheitsforscher bei Huntress sowie Ausbilder für Cybersicherheit, Entwickler, Red Teamer und CTF-Enthusiast.

Weitere Erkenntnisse der InfoSec Insider-Community von Threatpost finden Sie auf unserer Microsite.

Einige Teile dieses Artikels stammen aus:
threatpost.com