Erkennen von Cobalt Strike mit Speichersignaturen
Wir bei Elastic Security gehen die Bedrohungserkennung auf unterschiedliche Weise an. Traditionell haben wir uns dabei auf Machine-Learning-Modelle und verhaltensbasierte Erkennung konzentriert. Diese beiden Methoden sind wirkungsvoll, weil sie auch bisher unbekannte Malware erkennen können. In der Vergangenheit hatten wir das Gefühl, dass Signaturen zu leicht umgangen werden können, uns ist aber auch bewusst, dass die Frage der einfachen Umgehbarkeit nur einer von vielen Faktoren ist, die es zu berücksichtigen gilt. Weitere wichtige Kriterien für das Ermitteln der Effektivität von Erkennungstechniken sind die Performance und die Falsch-Positiv-Rate.
Signaturen können zwar keine unbekannte Malware entdecken, sie haben aber Falsch-Positiv-Raten, die in Richtung Null tendieren, und verfügen über zugehörige Labels, die beim Priorisieren von Alerts helfen. Ein Alert für Ransomware wie TrickBot oder REvil erfordert schnelleres Handeln als eine potenziell unerwünschte Adware-Variante. Selbst wenn wir hypothetisch nur die Hälfte der bekannten Malware mit Signaturen abfangen könnten, wäre dies angesichts der anderen Vorteile schon ein großer Gewinn und wir könnten diese Methode mit anderen Schutzmethoden kombinieren. Realistisch betrachtet kann uns das sogar noch besser gelingen.
Ein Hindernis bei der Erstellung von Signaturen, die einen langfristigen Wert bieten, ist der weit verbreitete Einsatz von Packern und Wegwerf-Ladern für Malware. Diese Komponenten entwickeln sich schnell weiter, um die Signaturerkennung zu umgehen, während die eigentliche Malware entschlüsselt und im Arbeitsspeicher ausgeführt wird.
Um das Problem von Packern und Ladern zu umgehen, können wir unsere Signaturerkennungsstrategien auf die Inhalte im Arbeitsspeicher konzentrieren. Dadurch verlängert sich die „Haltbarkeit“ der Signatur von Tagen auf Monate. In diesem Blogpost verwenden wir Cobalt Strike als Beispiel für die Nutzung von In-Memory-Signaturen.
Erstellen von Cobalt Strike-Signaturen
Cobalt Strike ist ein beliebtes Framework für die Durchführung von Red-Team-Operationen und die Angriffssimulation. Wegen seiner Benutzerfreundlichkeit, Stabilität und Tarnfunktionen wird es auch von Angreifern geschätzt, die wirklich Böses im Schilde führen. Für das Erkennen von Beacon, der Endpoint-Nutzlast von Cobalt Strike, gibt es verschiedene Techniken. Eine davon ist die Suche nach Threads ohne eine zugehörige Datei auf dem Datenträger und, seit Neuerem, nach integrierten Named Pipes. Aufgrund der hohen Konfigurierbarkeit in Beacon gibt es aber in der Regel Möglichkeiten, öffentliche Erkennungsstrategien zu umgehen. Hier werden wir versuchen, als alternative Erkennungsstrategie Speichersignaturen zu verwenden.
Beacon wird typischerweise reflektiv in den Speicher geladen und kommt nie in direkter signierbarer Form auf den Datenträger. Außerdem kann Beacon mit einer Vielzahl von Optionen zur In-Memory-Verschleierung konfiguriert werden, um seine Nutzlast zu verbergen. So versucht z. B. die Option Verschleiern und Schlafen („Obfuscate and sleep“), Teile der Beacon-Nutzlast zwischen Callbacks zu maskieren, um signaturbasierten Speicherscans auszuweichen. Zwar müssen wir diese Option beim Entwickeln von Signaturen berücksichtigen, dennoch ist es trotz dieser fortgeschrittenen Tarnfunktionen einfach, Beacon zu signieren.
Los gehts
Wir beginnen, indem wir bei aktivierter und deaktivierter Option sleep_mask in den neuesten Versionen (Hashes im Referenz-Abschnitt) eine Handvoll Beacon-Nutzlasten abrufen. Lassen wir zunächst sleep_mask deaktiviert: Nach dem Start können wir mithilfe von Process Hacker Beacon im Arbeitsspeicher ausfindig machen, indem wir nach einem Thread suchen, der SleepEx aus einem Bereich ohne zugehörige Datei aufruft:
Von dort können wir den zugehörigen Arbeitsspeicherbereich lokal speichern, um ihn zu analysieren:
Am einfachsten wäre es, wenn wir einige eindeutige Zeichenfolgen aus diesem Bereich auswählten und sie als unsere Signatur verwendeten. Zu Demonstrationszwecken schreiben wir Signaturen mit dem für diesen Zweck häufig verwendeten Tool yara:
rule cobaltstrike_beacon_strings { meta: author = "Elastic" description = "Identifies strings used in Cobalt Strike Beacon DLL." strings: $a = "%02d/%02d/%02d %02d:%02d:%02d" $b = "Started service %s on %s" $c = "%s as %s\\%s: %d" condition: 2 of them }
Damit könnten wir schon eine recht gute Abdeckung erzielen, aber wenn wir auch noch die Proben mit aktivierter Option sleep_mask einbeziehen, geht noch mehr. Wenn wir im Arbeitsspeicher an der Stelle suchen, an der sich der MZ/PE-Header normalerweise befinden würde, sehen wir, dass er verschleiert ist:
Ein schneller Blick darauf verrät uns, dass es viele wiederholte Bytes (in diesem Fall 0x80) gibt, wo wir eigentlich Null-Bytes erwarten würden. Dies kann ein Hinweis darauf sein, dass Beacon eine einfache 1-Byte-XOR-Verschleierung verwendet. Zur Bestätigung können wir CyberChef verwenden:
Wie wir sehen, erscheint nach dem Decodieren die Meldung „This program cannot be run in DOS mode“, was unsere Theorie bestätigt. Da die Verwendung eines 1-Byte-XOR-Operators eher aus der Trick-Mottenkiste stammt, unterstützt yara die native Erkennung mit dem xor-Modifikator:
rule cobaltstrike_beacon_xor_strings { meta: author = "Elastic" description = "Identifies XOR'd strings used in Cobalt Strike Beacon DLL." strings: $a = "%02d/%02d/%02d %02d:%02d:%02d" xor(0x01-0xff) $b = "Started service %s on %s" xor(0x01-0xff) $c = "%s as %s\\%s: %d" xor(0x01-0xff) condition: 2 of them }
Wir können die bisherige Erkennung für unsere yara-Regeln bestätigen, indem wir beim Scannen eine PID bereitstellen:
Das war aber noch nicht alles. Nach dem Testen dieser Signatur an einer Probe mit der neuesten Version von Beacon (zum Zeitpunkt des Verfassen dieses Blogs ist dies 4.2) hat sich die Verschleierungsroutine verbessert. Die Routine können wir finden, indem wir dem Aufruf-Stack wie oben beschrieben folgen. Sie verwendet jetzt einen 13-Byte-XOR-Schlüssel, wie im folgenden IDA Pro-Snippet zu sehen:
Glücklicherweise verschleiert die Option „Verschleiern und Schlafen“ von Beacon nur Zeichenfolgen und Daten, sodass der gesamte Code-Abschnitt für die Signaturerstellung zur Verfügung steht. Da stellt sich die Frage, für welche Funktion im Codeabschnitt wir eine Signatur entwickeln sollten; das wäre aber Thema für einen eigenen Blogpost. Wir begnügen uns fürs Erste damit, eine Signatur für die Verschleierungsroutine zu erstellen, die gut funktionieren müsste:
rule cobaltstrike_beacon_4_2_decrypt { meta: author = "Elastic" description = "Identifies deobfuscation routine used in Cobalt Strike Beacon DLL version 4.2." strings: $a_x64 = {4C 8B 53 08 45 8B 0A 45 8B 5A 04 4D 8D 52 08 45 85 C9 75 05 45 85 DB 74 33 45 3B CB 73 E6 49 8B F9 4C 8B 03} $a_x86 = {8B 46 04 8B 08 8B 50 04 83 C0 08 89 55 08 89 45 0C 85 C9 75 04 85 D2 74 23 3B CA 73 E6 8B 06 8D 3C 08 33 D2} condition: any of them }
Wir können uns vergewissern, dass wir Beacon erkennen können, selbst wenn es sich in seinem Tarnschlafstatus befindet (sowohl in der 32- als auch in der 64‑Bit-Variante):
Um daraus eine robustere Erkennung zu stricken, könnten wir regelmäßig alle Prozesse auf dem System (oder im gesamten Unternehmen) scannen. Möglich wäre dies mit dem folgenden PowerShell-Einzeiler:
powershell -command "Get-Process | ForEach-Object {c:\yara64.exe my_rules.yar $_.ID}"
Fazit
Die signaturbasierte Erkennung, auf die oft von oben herabgesehen wird, ist eine wertvolle Erkennungsstrategie, insbesondere wenn es um das In-Memory-Scanning geht. Mit nur einer Handvoll Signaturen können wir Cobalt Strike mit einer effektiven Falsch-Positiv-Rate von 0 erkennen – unabhängig von der Konfiguration oder davon, welche Tarnfunktionen aktiviert sind.
Referenz-Hashes
7d2c09a06d731a56bca7af2f5d3badef53624f025d77ababe6a14be28540a17a 277c2a0a18d7dc04993b6dc7ce873a086ab267391a9acbbc4a140e9c4658372a A0788b85266fedd64dab834cb605a31b81fd11a3439dc3a6370bb34e512220e2 2db56e74f43b1a826beff9b577933135791ee44d8e66fa111b9b2af32948235c 3d65d80b1eb8626cf327c046db0c20ba4ed1b588b8c2f1286bc09b8f4da204f2
Weitere Informationen zu Elastic Security
Wenn noch nicht geschehen, informieren Sie sich über die leistungsstarken Schutz-, Erkennungs- und Reaktionsmöglichkeiten von Elastic Agent. Probieren Sie Elastic Cloud 14 Tage lang kostenlos aus (keine Kreditkarte erforderlich) oder laden Sie unsere Produkte kostenlos herunter, wenn Sie Ihr Deployment selbst verwalten möchten. Hilfreiche Tipps für einen erfolgreichen Start finden Sie in unserer Quick-Start-Schulung.