Definition des Cobalt Strike Reflective Loaders

Mann mit schwarzem Kapuzenpullover, der auf der Straße einen Laptop nutzt – Darstellung eines Hackers

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:

  • Kundenspezifischen internen Tools.
  • Kundenspezifischen internen Loadern.
  • Einem kundenspezifischen internen C2-Framework.
  • Wir investieren weiterhin in die fortlaufende Weiterentwicklung und Tarnung alternativer C2-Frameworks von Drittanbietern.

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.

Die neuesten Tech-News – von Experten bestätigt

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.

Vielen Dank! Sie haben sich angemeldet.

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.

Aufbauend auf dem Framework mit Reflective Loadern

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:

  • Die Probleme beim Laden eines C2-Implantats von der Festplatte mit dem Windows DLL Loader.
  • Die Konzepte und Mechanismen des Reflective-Loading-Prozesses von Cobalt Strike.
  • Die Designanforderungen für den Bau eines effektiven Reflective Loaders.
  • Die Phasen, die am Reflective-Loading-Prozess beteiligt sind.

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.

Laden der Beacon-DLL

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.

Der integrierte Windows DLL-Loader

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:

  • Die Raw DLL muss im Dateisystem vorhanden sein.
  • Die unformatierte DLL-Datei muss frei von Obfuskation sein.
  • Kernel-Image-Load-Events werden durch den Windows DLL-Loader ausgelöst.

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:

  1. Vermeidung signierter Malware auf dem Dateisystem.
  2. Vermeidung von Kernel-Image-Load-Events, die von Security-Lösungen überwacht werden.
  3. Vermeidung der Auflistung der C2-Implantat-DLL im Process Environment Block (PEB).

Reflective Loader vs. Windows DLL-Loader

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.

Raw-Dateiformat vs. Virtual Address Format

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:

  • Dies ist das Format der DLL, wie sie auf einem Dateisystem vorliegt.
  • Die Abschnitte der DLL sind dicht beieinander gepackt.
  • Die Offsets basieren auf dem Start der Raw DLL-Datei, wie sie auf der Festplatte existiert.
  • Dieses Format benötigt weniger Speicherplatz.

Virtual Address Format:

  • Dies ist das Format der DLL, wie sie im virtuellen Speicherbereich eines Prozesses vorliegt.
  • Die Abschnitte sind mit Abständen angeordnet.
  • Die Offsets liegen als Relative Virtual Addresses (RVA) vor.
  • Während der Ausführung eines Prozesses bestimmen die DLL und andere Module ihre Positionen über RVAs.
  • Dieses Format benötigt mehr Speicherplatz.

Raw Beacon vs. virtuelles Beacon

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.

Tabelle mit Auflistung der Raw- und Virtual-Adressen der einzelnen Abschnitte der Beacon-DLL.

