Während Sicherheitslösungen der nächsten Generation zunehmend KI- und Maschinelles-Lernen-Komponenten einsetzen, um verhaltensbasierte Erkennungen zu verbessern, stützen sich viele im Kern weiterhin auf signaturbasierte Erkennungsmechanismen. Cobalt Strike , ein weit verbreitetes Red-Team-Command and Control (C2)-Framework, das seit seiner Einführung sowohl von Bedrohungsakteuren als auch von Red Teams eingesetzt wird, ist entsprechend stark von sicherheitsseitiger Signaturerkennung betroffen.
Um Cobalt Strike auch weiterhin operativ einsetzen zu können, hat das IBM X-Force Red Adversary Simulation-Team erhebliche Forschungs- und Entwicklungsarbeit investiert, um Cobalt Strike mithilfe interner Tools anzupassen und zu erweitern. Einige dieser Cobalt-Strike-spezifischen internen Tools sind auch öffentlich verfügbar, darunter: „InlineExecute-Assembly”, „CredBandit” und„BokuLoader”. In den vergangenen zwei Jahren haben wir, aufgrund der starken Über-Signaturisierung von Cobalt Strike, dessen Einsatz auf die Simulation weniger ausgefeilter Bedrohungsakteure beschränkt. Für anspruchsvollere Red-Team-Übungen greifen wir stattdessen auf andere Drittanbieter- sowie interne C2-Lösungen zurück.
Durch kontinuierliche Forschungs- und Entwicklungsarbeit haben wir bei fortgeschrittenen Red-Team-Übungen bessere operative Ergebnisse erzielt, unter anderem durch den Einsatz von:
Nichtsdestotrotz nutzen zahlreiche Bedrohungsakteure weiterhin raubkopierte Versionen von Cobalt Strike, weshalb es nach wie vor wichtig ist, diese Akteure realistisch simulieren zu können. Red Teams, die bereit sind, entsprechenden Forschungs- und Entwicklungsaufwand zu betreiben, können auch heute noch operative Erfolge mit Cobalt Strike erzielen, wenn sie diese Gegenspieler simulieren. Darüber hinaus ist Cobalt Strike ein großartiges Lernwerkzeug, das von Neueinsteigern genutzt werden kann, um im Rahmen von Red-Team-Schulungen Erfahrung mit einem C2-Framework zu sammeln.
Während wir unsere eigenen C2-Fähigkeiten weiter ausbauen, geben wir Einblicke in frühere Arbeiten, bei denen wir das Cobalt-Strike-Framework gezielt erweitert haben, insbesondere durch die Entwicklung kundenspezifischer Reflective Loader. Zudem soll er Defendern helfen zu verstehen, wie Cobalt Strike funktioniert, damit sie robustere Erkennungen entwickeln können.
Branchen-Newsletter
Bleiben Sie mit dem Think-Newsletter über die wichtigsten – und faszinierendsten – Branchentrends in den Bereichen KI, Automatisierung, Daten und mehr auf dem Laufenden. Weitere Informationen finden Sie in der IBM Datenschutzerklärung.
Ihr Abonnement wird auf Englisch geliefert. In jedem Newsletter finden Sie einen Abmeldelink. Hier können Sie Ihre Abonnements verwalten oder sich abmelden. Weitere Informationen finden Sie in unserer IBM Datenschutzerklärung.
Dieser Blogbeitrag ist der erste einer Serie, die als Einführung in die Grundlagen der Entwicklung eines Cobalt Strike Reflective Loaders dient. Im weiteren Verlauf dieser Serie bauen wir auf dieser Grundlage auf und verweisen auf diesen Beitrag.
Unser Ziel ist es, am Ende dieser Serie einen Reflective Loader zu entwickeln, der sich in die bestehenden Evasion-Funktionen von Cobalt Strike integriert und diese sogar um fortgeschrittene Techniken erweitert, die derzeit noch nicht im Tool vorhanden sind. Zukünftige Beiträge werden sich eingehender mit der Entwicklung spezifischer Evasion-Funktionen und deren Implementierung in unseren Cobalt Strike Reflective Loader befassen.
Zum Einstieg behandelt dieser Beitrag Folgendes:
Wir betrachten den Reflective-Loading-Prozess von Cobalt Strike aus der Perspektive eines Entwicklers offensiver Security-Tools und heben Möglichkeiten für Erkennungen und Evasion hervor. Einige Entwicklungsaspekte werden dabei ausgelassen oder vereinfacht dargestellt. Wir ermutigen dazu, diese Lücken zu schließen, indem bestehende Reflective Loader-Projekte debuggt, von Grund auf neu aufgebaut oder entsprechende Schulungen genutzt werden.
Das Cobalt Strike C2-Implantat, bekannt als Beacon, ist eine Windows Dynamic-Link-Library (DLL). Die modulare Möglichkeit, einen eigenen DLL-Loader in Cobalt Strike zu verwenden, ist als User-Defined Reflective Loader (UDRL) bekannt.
Normalerweise ist der in Windows DLL-Loader für das Laden von DLLs in den virtuellen Speicherbereich eines Prozesses zuständig. Er existiert hauptsächlich im User Space, wechselt jedoch beim Mapping von DLLs von der Festplatte teilweise in den Kernel Space.
Nachteile des Windows DLL‑Loaders bei Adversary-Simulationen:
Daher ist der Windows DLL Loaders für das Laden unserer Beacon-DLL keine ideale Lösung. Um diese Herausforderungen zu umgehen, laden wir die Beacon-DLL aus dem Speicher mittels eines Reflective Loaders.
Die drei wichtigsten Erkennungspunkte, die durch Reflective Loading vermieden werden:
Reflective Loading bezeichnet das Laden einer Raw DLL direkt aus dem Speicher im Gegensatz zum Laden von der Festplatte.
Sowohl ein Reflective Loader als auch der Windows DLL-Loader erfüllen denselben Zweck: das Laden einer DLL aus dem Raw-Dateiformat in den virtuellen Speicherbereich eines Prozesses. Reflective Loading bietet jedoch den entscheidenden Vorteil, dass die DLL-Datei nicht im Dateisystem vorhanden sein muss. Diese In-Memory-Ladung ermöglicht eine unbegrenzte Anzahl von Chain-Loading-Phasen, da die C2-Implantat-DLL in mehreren Schichten aus Verschlüsselung und Encodierung im Speicher verborgen werden kann.
Ein wichtiger Aspekt beim Laden einer DLL ist, dass die DLL auf der Festplatte anders formatiert wird als im Speicher. Die Hauptunterschiede zwischen der DLL im Raw- und im Virtual-Address-Format sind:
Raw-Dateiformat:
Virtual Address Format:
Durch die Analyse einer HTTP-Beacon-DLL mit dem PE-Bear -Tool von Aleksandra Doniec lassen sich die Unterschiede zwischen der Raw- und Virtual-Adressierung pro Abschnitt der DLL erkennen:
Tabelle mit Auflistung der Raw- und Virtual-Adressen der einzelnen Abschnitte der Beacon-DLL.
Diese HTTP/S-Beacon-DLL ist
PE-Bear bietet eine visuelle Darstellung unserer Beacon-DLL im Raw-Dateiformat im Vergleich zum Virtual-Adress-Format:
Visuelle Darstellung der Beacon-DLL im Raw-Dateiformat (links) im Vergleich zum Virtual Address Format (rechts)
Auch wenn dies für eine Adversary-Simulation nicht die klügste Vorgehensweise ist, ist das Speichern einer unverschleierten Beacon-DLL auf der Festplatte und deren Laden mit dem Windows DLL-Loader eine hervorragende Möglichkeit, sowohl das Laden von Beacons als auch von DLLs zu entmystifizieren. Im Kern ist Beacon lediglich eine DLL. Sowohl der Windows DLL-Loader als auch ein Reflective Loader laden eine DLL in einen Prozess.
Um die Beacon-DLL mit dem Windows DLL Loader zu laden, führen wir die folgenden Schritte aus:
LoadLibrary
API verwendet, um die Beacon-DLL von der Festplatte zu laden.Zunächst deaktivieren wir alle Malleable-PE-Optionen, die verhindern, dass unsere Beacon-DLL durch den Windows DLL-Loader geladen werden kann. Dazu modifizieren wir unser Malleable-C2-Profil und deaktivieren die Malleable-PE-Evasion-Optionen, die sich im Stage-Block befinden:
Der Stage-Block des Malleable-C2-Profils wurde angepasst, um die Evasion-Funktionen von Cobalt Strike zu deaktivieren.
Nach der Änderung des Profils starten wir den Cobalt Strike Team Server neu und übergeben unser
Anschließend verbinden wir uns mit dem Team Server über den Cobalt Strike Client. Danach erstellen wir eine
Screenshot von der Erstellung einer „raw stageless“ Beacon-DLL im Cobalt Strike Client
Mit dem folgenden Code erstellen wir ein C-Programm mit dem Namen
Der Code lädt die Raw-Beacon-DLL mithilfe des Windows-DLL-Loaders von der Festplatte.
Wir verwenden die
Im Rahmen des Ladevorgangs initialisiert der Windows-DLL-Loader unsere Beacon-DLL, indem er ihren Entry Point mit dem
Nachdem der Windows DLL Loader unsere Beacon-DLL in den virtuellen Speicherbereich unseres Prozesses geladen und initialisiert hat, müssen wir den Entry Point der virtuellen Beacon-DLL erneut mit dem Argument aufrufen
Unser Programm muss den Entry Point der virtuellen Beacon-DLL kennen, um diese ausführen zu können. Dies kann dynamisch innerhalb des Programms erfolgen, indem die Header der virtuellen Beacon-DLL nach der Relative Virtual Adress (RVA) des Entry Points durchsucht werden, oder durch das manuelle Ermitteln und Festcodieren des Werts.
Für unser Proof-of-Concept ermitteln wir den RVA des Entry Points der Beacon-DLL manuell und codieren ihn fest im Programm. Mithilfe von PE-Bear stellen wir fest, dass der RVA des Entry Points der Beacon-DLL das Folgende beträgt:
Screenshot der Suche nach der RVA des Entry Points der Beacon-DLL mithilfe von PE-Bear
Der
Mit dem fertigen Code kompilieren wir unser C-Programm zu einer Windows-Executable:
Der Befehl, der zum Kompilieren unseres Programms verwendet wurde:
Indem wir unsere Beacon-DLL und unser ausführbares Beacon-Loader-Programm im selben Verzeichnis ablegen, kann der Windows DLL-Loader unsere DLL während seiner Laderoutine finden.
Wir platzieren beide
Die Beacon-DLL und das Loader-Programm befinden sich im selben Verzeichnis.
Von unserem Windows-Desktop aus doppelklicken wir auf die Datei loadBeaconDLL.exe. und stellen eine aktive Beacon-Verbindung zu unserem Team Server her.
Erfolgreiche Verbindung zum C2-Team-Server über eine Beacon-DLL, die mit dem Windows DLL-Loader geladen wurde.
Cobalt Strike verwendet eine modifizierte Version des Reflective Loader-Projekts von Stephen Fewer. Dieser legendäre DLL-Loader im Speicher ist über ein Jahrzehnt alt und wurde in Metasploit und anderen bedeutenden offensiven Sicherheitstools verwendet.
Im Laufe der Jahre wurde der Cobalt Strike Reflective Loader kontinuierlich verbessert, um alle Malleable-PE-Evasion-Funktionen von Cobalt Strike zu bewältigen. Der größte Nachteil bei der Verwendung eines benutzerdefinierten User-Defined Reflective Loaders (UDRL) besteht darin, dass einige Malleable-PE-Evasion-Funktionen möglicherweise nicht standardmäßig unterstützt werden.
Einige Evasion-Funktionen werden bei Verwendung einer UDRL vollständig implementiert und von der Cobalt Strikes Malleable PE-Engine bei der Erstellung der Beacon-Payload in die Beacon-DLL gepatcht. Aktuell müssen jedoch Funktionen wie von der UDRL selbst behandelt werden, während andere wie und durch die Beacon-DLL mit entsprechender UDRL-Integration gehandhabt werden können.
Das ursprüngliche Reflective-Loader-Projekt erfordert, dass die ReflectiveLoader-Funktion in das DLL-Projekt kompiliert
Ein weiteres Projekt ist verantwortlich für:
Diagramm des ursprünglichen Reflective Loaders beim Laden einer DLL in den virtuellen Speicher.
Eine alternative Methode besteht darin, den Reflective Loader vor die DLL zu stellen. Dies ermöglicht das Laden beliebiger unmanaged DLLs und erfordert keine Kompilierung der DLL aus dem Quellcode. Diese Methode ist robust und kann prinzipiell jede PE-Datei (EXE oder DLL) laden.
Diagramm eines vorangestellten Reflective Loaders, der eine DLL in den virtuellen Speicher lädt.
Die Implementierung des Reflective Loadings in Cobalt Strike verwendet eine Kombination der beiden oben genannten Methoden. Diese Methode dürfte denjenigen vertraut sein, die wissen, wie Metasploits Meterpreter Reflective Loading durchführt.
Wie bei der ursprünglichen Reflective-Loader-Methode wird
Wenn eine UDRL in Cobalt Strike geladen wird und ein Operator eine Beacon-Payload vom Cobalt Strike-Client aus generiert, fügt die Malleable PE-Engine von Cobalt Strike den Shellcode des Reflective Loaders am Raw-File-Offset des
Sobald die Malleable PE-Engine das Patchen der Raw Beacon-DLL abgeschlossen hat, wird die Raw Beacon-DLL dem Operator in einem ausführbaren Shellcode-ähnlichen Format zur Verfügung gestellt.
Diagramm des Cobalt Strik Reflective Loaders beim Laden der Beacon-DLL in den virtuellen Speicher.
Die ersten Bytes der Beacon-DLL im PE-Bear-Disassembler zeigen, dass sie ausführbar ist:
Der Call Reflective Loader Stub wird als ausführbare Assembly-Operationscodes angezeigt.
Die ersten Bytes
Nach der Ausführung optional vorangestellter
Wir bestätigen den Raw-File-Offset des
Screenshot der Verwendung von PE-Bear zur Bestimmung des Raw-File-Offsets des Reflective-Loader-Exports.
Da der Export im Exportverzeichnis existiert, verweist die Adresse des
Um den Raw-File-Offset des
Die virtuellen und Raw-Adressen des
Roh- und virtuelle Adressen des .text -Abschnitts der Beacon-DLL.
Der Unterschied zwischen den beiden beträgt
Dies lässt sich in PE-Bear bestätigen, indem man mit der rechten Maustaste auf
Zusammenfassung des Reflective-Loading-Prozesses von Cobalt Strike:
Diagramm, das die Hauptphasen des Reflective Loadings der Beacon-DLL durch Cobalt Strike zeigt.
Da der Reflective Loader ausgeführt wird, bevor die Beacon-DLL geladen ist, muss sein Code reiner Shellcode sein.
Der einfachste Weg, um komplexen Shellcode zu erzeugen, ist, ihn in C ohne externe Abhängigkeiten zu schreiben. Die C-Datei wird anschließend zu einer Objektdatei kompiliert. Der gesamte Code muss im abschnitt der Objektdatei enthalten sein. Abschließend wird dieser Abschnitt extrahiert,.text
Die Malleable-PE-Engine von Cobalt Strike übernimmt das Extrahieren des Shellcodes aus unserer Reflective-Loader-Objektdatei und patcht ihn in die Raw-Beacon-DLL am Raw-File-Offset des
Aggressor-Skript zum Schreiben des Reflective-Loader-Shellcodes in die Raw Beacon-DLL mit Cobalt Strike.
Mit diesem Aggressor-Skript kann der Reflective-Loader-Shellcode in die Raw Beacon-DLL mit Cobalt Strike geschrieben werden. Führen Sie dazu die folgenden Schritte aus:
<a href="https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics_aggressor-scripts/as-resources_functions.htm#extract_reflective_loader">extract_reflective_loader</a>Cobalt-Strike-Aggressor-Funktion parst unsere UDRL-Objektdatei aus dem
<a href="https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics_aggressor-scripts/as-resources_functions.htm#setup_reflective_loader">setup_reflective_loader</a>Die Cobalt-Strike-Aggressor-Funktion verwendet anschließend die Malleable-PE-Engine, um den Raw-File-Offset unseres
Cobalt Strike hat die Arbeit für uns übernommen, indem der .text -Abschnitt aus unserer Reflective-Loader-Objektdatei extrahiert, unser Reflective-Loader-Shellcode gepatcht und unser Reflective Loader über den Call Reflective Loader Stub im Header der Beacon-DLL aufgerufen wird.
Dies sind die Phasen, die wir entwickeln müssen, um Beacon mittels Reflective Loading zu laden:
Es gibt mehrere Methoden, mit denen wir die Adresse für die Raw Beacon-DLL im Speicher ermitteln können. Einige dieser Methoden sind:
Wenn wir eine Methode verwenden, die rückwärts sucht, müssen wir zunächst die aktuelle Adresse des Instruction Pointers unseres Threads ermitteln (
Intel-x64-Assembler-Code zum Ermitteln der Basisadresse der Raw Beacon DLL aus dem RDI-Register.
Das ursprüngliche Reflective-Loader-Projekt sucht rückwärts nach den MZ- und PE-Headern. Diese Header sind zu Erkennungspunkten geworden. Um dies zu vermeiden, hat Cobalt Strike die
Die Cobalt-Strike-Dokumentation beschreibt hierzu folgende
Wenn diese Option konfiguriert ist, sind
Diese Bytes müssen hinreichend eindeutig sein, sonst kann der Reflective Loader sie nicht finden. Zusätzlich müssen die Bytes des MZ-Headers No-Operation-Instruktionen sein und ausführbar bleiben. Bestimmte Werte wie
Nachdem ich diesen potenziellen Erkennungspunkt entdeckt hatte, entwickelte ich eine andere, aber ähnliche Methode, um die Adresse der Raw Beacon-DLL zu ermitteln. Diese Methode verwendet einen Egg-Hunter, der rückwärts nach dem
Die Adresse
Da wir keinen einfachen Zugriff auf die Java Malleable PE-Engine haben, kann das
Aggressor-Skript zum Schreiben eines Eggs in die Raw Beacon-DLL und zur Anzeige der Änderungen in der Cobalt Strike-Skriptkonsole.
Der UDRL-Code muss den Egg-Wert kennen, der vom UDRL-Skript in die Raw Beacon-DLL geschrieben wurde. Wenn das Egg bekannt ist, sucht der Egg-Hunter rückwärts nach zwei Instanzen dieses Eggs, wie im folgenden Code zu sehen ist:
Intel-x64-Assembler-Code für einen Egg-Hunter, der rückwärts nach zwei Instanzen eines 64-Bit-Eggs sucht.
Da die MZ- und PE-Header nicht mehr benötigt werden, können sie im UDRL-Aggressor-Skript maskiert werden:
Aggressor-Skript zum Maskieren von MZ-, PE- und ungenutzten Bytes des DOS-Banners in den Headern der Raw Beacon-DLL.
Es gibt auch eine andere, speziell für Cobalt Strike entwickelte Methode, um die Basisadresse der Raw Beacon-DLL zu ermitteln. Wie wir oben gesehen haben, speichern die ersten Bytes des Call Reflective Loader Stubs die Basisadresse der Raw Beacon-DLL in einem
Um dies im Debugger weiter zu untersuchen, generieren wir ein Beacon, stellen einen Breakpoint voran (
Screenshot von X64dbg, der das schrittweise Durchlaufen des Aufrufs des Reflective Loaders zeigt, wobei die Basisadresse der Raw Beacon-DLL vor dem Aufruf des Reflective Loaders im RDI-Register gespeichert wird.
Nachfolgend ein funktionierendes Beispiel, wie man die Basisadresse der Raw Beacon-DLL aus dem Call Reflective Loader Stubs erhält:
Inline-Assembly-C-Code zum Ermitteln der Raw-Beacon-DLL-Basisadresse aus dem RDI-Register.
Mit der Adresse der Raw-Beacon-DLL können wir nun die Werte ermitteln, die wir benötigen, um Beacon in den virtuellen Adressraum des Prozesses zu laden.
Die folgende Tabelle listet die Werte auf, die wir aus den Headern der Raw Beacon-DLL benötigen, die Speicherorte, an denen wir sie finden, sowie ihre Typen:.
Tabelle mit Werten aus den Headern der Raw Beacon-DLL, die für das Laden der Beacon-DLL relevant sind.
Nicht alle Inhalte der Header werden zum Laden der Beacon-DLL benötigt. Erforderliche Werte können neu gepackt oder verschleiert werden. Nicht benötigte Werte können entfernt oder randomisiert werden.
Sobald wir wissen,SizeOfImagef
Für die Zuweisung von Arbeitsspeicher für die virtuelle Beacon-DLL können verschiedene Methoden verwendet werden. Unterschiedliche Methoden verwenden unterschiedliche Speichertypen. Die verschiedenen vom standardmäßigen Reflective Loader von Cobalt Strike unterstützten Methoden sind:
Tabelle mit den Speicherzuweisungsoptionen von Cobalt Strike für die Virtual Beacon-DLL.
Mit UDRL kann dies noch einen Schritt weitergeführt werden. Stattdessen kann die NTAPI-Version dieser Funktionen verwendet werden. Darüber hinaus können die NTAPI-Funktionen über direkte oder indirekte Systemaufrufe aufgerufen werden, was die Evasions-Fähigkeiten verbessern kann.
Wenn die Allokationsmethode auf
Ein Codebeispiel aus dem BokuLoader-Projekt, das zeigt, wie ein direkter Systemaufruf zur Speicherzuweisung für die Virtual Beacon-DLL verwendet wird.
Das folgende Bild zeigt ein Codebeispiel für die Verwendung der HellsGate- und HalosGate-Methoden zur Ermittlung der Systemaufrufnummern:
Codebeispiel aus dem BokuLoader-Projekt, das zeigt, wie Systemaufrufe aus dem Prozess ermittelt werden.
Da wir nun Speicher für unsere virtuelle Beacon-DLL zugewiesen haben, müssen wir die Abschnitte des Beacons aus ihren Raw-File-Offsets, wie sie in der Raw-Beacon-DLL vorliegen, in den zugewiesenen Speicher an ihren relativen virtuellen Offsets kopieren.
Wenn wir unseren Speicher mitREADWRITE Vor dem Aufrufen des Entry Points der Virtual Beacon-DLL müssen wir den Speicherschutz des
Die Zuweisung unseres Speichers mit
Im Folgenden ist ein vereinfachtes Codebeispiel aus dem BokuLoader-Projekt dargestellt, das dies demonstriert:.
Codebeispiel aus dem BokuLoader-Projekt, das zeigt, wie Abschnitte aus der Raw Beacon-DLL in die Virtual Beacon-DLL kopiert werden.
Einige Evasionsfunktionen beim Laden von Abschnitten sind:
Im öffentlichen BokuLoader-Projekt werden die Header der Beacon-DLL nicht von der Raw Beacon-DLL in die Virtual Beacon-DLL kopiert. Derzeit bestehen die ersten
Eine weitere mögliche Evasionsmaßnahme besteht darin, dass das UDRL-Aggressor-Skript die Abschnitte verschlüsselt. Diese Abschnitte können anschließend im Speicher durch die UDRL entschlüsselt werden, wobei ein zwischen der UDRL und dem UDRL-Aggressor-Skript geteilter Schlüssel verwendet wird.
Das x64-HTTP/S-Beacon benötigt vier DLLs, um richtig zu funktionieren. Falls diese DLLs aktuell nicht in den Prozess geladen sind, muss unser Reflective Loader sie laden.
Die vier DLLs sind im Importverzeichnis der HTTP/S-Beacon-DLL aufgeführt:
Screenshot von PE-Bear, der die DLLs aus dem Import-Verzeichnis der Beacon-DLL auflistet.
Der integrierte Cobalt Strike Reflective Loader verwendet die API kernel32.LoadLibraryA zum Laden von DLLs.
Das Laden von DLLs kann auf unterschiedliche Arten erfolgen, wobei jeweils verschiedene Aspekte der operativen Sicherheit zu berücksichtigen sind. Einige dieser Methoden sind:
Wenn die DLL bereits im Prozess vorhanden ist, können die oben genannten Windows-APIs weiterhin verwendet werden, um die Basisadressen der DLL zu ermitteln. Dies kann jedoch unerwünschte Erkennungswarnungen auslösen.
Alternativ enthält der PEB einen Zeiger auf die
<a title="https://learn.microsoft.com/de-de/windows/win32/api/winternl/ns-winternl-peb_ldr_data" href="https://learn.microsoft.com/de-de/windows/win32/api/winternl/ns-winternl-peb_ldr_data">_PEB_LDR_DATA</a>
-Struktur. Diese enthält eine verknüpfte Liste aller im Prozess geladenen DLLs sowie deren zugehörigen Informationen (
). BokuLoader nutzt dies, um DLL-Informationen zu ermitteln und unnötige API-Aufrufe zu vermeiden.
Ist die DLL nicht im vorhanden
Verschachteltes reflektierendes Laden eignet sich hierfür nicht ohne Weiteres, da reflektierende Loader DLLs in der Regel nicht beim Prozess registrieren. Code außerhalb der DLL kann eine reflektierend geladene DLL daher nicht korrekt verwenden. Das DarkLoadLibrary-Projekt könnte in der Lage sein, eine DLL korrekt in den Speicher zu laden, ohne ein Kernel-Image-Load-Event auszulösen.
Codebeispiel aus dem BokuLoader-Projekt, das zeigt, wie die Basisadressen geladener DLLs durch Durchlaufen der InMemoryOrderModuleList aufgelöst werden.
Nachdem die erforderlichen DLLs in den Prozess geladen wurden, müssen die im Importverzeichnis aufgeführten APIs aufgelöst werden. Anschließend müssen die API-Adressen in die Import Address Table (IAT) der Virtual Beacon-DLL geschrieben werden. Auf diese Weise weiß Beacon, zu welcher Adresse gesprungen werden muss, wenn APIs wie die folgende aufgerufen werden:
Der Importeintrag muss dabei entweder über die Ordinalzahl oder die Zeichenfolge aufgelöst werden.
Im folgenden Bild sehen wir, dass die Cobalt-Strike-Beacon-DLL eine Kombination aus Ordinalzahlen und Namenszeichenfolgen für Importeinträge verwendet:
Screenshot von PE-Bear, der zeigt, dass einige Importeinträge der Beacon-DLL über Ordinalzahlen aufgelöst werden müssen.
Der integrierte Cobalt Strike Reflective Loader nutzt eine
Einige Evasionsmethoden zur Auflösung von API-Adressen sind:
GetProcAddress
NTDLL.LdrGetProcedureAddress
BokuLoader verwendet eine benutzerdefinierte Code-Implementierung vonGetProcAddress
Die ist ebenfalls in der Lage, sowohl Namenszeichenfolgen als auch Ordinalzahlen zu verarbeiten. Wenn die für den Import-Eintrag zurückgegebene Adresse eine Weiterleitung auf eine andere DLL darstellt, verwendet BokuLoader standardmäßig ,
Beim Schreiben der IAT kann Hooking implementiert werden, indem die virtuellen Adressen der implementierten Hook-Funktionen anstelle der vorgesehenen virtuellen Adressen der APIs angegeben werden. Solange die erwartete Ausgabe an den Beacon zurückgegeben wird, wenn die Adresse in der IAT aufgerufen wird, können wir zusätzlichen Code ausführen, bevor wir zum Beacon zurückkehren. Zukünftige Beiträge und öffentliche BokuLoader-Veröffentlichungen werden zeigen, wie wir IAT-Hooking für fortschrittliche Evasionsfunktionen nutzen können.
Mit einer kürzlich veröffentlichten Version unterstützt das öffentliche BokuLoader-Projekt die Malleable-PE-Funktion aus dem Cobalt Storm C2-Profil mit einer benutzerdefinierten Implementierung. Durch Ändern des Maskierungsschlüssels imBokuLoader.cna
Hinsichtlich der operativen Sicherheit ist zu beachten, dass Pattern-Matching-Engines in der Lage sind, Single-Byte-XOR-Masken durch Brute-Force zu ermitteln. Zukünftige Beiträge werden zeigen, wie wir mithilfe der Aggressor-Skripting-Funktionalität von Cobalt Strike unsere eigene Malleable-PE-Engine erstellen können, um Beacon zu obfuskieren und so Pattern-Matching zu umgehen.
Die Beacon-DLL enthält zahlreiche Relokationen, die vor ihrer Ausführung aufgelöst und in die Base Relocation Table der Virtual Beacon-DLL geschrieben werden müssen.
In PE-Bear können wir sehen, dass die Beacon-DLL standardmäßig eine Image-Basisadresse besitzt:
Screenshot aus PE-Bear, zeigt die Basisadresse der Beacon-DLL.
Bevor wir mit dem Schreiben von Relocationen beginnen, müssen wir das Delta zwischen der Basisadresse der Virtual Beacon-DLL und der fest codierten Basisadresse berechnen.
Nehmen wir beispielsweise an, die Basisadresse der Virtual Beacon-DLL sei
Als Nächstes fügen wir zur Bestimmung der virtuellen Adresse für jeden Relocation-Eintrag in der Basis-Relocation-Tabelle das Delta der Basisadresse zur fest codierten Relocation-Eintragsadresse hinzu, um die Relocation innerhalb unserer Virtual Beacon-DLL zu bestimmen.
In der folgenden Abbildung ist zu sehen, dass die Relocation-Einträge des Beacons rückwärts im Little-Endian-Format geschrieben sind:
Screenshot aus PE-Bear, der zeigt, dass einige Relocation-Einträge im Little-Endian-Format vorliegen.
Die fest codierte Adresse für diesen Relocation-Eintrag lautet:
Wir addieren diese Adresse zum Delta der Basisadresse, um die virtuelle Adresse der Relocation zu erhalten, wie sie in der Virtual Beacon-DLL existiert:
Für jeden Relocation-Eintrag müssen wir überprüfen, ob der Typ ist
<a title="https://learn.microsoft.com/de-de/windows/win32/debug/pe-format" href="https://learn.microsoft.com/de-de/windows/win32/debug/pe-format">IMAGE_REL_BASED_DIR64 (0xA)</a>
. Falls dies nicht zutrifft, überspringen wir das Schreiben der Relocation.
Sobald wir die virtuelle Adresse der Relocation in der Virtual Beacon-DLL bestimmt haben, schreiben wir sie in den Speicher, in dem die fest codierte Relocation-Eintragsadresse gespeichert ist.
Wenn Sie mehr darüber erfahren möchten, wie Sie PE-Relocations durchführen, sehen Sie sich den Code der Funktion doRelocations im öffentlichen BokuLoader-Projekt an. Bevor ich diesen Blogbeitrag veröffentlichte, habe ich den Relokationscode von Assembler in hoffentlich gut lesbaren C-Code geändert, um anderen beim Verständnis der technischen Details dieser Vorgehensweise zu helfen.
Die Ausführung des Beacons lässt sich in drei Schritte unterteilen:
Wenn der Speicher, den wir für unsere virtuelle Beacon-DLL zugewiesen haben, ist,
Wenn wir den Speicher für unsere Virtual Beacon als nicht ausführbar zugewiesen haben (
Im öffentlichen BokuLoader-Projekt werden Änderungen der Speicherschutzmechanismen durch direkte Systemaufrufe an
Codebeispiel aus dem BokuLoader-Projekt, das das Ändern des .text -Abschnitts der Virtual Beacon-DLL ausführbar demonstriert.
Die
Damit die Virtual Baken-DLL ordnungsgemäß ausgeführt werden kann, muss sie zunächst initialisiert werden, indem der Entry Point der Virtual Beacon-DLL aufgerufen wird. Das erste Argument ist die Basisadresse der Virtual Beacon-DLL. Das zweite Argument ist
Codebeispiel aus dem BokuLoader-Projekt zur Initialisierung der Virtual Beacon-DLL.
Nach der Initialisierung der Virtual Beacon-DLL können wir entweder den Entry Point des Virtual Beacons an den Call Reflective Loader Stub zurückgeben oder den Entry Point der Virtual Beacon-DLL in unserer UDRL mit
Im Gegensatz zu einer typischen DLL, bei der das erste Argument von
<a href="https://learn.microsoft.com/de-de/windows/win32/dlls/dllmain">DLLMAIN</a>
die Basisadresse der Virtual DLL wäre, erwartet Beacon die Basisadresse der Raw Beacon-DLL. Wenn diese nicht bereitgestellt wird, können einige Malleable-PE-Evasionsfunktionen fehlschlagen.
Codebeispiel aus dem BokuLoader-Projekt, das zwei verschiedene Möglichkeiten zur Ausführung der Virtual Beacon-DLL zeigt.
Ich hoffe, dieser Blogbeitrag hilft sowohl roten als auch blauen Teams dabei, Cobalt Strike und den Reflective-Loading-Prozess besser zu verstehen. Es gibt nach wie vor zahlreiche Evasionsmöglichkeiten, die durch Reflective Loading umgesetzt werden können. Mit einem tieferen Verständnis dieser Konzepte können sich Unternehmen besser auf eine erfolgreiche Verteidigung gegen Cyberbedrohungen vorbereiten.
Zukünftige Beiträge dieser Reihe werden sich auf die Integration des UDRLs mit den aktuellen Evasionsfunktionen von Cobalt Strike konzentrieren, auf undokumentierte Evasionsfunktionen eingehen, die bereits im öffentlichen BokuLoader vorhanden sind, sowie auf fortgeschrittene Funktionen, die bislang nicht öffentlich veröffentlicht wurden. Bleiben Sie dran, um detailliertere Informationen und Techniken zu erhalten und zu lernen, wie Sie Ihr Cobalt-Strike-Niveau mit der UDRL-Entwicklung auf die nächste Stufe bringen können!