Diese HTTP/S-Beacon-DLL ist 0x52000  () Byte327KB groß, wenn sie in den virtuellen Speicherplatz eines Prozesses geladen wird, verglichen mit 0x44000  Bytes (272KB () Byte, wie sie im Dateisystem existiert. Dieser Größenunterschied ist darauf zurückzuführen, dass die Abschnitte im Virtual-Address-Format weiter auseinanderliegen als im Raw-Dateiformat.

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 im Vergleich zum Virtual-Address-Format

Visuelle Darstellung der Beacon-DLL im Raw-Dateiformat (links) im Vergleich zum Virtual Address Format (rechts)

Beacon wird mit dem Windows DLL-Loader geladen.

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:

  1. Generieren einer Raw Beacon-DLL ohne Obfuskation.
  2. Erstellen eines Programms, das:
    1. Eine LoadLibrary API verwendet, um die Beacon-DLL von der Festplatte zu laden.
    2. Die Beacon-DLL durch Aufruf ihres Entry Points ausführt.
  3. Das ausführbare Programm und die Beacon-DLL im selben Ordner platziert.
  4. Ausführen des Programms.

Generieren einer Raw Beacon-DLL ohne Obfuskation

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.

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 no_evasion.profile  Profil als Argument.

Screenshot für den Blogbeitrag erstellt

Anschließend verbinden wir uns mit dem Team Server über den Cobalt Strike Client. Danach erstellen wir eineWindows Stageless Payload  Raw-, stageless-Beacon-DLL, wobei die Ausgabeoption auf „Raw“ gesetzt ist und der entsprechende Listener ausgewählt wird.https Die Payload wird anschließend gespeichert:beacon.dll .

Screenshot von der Erstellung einer „raw stageless“ Beacon-DLL im Cobalt Strike Client

Screenshot von der Erstellung einer „raw stageless“ Beacon-DLL im Cobalt Strike Client

Erstellung unseres Beacon DLL-Loader-Programms

Mit dem folgenden Code erstellen wir ein C-Programm mit dem Namen loadBeaconDLL.c  und kompilieren es:

Der Code lädt die Raw-Beacon-DLL mithilfe des Windows-DLL-Loaders von der Festplatte.

Der Code lädt die Raw-Beacon-DLL mithilfe des Windows-DLL-Loaders von der Festplatte.

Wir verwenden die Kernel32.LoadLibraryA  entsprechende API, um unsere Raw Beacon-DLL von der Festplatte zu laden. Diese API ruft den in Windows integrierten DLL-Loader auf, der die Beacon-DLL in den virtuellen Speicherbereich unseres Host-Prozesses lädt.

Im Rahmen des Ladevorgangs initialisiert der Windows-DLL-Loader unsere Beacon-DLL, indem er ihren Entry Point mit demDLL_PROCESS_ATTACH (1)  entsprechenden Argument aufruft.

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 0x4.

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: 0x1D840 :

Screenshot der Suche nach der RVA des Entry Points der Beacon-DLL mithilfe von PE-Bear

Screenshot der Suche nach der RVA des Entry Points der Beacon-DLL mithilfe von PE-Bear

Der LoadLibraryA  Die API gibt die Base Adress der virtuellen Beacon-DLL zurück. Diese wird zum Entry-Point-RVA addiert, um die Adresse des Entry Points zu bestimmen.

Mit dem fertigen Code kompilieren wir unser C-Programm zu einer Windows-Executable:

Der Befehl, der zum Kompilieren unseres Programms verwendet wurde:

Der Befehl, der zum Kompilieren unseres Programms verwendet wurde:

Positionierung unseres Programms und der Beacon-DLL im Dateisystem

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 beacon.dll  und loadBeaconDLL.exe  Komponenten im Dateisystem innerhalb desselben Verzeichnisses:

Die Beacon-DLL und das Loader-Programm befinden sich im selben Verzeichnis.

Die Beacon-DLL und das Loader-Programm befinden sich im selben Verzeichnis.

Ausführen unseres Programms

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.

Erfolgreiche Verbindung zum C2-Team-Server über eine Beacon-DLL, die mit dem Windows DLL-Loader geladen wurde.

Cobalt Strike Reflective Loading

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.

Überlegungen zur Verwendung von UDRL

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 obfuscate von der UDRL selbst behandelt werden, während andere wie sleepmask und cleanup durch die Beacon-DLL mit entsprechender UDRL-Integration gehandhabt werden können.

Reflective-Loading-Methoden

Ursprüngliche Reflective-Loader-Methode

Das ursprüngliche Reflective-Loader-Projekt erfordert, dass die ReflectiveLoader-Funktion in das DLL-Projekt kompiliert ReflectiveLoader  und innerhalb der C2-Implantat-DLL exportiert wird.

Ein weiteres Projekt ist verantwortlich für:

  1. Die Ermittlung der virtuellen Adresse desReflectiveLoader  Exports.
  2. Die Ausführung des ReflectiveLoader  Exports, der den Entry Point unserer geladenen DLL zurückgibt.
  3. Den Aufruf des Entry Points der reflektiert geladenen DLL.
Diagramm des ursprünglichen Reflective Loaders beim Laden einer DLL in den virtuellen Speicher.

Diagramm des ursprünglichen Reflective Loaders beim Laden einer DLL in den virtuellen Speicher.

Vorangestellte Reflective-Loader-Methode

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.

Diagramm eines vorangestellten Reflective Loaders, der eine DLL in den virtuellen Speicher lädt.

Die Reflective-Loader-Methode von Cobalt Strike

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 ReflectiveLoader  die Funktion innerhalb der ursprünglichen Beacon-DLL kompiliert und exportiert. Wenn ein Operator eine Beacon-Payload vom Cobalt Strike-Client generiert, patcht die Malleable PE Engine von Cobalt Strike die rohe Beacon-DLL, um den Reflective Loader über die zu verwendenden Malleable PE-Optionen zu informieren. Der DOS-Header von Beacon wird gepatcht, um denReflectiveLoader -Export an einem fest kodierten Offset aufzurufen. Die initialen gepatchten Bytes des DOS-Headers von Beacon, die den ReflectiveLoader  den Export aufrufen, werden in diesem Blog als „Call Reflective Loader Stub“ bezeichnet.

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 desReflectiveLoader  -Exports ein.

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.

Diagramm des Cobalt Strik Reflective Loaders beim Laden der Beacon-DLL in den virtuellen Speicher.

Call Reflective Loader Stub

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.

Der Call Reflective Loader Stub wird als ausführbare Assembly-Operationscodes angezeigt.

Die ersten Bytes MZAR  sind über die Malleable-PE-Optionen im Cobalt-Strike-C2-Profil konfigurierbar. Diese Bytes müssen ausführbar sein und dürfen keine Operation auslösen (nop ).

Nach der Ausführung optional vorangestellter nops  und magischer Bytes, führt der Call Reflective Loader Stub folgende Schritte aus:

  • Er erzeugt einen Stack-Frame.
  • Er verwendet RIP-relative Adressierung, um die Basisadresse der Raw Beacon-DLL zu bestimmen.
  • Er ruft den ReflectiveLoader -Export im bekannten 0x16E3C  Raw-File-Offset auf.
  • Er ruft den Entry Point der geladenen Beacon-DLL auf.

Wir bestätigen den Raw-File-Offset des ReflectiveLoader -Exports 0x16E3C  anhand des Exportverzeichnisses der Beacon-DLL:

Screenshot der Verwendung von PE-Bear zur Bestimmung des Raw-File-Offsets des Reflective-Loader-Exports.

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 ReflectiveLoader -Exports auf die Beacon-DLL in ihrem virtuellen Zustand. Da der ReflectiveLoader -Export ausführbar ist, wissen wir, dass er sich innerhalb des .text -Abschnitts der Beacon-DLL befindet.

Um den Raw-File-Offset des ReflectiveLoader -Exports zu bestimmen, wir müssen die Differenz zwischen den virtuellen und den Raw-Adressen der .text -Abschnitte kennen. Da wir die Differenz kennen, können wir sie einfach von der RVA des ReflectiveLoader -Export substrahieren, um den Raw-File-Offset des ReflectiveLoader -Exports zu ermitteln.

Die virtuellen und Raw-Adressen des .text -Abschnitts sind in den Section-Headern der Beacon-DLL aufgeführt:

Roh- und virtuelle Adressen des .text -Abschnitts der Beacon-DLL.

Roh- und virtuelle Adressen des .text -Abschnitts der Beacon-DLL.

Der Unterschied zwischen den beiden beträgt 0xC00  Bytes. Durch Subtraktion der ReflectiveLoader  Differenz vom RVA des 0x17A3C  ergibt sich der Raw-File-Offset des 0x16E3C .

Dies lässt sich in PE-Bear bestätigen, indem man mit der rechten Maustaste auf ReflectiveLoader  Export-RVA klicken. Follow RVA:17A3C Der Hex-Viewer im obigen Widget springt zur Anzeige des ReflectiveLoader -Exports an der Position des Raw-File-Offsets.

Zusammenfassung des Reflective-Loading-Prozesses von Cobalt Strike:

  • Ein Thread führt die Raw Beacon-DLL aus.
  • Der Call Reflective Loader Stub ruft denReflectiveLoader -Export an einem bekannten Raw-File-Offset auf.
  • Der Reflective Loader lädt die Beacon-DLL in den virtuellen Speicher des Host-Prozesses.
  • Nach dem Laden gibt der Reflective Loader den Entry Point der virtuellen Beacon-DLL an den Call Reflective Loader Stub zurück.
  • Der Call Reflective Loader Stub ruft den Entry Point der virtuellen Beacon-DLL auf.
Diagramm, das die Hauptphasen des Reflective Loadings der Beacon-DLL durch Cobalt Strike zeigt.

Diagramm, das die Hauptphasen des Reflective Loadings der Beacon-DLL durch Cobalt Strike zeigt.

Anforderungen an die Konstruktion des Reflective Loaders:

Positionsunabhängiger Code

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 text abschnitt der Objektdatei enthalten sein. Abschließend wird dieser Abschnitt extrahiert,.text um den Shellcode des Reflective Loaders zu erhalten.

Einfügen unserer UDRL in Cobalt Strike

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 ReflectiveLoader -Exports. Dies erfolgt über ein UDRL-Aggressor-Skript, wie unten dargestellt:

Aggressor-Skript zum Schreiben des Reflective-Loader-Shellcodes in die Raw Beacon-DLL mit Cobalt Strike.

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:

  • Wir öffnen eine$handle Verbindung zu unserer UDRL-Objektdatei.openf getätigt.
  • Mit dieser Datei$handle lesen wir den Bytestream und speichern ihn in der$data Byte-Array-Variable.
  • Danach schließen wir die Datei$handle mitclosef getätigt.
  • Die integrierte
    <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$data Byte-Array, lokalisiert den.text Abschnitt aus unserer UDRL-Objektdatei extrahiert den .text- Abschnitt und speichert ihn in der$loader Byte-Array-Variable.
  • Die integrierte
    <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 unseresReflectiveLoader -Exports zu ermitteln und unseren UDRL-Shellcode zu patchen.$loader Byte-Array-Variable.
  • Schließlich geben wir die modifizierte Beacon-DLL an Cobalt Strike zurück und speichern unsere Datei über den Client.

Reflective-Loading-Phasen

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:

  1. Raw Beacon-DLL finden
  2. Beacon-DLL-Header parsen
  3. Speicher für Virtual Beacon-DLL zuweisen
  4. Abschnitte in den virtuellen Speicher laden
  5. DLL-Abhängigkeiten laden
  6. Importable Adress Table auflösen
  7. Relocations bewältigen
  8. Beacon ausführen

Phase 1: Ermitteln der Basisadresse der Raw Beacon-DLL

Es gibt mehrere Methoden, mit denen wir die Adresse für die Raw Beacon-DLL im Speicher ermitteln können. Einige dieser Methoden sind:

  • Rückwärtssuche nach MZ & PE Headern
  • Rückwärtssuche nach einem Egg
  • Ermitteln der Raw-Beacon-DLL-Basisadresse aus dem Call Reflective Loader Stub

Unsere Position im Speicher ermitteln

Wenn wir eine Methode verwenden, die rückwärts sucht, müssen wir zunächst die aktuelle Adresse des Instruction Pointers unseres Threads ermitteln (RIP ). Hierfür können wir einen einfachen Trick verwenden getRip :

  1. In unserer UDRL erstellen wir eine Funktion namens getRip .
  2. Wir rufe getRip  auf, wodurch die Adresse nach dem „call getRip " auf den Stack gelegt wird. Dies ist die Rücksprungadresse.
  3. In unserer getRip  -Funktion kopieren wir anschließend die Rücksprungadresse an den Aufrufer vom oberen Ende des Stacks zurück.
  4. In der x64-Windows-C-Implementierung können Funktionen einen Wert zurückgeben. Dieser Rückgabewert wird dem Aufrufer über ein RAX  -Register zurückgegeben. Durch Verschieben der Rücksprungadresse des Aufrufers in das RAX  -Register geben wir die Rücksprungadresse an den Anrufer zurück.
Intel-x64-Assembler-Code zum Ermitteln der Basisadresse der Raw Beacon DLL aus dem RDI-Register.

Intel-x64-Assembler-Code zum Ermitteln der Basisadresse der Raw Beacon DLL aus dem RDI-Register.

Rückwärtssuche nach MZ- und PE-Headern

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 magic_mz  und magic_pe  Malleable-PE-Evasion-Funktionen eingeführt.

Die Cobalt-Strike-Dokumentation beschreibt hierzu folgende magic_mz  Option:

  • „Überschreibt die ersten Bytes (einschließlich des MZ-Headers) der Reflective DLL von Beacon. Es sind gültige Instruktionen erforderlich. Instruktionen, die den CPU-Zustand verändern, müssen durch Instruktionen ergänzt werden, die diese Änderung rückgängig machen.“

Wenn diese Option konfiguriert ist, sind MZ--  -Bytes am Raw-File-Offset 0x00  und die PE00  -Bytes am Raw-File-Offset  0x80  dem Reflective Loader bekannt. Sie werden von der Malleable PE-Engine in die Beacon-DLL gepatcht.

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 0x00  sind nicht zulässig, da Beacon abstürzen kann. Dies stellt einen potenziellen Erkennungspunkt dar.

Rückwärtssuche nach einem Egg

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 RIP sucht, zwei identischen Instanzen eines eindeutigen 64-Bit-Eggs an einem bekannten beacon.dll+0x50  Raw-File-Offset.

Die Adresse beacon.dll+0x50  wurde gewählt, weil sich hier der Hinweis „Dieses Programm kann nicht im DOS-Modus ausgeführt werden“ befindet, der beim Reflective Loading von Beacon nicht benötigt wird.

Da wir keinen einfachen Zugriff auf die Java Malleable PE-Engine haben, kann das BokuLoader.cna  UDRL-Aggressor-Skript verwendet werden, um das 0xB0C0ACDC  Egg in die Beacon zu schreiben. Der folgende Code zeigt, wie die Raw Beacon-DLL so modifiziert werden kann, dass sie das Egg enthält:

Aggressor-Skript zum Schreiben eines Eggs in die Raw Beacon-DLL und zur Anzeige der Änderungen in der Cobalt Strike-Skriptkonsole.

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.

Intel-x64-Assembler-Code für einen Egg-Hunter, der rückwärts nach zwei Instanzen eines 64-Bit-Eggs sucht.

  • Sowohl das UDRL-Aggressor-Skript als auch der UDRL-C-Code können angepasst werden, um unterschiedliche Eggs zu verwenden.

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.

Aggressor-Skript zum Maskieren von MZ-, PE- und ungenutzten Bytes des DOS-Banners in den Headern der Raw Beacon-DLL.

Ermitteln der Raw-Beacon-DLL-Basisadresse über den Call Reflective Loader Stub

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 RDI  -Registrier, bevor der Reflective Loader aufgerufen wird. Anstatt rückwärts von RIP nach einem Egg zu suchen, können wir den Wert einfach aus dem RDI  -Register zu Beginn unseres Reflective-Loader-Codes auslesen.

Um dies im Debugger weiter zu untersuchen, generieren wir ein Beacon, stellen einen Breakpoint voran (0xCC ), und öffne den Beacon in x64dbg. Da der Breakpoint vorangestellt ist, befindet sich die Basisadresse des Raw Beacons bei +1  des zugewiesenen Speichers. Wie zuvor gezeigt, verwendet der Call Reflective Loader StubRIP relative Adressierung zur Ermittlung der Basisadresse der Raw Beacon-DLL:

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.

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.

Inline-Assembly-C-Code zum Ermitteln der Raw-Beacon-DLL-Basisadresse aus dem RDI-Register.

Phase 2: Parsen der Header der Beacon-DLL

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.

Tabelle mit Werten aus den Headern der Raw Beacon-DLL, die für das Laden der Beacon-DLL relevant sind.

Evasionstechniken

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.

Phase 3: Speicherzuweisung für Virtual Beacon

Sobald wir wissen,SizeOfImagef welche Größe aus dem Header der Raw Beacon-DLL erforderlich ist, müssen wir Speicher in dieser Größe zuweisen. Dieser Speicherraum wird unsere Virtual Beacon-DLL aufnehmen.

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.

Tabelle mit den Speicherzuweisungsoptionen von Cobalt Strike für die Virtual Beacon-DLL.

Evasionstechniken

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 VirtualAlloc  im Cobalt Strike Malleable-C2-Profil eingestellt ist, verwendet das BokuLoader-Projekt derzeit einen direkten Systemaufruf, um NtAllocateVirtualMemory  Speicher für die Virtual Beacon-DLL zuweisen:

Ein Codebeispiel aus dem BokuLoader-Projekt, das zeigt, wie ein direkter Systemaufruf zur Speicherzuweisung für die Virtual Beacon-DLL verwendet wird.

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.

Codebeispiel aus dem BokuLoader-Projekt, das zeigt, wie Systemaufrufe aus dem Prozess ermittelt werden.

Phase 4: Laden von Abschnitten in den virtuellen Speicher

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 zugewiesen haben, müssen wir die Adresse des .text -Abschnitts und dessen Größe verfolgen. Vor dem Aufrufen des Entry Points der Virtual Beacon-DLL müssen wir den Speicherschutz des .text -Abschnitts auf ausführbar setzen.

Die Zuweisung unseres Speichers mit READWRITE_EXECUTE vereinfacht zwar den Reflective-Loading-Prozess, erhöht jedoch die Wahrscheinlichkeit der Erkennung durch Sicherheitslösungen.

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.

Codebeispiel aus dem BokuLoader-Projekt, das zeigt, wie Abschnitte aus der Raw Beacon-DLL in die Virtual Beacon-DLL kopiert werden.

Evasionstechniken

Einige Evasionsfunktionen beim Laden von Abschnitten sind:

  • Die Beacon-Header werden nicht in die Virtual Beacon-DLL kopiert.
  • Freigabe des Speicherplatzes in der Virtual Beacon-DLL, in dem sich die Header befinden würden.

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 0x1000  Bytes der Virtual Beacon-DLL aus Nullwerten (0x00‘s ). Meinen Tests zufolge ist Beacon nicht mehr von seinen Headern abhängig, sobald Beacon ordnungsgemäß in den virtuellen Speicher geladen wurde. Das Vermeiden des Kopierens der Header kann zwar helfen, In-Memory-Scanner zu umgehen, aber diese Nullbytes können jedoch ebenfalls einen potenziellen Erkennungspunkt darstellen.

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.

Phase 5: Laden der DLL-Abhängigkeiten

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.

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.

Evasionstechniken

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 (

InMemoryOrderModuleList

). BokuLoader nutzt dies, um DLL-Informationen zu ermitteln und unnötige API-Aufrufe zu vermeiden.

Ist die DLL nicht im vorhandenInMemoryOrderModuleList , verwendet BokuLoader derzeit die NTDLL.LdrLoadDll  -API um die DLL-Abhängigkeit unter Nutzung des integrierten Windows DLL Loaders in den Speicher zu laden.

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.

Codebeispiel aus dem BokuLoader-Projekt, das zeigt, wie die Basisadressen geladener DLLs durch Durchlaufen der InMemoryOrderModuleList aufgelöst werden.

Phase 6: Auflösung der Import Address Table

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: WININET.HttpSendRequest

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.

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 Kernel32.GetProcAddress  -API zur Auflösung der virtuellen Adressen für Importeinträge.

Evasionstechniken

Einige Evasionsmethoden zur Auflösung von API-Adressen sind:

  • Benutzerdefinierte Code-Implementierungen von GetProcAddress
  • NTDLL.LdrGetProcedureAddress

BokuLoader verwendet eine benutzerdefinierte Code-Implementierung vonGetProcAddress um die Adresse für den Importeintrag aufzulösen, wobei sowohl Namenszeichenfolgen als auch Ordinalzahlen verarbeitet werden.

Die NTDLL.LdrGetProcedureAddress 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 ,NTDLL.LdrGetProcedureAddress um Weiterleitung aufzulösen.

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 obfuscate Malleable-PE-Funktion aus dem Cobalt Storm C2-Profil mit einer benutzerdefinierten Implementierung. Durch Ändern des Maskierungsschlüssels imBokuLoader.cna -UDRL Aggressor-Skript, kann die Obfuskation verbessert werden, indem ein eigener Single-Byte-XOR-Schlüssel gewählt wird.

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.

Phase 7: Auflösung der Relokationen

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: 0x180000000 :

Screenshot aus PE-Bear, zeigt die Basisadresse der Beacon-DLL.

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 0x7FFC44FE0000 . Wir ziehen die fest codierte Basisadresse von der Basisadresse unserer Virtual Beacon-DLL ab, um das Delta der Basisadresse zu erhalten:

Screenshot zum Abrufen des Deltas der Basisadresse

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.

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: 0x1800341C8 .

Wir addieren diese Adresse zum Delta der Basisadresse, um die virtuelle Adresse der Relocation zu erhalten, wie sie in der Virtual Beacon-DLL existiert:

Screenshot zum Hinzufügen der Adresse zum Delta der Basisadresse, um die virtuelle Adresse der Relocation zu erhalten, wie sie in der Virtual Beacon-DLL vorhanden ist:

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.

Phase 8: Ausführung des Beacons

Die Ausführung des Beacons lässt sich in drei Schritte unterteilen:

  • Sicherstellen, dass die Abschnitter der Virtual Beacon-DLL über die korrekten Speicherberechtigungen verfügen.
  • Initialisierung der Virtual Beacon-DLL.
  • Aufruf des Entry Points der Virtual Beacon-DLL.

Virtual Beacon ausführbar machen

Wenn der Speicher, den wir für unsere virtuelle Beacon-DLL zugewiesen haben, ist,READWRITE_EXECUTE müssen wir die Speicherschutzmaßnahmen nicht ändern, damit Beacon ordnungsgemäß funktioniert, ohne abzustürzen.

Wenn wir den Speicher für unsere Virtual Beacon als nicht ausführbar zugewiesen haben (READWRITE ), müssen wir den .text  -Abschnitt unserer Virtual Beacon-DLL auf ausführbar setzen. Der Speicherort und die virtuelle Größe des .text  -Abschnitts sollten zuvor in unserer UDRL-Hauptfunktion als Variable gespeichert worden sein.

Im öffentlichen BokuLoader-Projekt werden Änderungen der Speicherschutzmechanismen durch direkte Systemaufrufe an NTProtectVirtualMemory durchgeführt, wie im folgenden Codebeispiel zu sehen ist:

Codebeispiel aus dem BokuLoader-Projekt, das das Ändern des .text -Abschnitts der Virtual Beacon-DLL ausführbar demonstriert.

Codebeispiel aus dem BokuLoader-Projekt, das das Ändern des .text -Abschnitts der Virtual Beacon-DLL ausführbar demonstriert.

Die .data  Ein bestimmter Abschnitt unserer Virtual Beacon-DLL sollte die -Berechtigungen besitzenREADWRITE . Wenn der Abschnitt nicht beschreibbar ist, kann unsere Beacon-DLL während der Ausführung abstürzen.

Initialisierung der Virtual Beacon-DLL

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 fwdReason  und sollte auf gesetzt werdenDLL_PROCESS_ATTACH (1) .

Codebeispiel aus dem BokuLoader-Projekt zur Initialisierung der Virtual Beacon-DLL.

Codebeispiel aus dem BokuLoader-Projekt zur Initialisierung der Virtual Beacon-DLL.

Ausführung unserer 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 fwdReason  aufrufen 0x4 .

Im Gegensatz zu einer typischen DLL, bei der das erste Argument vonhinstDLL  Zu 

<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.

Codebeispiel aus dem BokuLoader-Projekt, das zwei verschiedene Möglichkeiten zur Ausführung der Virtual Beacon-DLL zeigt.

Schlussgedanken

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!

Mixture of Experts | 12. Dezember, Folge 85

KI entschlüsseln: Wöchentlicher Nachrichtenüberblick

Schließen Sie sich unserer erstklassigen Expertenrunde aus Ingenieuren, Forschern, Produktführern und anderen an, die sich durch das KI-Rauschen kämpfen, um Ihnen die neuesten KI-Nachrichten und Erkenntnisse zu liefern.