Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
Inline-Execute-PE — Führen Sie nicht verwaltete Windows-Executables in CobaltStrike Beacons aus. | Kitploit
Tools/GitHubGitHub/octoberfest7/inline-execute-pe
Privilege Escalation
GitHuboctoberfest7/inline-execute-pe

Inline-Execute-PE

Führen Sie nicht verwaltete Windows-Executables in CobaltStrike Beacons aus.

Repository anzeigen
723103vor 3 JahrenVon Kitploit geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

Inline-Execute-PE

HAFTUNGSAUSSCHLUSS:

Dieses Projekt ist komplex, und wenn Sie nicht verstehen, wie es funktioniert, und es nicht ausreichend testen, kann dies dazu führen, dass Sie Beacons zum Absturz bringen und den Zugriff verlieren!

Ich empfehle Ihnen dringend, die gesamte Dokumentation bis zum Abschnitt "Design Considerations and Commentary" zu lesen!

Einführung

Inline-Execute-PE ist eine Sammlung von Beacon Object Files (BOFs) und einem dazugehörigen Aggressor-Skript für CobaltStrike, das es Operatoren ermöglicht, nicht verwaltete Windows-Executables in den Beacon-Speicher zu laden und auszuführen, die Ausgabe abzurufen und in der Beacon-Konsole anzuzeigen.

Dies ermöglicht es Operatoren, viele Tools von Drittanbietern (Mimikatz, Dsquery, Sysinternals-Tools usw.) zu verwenden, ohne sie auf die Festplatte schreiben, mit einem Tool wie Donut in positionsunabhängigen Code umwandeln oder einen neuen Prozess erstellen zu müssen, um sie auszuführen.

Diese Executables werden in den Beacon-Speicher gemappt, sodass sie wiederholt ausgeführt werden können, ohne sie jedes Mal über das Netzwerk senden, neuen Speicher allozieren und einen neuen conhost.exe-Prozess erstellen zu müssen.

In Beacons geladene Executables sind für alle CobaltStrike-Clients, die mit dem CobaltStrike-Team-Server verbunden sind, zugänglich und können von ihnen ausgeführt werden.

Inline-Execute-PE wurde für x64-Beacons und x64-Windows-C- oder C++-Executables entwickelt, die mit Mingw oder Visual Studio kompiliert wurden. Dieses Projekt unterstützt keine x86-Executables oder x64-Executables, die in einer anderen Sprache geschrieben oder mit einem anderen Compiler kompiliert wurden.

Einrichtung

Klonen Sie das Repository und führen Sie optional make aus, um die BOFs neu zu kompilieren.

Laden Sie Inline-Execute-PE.cna in den CobaltStrike-Client. Stellen Sie sicher, dass das Verzeichnis, aus dem CobaltStrike ausgeführt wird, von Ihrem Benutzer beschreibbar ist; Inline-Execute-PE erstellt dort eine Textdatei (petable.txt), um die Verfügbarkeit der für die Funktion von Inline-Execute-PE erforderlichen Daten sicherzustellen.

Befehle

Inline-Execute-PE umfasst 3 zielgerichtete Befehle, die BOFs ausführen, und 3 interne Befehle, die die Projektdatenstruktur manipulieren:

Auf das Ziel ausgerichtet:

  1. peload
  2. perun
  3. peunload

Interne Datenstruktur:

  1. petable
  2. peconfig
  3. pebroadcast

peload

peload ist der Beginn von Inline-Execute-PE. Dieser Befehl wird verwendet, um ein PE in den Beacon-Speicher zu laden. Er führt die folgenden Hauptaktionen aus:

  1. Sendet das angegebene PE über das Netzwerk an Beacon ODER sendet den Namen des PE, das von der Festplatte auf dem Zielcomputer gelesen werden soll
  2. Erstellt eine Struktur im Beacon-Speicher, um verschiedene Zeiger und Handles zu speichern, die während des gesamten Lebenszyklus von Inline-Execute-PE benötigt werden
  3. Allokiert Speicher in Beacon und schreibt das PE mit RW-Schutz hinein
  4. Verschlüsselt das PE im Speicher mit einem vom Benutzer angegebenen Schlüssel per XOR
  5. Allokiert einen weiteren Speicherblock und kopiert das XOR-verschlüsselte PE dorthin. Dies ist notwendig, um das PE für nachfolgende Ausführungen "zurücksetzen" zu können
  6. Erzeugt einen conhost.exe-Unterprozess unter Beacon, um stdin/stdout/stderr zu initialisieren
  7. Leitet stdout und stderr in eine anonyme Pipe um, damit die Ausgabe des PE erfasst werden kann

perun

perun ist der zweite Schritt in Inline-Execute-PE. Er führt die folgenden Hauptaktionen aus:

  1. Sendet Befehlszeilenargumente über das Netzwerk an Beacon
  2. Entschlüsselt das PE im Speicher per XOR
  3. Repariert die Import Address Table des PE und hookt bestimmte APIs in Bezug auf Befehlszeilenargumente und das Beenden von Prozessen
  4. Ändert den Speicherschutz des PE auf RWX
  5. Führt das PE in einem eigenen Thread aus
  6. Erfasst die Ausgabe des PE und gibt sie an CobaltStrike zurück
  7. Setzt den Speicherschutz des PE auf RW zurück
  8. Überschreibt das PE im Speicher mit der XOR-Kopie, die während peload erstellt wurde

peunload

peunload wird aufgerufen, um das PE aus dem Beacon-Speicher zu entfernen, wenn ein Operator damit fertig ist oder ein anderes PE laden möchte. Es führt die folgenden Hauptaktionen aus:

  1. Schließt die während peload erstellten Handles und Dateizeiger
  2. Beendet den während peload erstellten conhost.exe-Prozess
  3. Löscht beide Kopien des PE im Speicher und gibt sie dann frei
  4. Versucht, alle vom PE in den Beacon-Prozess geladenen DLLs zu entladen (optional)

petable

petable wird verwendet, um Informationen zu allen aktuell in Beacons geladenen PEs anzuzeigen.

Jeder CobaltStrike-Client hat seine eigene petable; Inline-Execute-PE unternimmt große Anstrengungen, um die Synchronität seiner Daten zwischen allen verbundenen CobaltStrike-Clients sicherzustellen, damit PEs von allen Operatoren verwendet werden können. Weitere Informationen hierzu finden Sie unter "Design Considerations and Commentary".

image

peconfig

peconfig wird verwendet, um Optionen zu konfigurieren, die die Funktionsweise von Inline-Execute-PE betreffen. Die beiden aktuell änderbaren Optionen sind:

  1. Timeout: Gibt an, wie lange perun auf die Fertigstellung des PE wartet, bevor es beendet wird. Dies dient als Sicherheitsmaßnahme für den Fall, dass einem PE falsche Argumente übergeben werden, die dazu führen, dass es nie zurückkehrt/abschließt. Diese Einstellung beträgt standardmäßig 60 Sekunden, kann aber für länger laufende PEs angepasst werden.
  2. UnloadLibraries: Diese Option steuert, ob peunload versucht, DLLs aus dem Beacon-Prozess zu entfernen, die vom PE geladen wurden. Standardmäßig ist dies auf TRUE gesetzt. Manche PEs verursachen Probleme, wenn DLLs aus dem Beacon-Prozess entladen werden, und können Beacon zum Absturz bringen; in diesem Fall ist es besser, alle vom PE geladenen DLLs im Beacon-Prozess zu belassen. Dies wurde bei der Verwendung von powershell.exe beobachtet (möglicherweise weil es die .Net CLR in den Beacon-Prozess lädt).

pebroadcast

pebroadcast kann verwendet werden, um den Inhalt der petable eines Clients manuell an alle anderen verbundenen CobaltStrike-Clients zu senden.

Jeder andere CobaltStrike-Client wird seine petable mit den gesendeten Daten aktualisieren. Dies sollte eigentlich nie notwendig sein, aber die Funktion existiert für den Fall der Fälle.

Verwendung

Verwenden Sie peload, um ein PE in den Beacon-Speicher zu laden
image

Alternativ, wenn sich auf dem Zielcomputer ein PE befindet, das Sie ohne Erstellen eines neuen Prozesses verwenden möchten, geben Sie den Pfad und den --local-Schalter an
image

Rufen Sie perun auf und übergeben Sie Argumente an das geladene PE
image

Anführungszeichen in Argumenten müssen mit Backslashes maskiert werden
image

Wenn Sie festgestellt haben, dass ein PE Probleme verursacht, wenn während des Entladens versucht wird, DLLs freizugeben, verwenden Sie peconfig, um unloadlibraries auf false zu setzen
image

Sobald Sie mit einem PE fertig sind, rufen Sie peunload auf, um es aus dem Beacon zu entfernen
image

Ein anderes PE kann nun in den Beacon geladen werden
image

perun timeout

Sie müssen bei den Befehlszeilenargumenten, die Sie an das PE übergeben, vorsichtig sein; manche PEs stürzen bei falschen Argumenten sofort ab, während andere endlos laufen und dazu führen, dass Beacon nie wieder zurückmeldet, obwohl der Prozess noch läuft.

Dies ist bei Mimikatz.exe zu beobachten, wenn 'exit' nicht am Ende der Argumentliste angegeben wird
image

...

image

Inline-Execute-PE beendet den Thread des laufenden PE, nachdem der angegebene Timeout-Wert erreicht wurde. Dies ermöglicht es Beacon, die normale Kommunikation wieder aufzunehmen (Beacon meldet sich erst zurück, wenn das perun-BOF abgeschlossen ist). Während in diesem Beacon weiterhin normale CobaltStrike-Befehle und andere BOFs verwendet werden können, ist Inline-Execute-PE jetzt deaktiviert; wenn ein laufendes PE auf diese Weise beendet wird, scheint dies stdout und stderr im Beacon-Prozess zu beschädigen, und nachfolgend geladene PEs funktionieren nicht richtig.

Das PE kann (und sollte) weiterhin aus dem Beacon-Speicher entladen werden, jedoch zeigt ein Blick auf petable, dass in diesem Beacon möglicherweise keine weiteren PEs mehr geladen werden können. image

Es ist unerlässlich, dass Sie die PEs, die Sie mit Inline-Execute-PE ausführen möchten, testen und bei der Übergabe von Befehlszeilenargumenten an perun vorsichtig sind. Manche PEs sind nachsichtiger als andere.

Tipps, Tricks und Beobachtungen

Im Folgenden finden Sie in keiner bestimmten Reihenfolge einige Beobachtungen, die während des Testens und der Entwicklung zu bestimmten PEs gemacht wurden, die Benutzer möglicherweise in Beacon laden möchten.

  1. Die Verwendung von peunload auf Powershell.exe führt normalerweise zu einem Beacon-Absturz, wenn UnloadLibraries TRUE ist; ich glaube, dies hängt damit zusammen, dass Powershell.exe die CLR lädt.
  2. Cmd.exe lässt Beacon abstürzen, es sei denn, '/c' wird als erstes Argument verwendet. Z.B. 'perun /c cd' ist in Ordnung, 'perun cd' nicht.
  3. Mimikatz.exe lässt Beacon abstürzen, wenn es geladen, verwendet, entladen und dann erneut geladen wurde, WENN UnloadLibraries während des ersten peunload TRUE war.
  4. Manche PEs sind so programmiert, dass sie beim Beenden ihr Hilfemenü ausgeben; diese werden nicht angezeigt, da Aufrufe von ExitProcess, exit() und ähnlichen Funktionen gehooked und zu ExitThread umgeleitet werden, damit das PE nicht dazu führt, dass unser Beacon-Prozess beendet wird.
  5. Manche PEs sind nicht gut darin, Speicher freizugeben, wenn sie damit fertig sind, und verlassen sich darauf, dass der Speicher freigegeben wird, wenn der Prozess beendet wird; da das PE innerhalb des Beacon-Prozesses läuft (und der Prozess daher nicht beendet wird, wenn das PE fertig ist), kann Beacon dazu neigen, aufzublähen, je mehr PEs darin geladen und ausgeführt werden. Beobachten Sie dies während des Testens mit etwas wie Process Explorer und seien Sie sich dessen während des Betriebs bewusst.
  6. Sysinternals Psexec scheint nicht zu funktionieren; es wird zwar ausgeführt, beschwert sich aber über ein ungültiges Handle für den entfernten Computer. In der Praxis wäre es wahrscheinlich besser, so etwas wie psexec mit dem Socks-Proxy von CobaltStrike und einer Attack-Box-Version von psexec zu verwenden.
  7. Einen neuen Beacon für die Verwendung mit Inline-Execute-PE zu spawnen, ist wahrscheinlich keine schlechte Idee, besonders wenn Sie ein Gefühl dafür bekommen, wie verschiedene PEs im Framework interagieren und funktionieren. Zwei ist eins, eins ist keins.
  8. Wenn es ein LOLBIN gibt, das Sie ohne die Telemetrie der Erstellung eines neuen Prozesses verwenden möchten, verwenden Sie den --local-Schalter mit peload und lesen Sie es von der Festplatte des Zielsystems. Dies kann auch nützlich sein, um Versionsprobleme zu vermeiden.

IOCs und AV/EDR

Zu den mit Inline-Execute-PE verbundenen IOCs gehören unter anderem:

  1. Allozieren von Speicher mit VirtualAlloc
  2. Ändern des Speicherschutzes auf alloziertem Speicher zwischen RW und RWX
  3. Erstellen eines untergeordneten conhost.exe-Prozesses
  4. Laden von DLLs, die vom gemappten PE benötigt werden
  5. Alle Aktionen, die vom eigentlichen PE ausgeführt werden; z.B. Mimikatz, das auf LSASS zugreift

AV/EDR

Ich habe dies während der Entwicklung keinem vollständigen Test gegen ein EDR unterzogen, teils aus Faulheit, teils aufgrund fehlender Verfügbarkeit einer Testumgebung. Es wurde jedoch gegen das aktuell gepatchte Windows Defender getestet (das meiner Erfahrung nach ein recht gutes AV-Produkt ist).

Mimikatz.exe ist wahrscheinlich das verdächtigste und bekannteste PE, das als Kandidat für die Verwendung mit Inline-Execute-PE in den Sinn kommt. Ich stellte fest, dass die Fähigkeit von Windows Defender, Mimikatz zu erkennen, das mit Inline-Execute-PE ausgeführt wird, vom Prozess abhing, in dem Beacon lief.

Ein Beacon, der in einem eigenständigen Executable läuft (denken Sie an beacon.exe mit Artifact Kit, sodass er ausgeführt werden kann und normal an Defender vorbeikommt), wird erkannt, wenn Mimikatz.exe mit Inline-Execute-PE verwendet wird.

Ein Beacon, der in einem Windows-Prozess läuft (injiziert in Explorer.exe, notepad.exe usw. oder per DLL-Sideloading in einen legitimen Prozess), wird NICHT erkannt, wenn Mimikatz.exe mit Inline-Execute-PE verwendet wird.

In Bezug auf EDRs, die Userland-Hooking durchführen, habe ich, wie gesagt, nicht getestet, habe aber die folgenden allgemeinen Gedanken:

Da das PE innerhalb des Beacon-Prozesses läuft, in dem Sie vermutlich bereits NTDLL ent-hooked/aktualisiert haben, würde ich denken, dass Sie nicht allzu viele Probleme damit haben sollten, dass die API-Aufrufe des PE markiert werden. Dieselben Probleme in Bezug auf das, was das PE tatsächlich tut (Prozesse berühren, Registrierungsschlüssel ändern usw.), gelten weiterhin.

Design Considerations and Commentary

Vor ein paar Monaten stieß ich auf RunPE-In-Memory und hatte die Idee, es in ein BOF für CobaltStrike umzuwandeln. Die folgende Reise war viel komplexer und dauerte viel länger als erwartet. Dieses Projekt war besonders herausfordernd, da es kein eigenständiges Tool ist, sondern ein Tool, das zum Ausführen anderer Tools verwendet wird. Dies erfordert ein hohes Maß an Flexibilität und Bemühungen um Kompatibilität mit einer breiten Palette von PEs und all den verschiedenen Arten, wie diese PEs dieselbe Aufgabe erledigen könnten (Argumente abrufen, beenden usw.).

Zu Beginn war Inline-Execute-PE als All-in-One-BOF konzipiert, das für das Laden, Ausführen und Freigeben eines PE in einem Beacon verantwortlich ist. Etwa 3 Wochen nach Projektbeginn, zu dem Zeitpunkt hatte ich einen POC zu ~75% fertig, fand ich Pezor, das vor etwa 1,5 Jahren veröffentlicht wurde und bereits fast alles tat, was ich versuchte; der Hauptunterschied war, dass Pezor unter der Haube Donut aufrief, um das PE in Shellcode umzuwandeln, anstatt das ursprüngliche PE manuell in den Speicher zu mappen.

Diese Entdeckung war in gewisser Hinsicht willkommen und in anderer Hinsicht enttäuschend; es war phänomenal, ein ausgereiftes Projekt zu haben, aus dem ich Inspiration schöpfen und mir über einige Hürden in meinem Code hinweghelfen konnte, aber entmutigend, weil ich effektiv das Rad neu erfunden hatte, ohne es zu wissen. Nachdem ich mich über Pezor informiert und über sein Design, einige handwerksbezogene Angelegenheiten und die operativen Anforderungen meiner Organisation nachgedacht hatte, änderte ich den Kurs von Inline-Execute-PE zu dem, was Sie heute sehen. Diese Entscheidung wurde von mehreren Faktoren getrieben, die unten erörtert werden, ebenso wie einige der merkwürdigeren Designentscheidungen, die bei denen, die bis hierher gelesen haben, vielleicht für hochgezogene Augenbrauen gesorgt haben.

Inline-Execute-PE vs Pezor

Bei der Untersuchung meiner operativen Erfahrung stieß ich auf mehrere Instanzen und Tools, bei denen ich das Tool wiederholt ausführen musste; mit Pezor muss ein Operator das PE wiederholt über das Netzwerk senden, einen conhost.exe erstellen, neuen Speicher in Beacon allozieren usw., was mir potenziell unerwünscht erschien, wenn man AV/EDR bedenkt. Diese Gedankengänge führten zu der Idee, ein PE in Beacon zu 'laden', ähnlich wie Sie ein .PS1 in Beacon für wiederholte Verwendung laden können. Der conhost.exe wird erstellt, wenn das PE zum ersten Mal geladen wird, und bleibt bestehen, solange das PE im Speicher geladen ist; ebenso wird neuer Speicher für das PE einmal bei der ersten Ladung alloziert, und natürlich vermeiden Sie es, das PE jedes Mal über das Netzwerk senden zu müssen, wenn Sie es verwenden möchten. Das Modell, das Inline-Execute-PE übernommen hat, ist nicht ohne Fehler, die ich mit unterschiedlichem Erfolg zu beheben versuchte.

Two Copies of PE

Eine Designentscheidung, die ins Auge springen sollte, ist die Tatsache, dass Inline-Execute-PE das PE ZWEIMAL in Beacon mapped. Dies ist sicherlich nicht wünschenswert oder eine Entscheidung, die ich freiwillig getroffen habe, sondern aus der Notwendigkeit geboren. Wie bereits erwähnt, muss Inline-Execute-PE mehrere Funktionen in Bezug auf Befehlszeilenargumente im PE hooken. Da das gemappte PE innerhalb des Beacon-Prozesses läuft, wird das PE versuchen, die im Abschnitt PROCESS_PARAMETERS des PEB angegebenen Befehlszeilenargumente zu verwenden; um dies zu umgehen, müssen wir, wenn das PE eine der verschiedenen Funktionen aufruft, die die Befehlszeilenargumente abrufen, das PE auf unsere eigenen benutzerdefinierten Funktionen umleiten, wo wir die beabsichtigten Argumente bereitstellen können, wie sie von CobaltStrike per perun übergeben wurden.

Dies funktionierte gut, aber während der Entwicklung bemerkte ich etwas Seltsames bei mehreren verschiedenen PEs. Beim ersten Mal, als das PE ausgeführt wurde, wurde die benutzerdefinierte Funktion, die wir in die IAT des PE eingefügt hatten, korrekt aufgerufen, jedoch rief das PE bei allen späteren Ausführungen mit anderen Argumenten die benutzerdefinierte Funktion nicht auf und erhielt daher die von CobaltStrike übergebenen Argumente nicht. Ich bin mir nicht sicher, was unter der Haube tatsächlich passiert, aber ich bin zu der Annahme gelangt, dass das PE nach der ersten Ausführung die Befehlszeilenargumente irgendwo im Speicher kopiert und bei späteren Ausführungen zuerst an dieser Stelle im Speicher nachschaut, bevor es die gehookedten Funktionen aufruft, um die Befehlszeilenargumente wie beim ersten Mal abzurufen. Ich untermauerte diese Theorie, indem ich die Speicherstelle abrief, an der ein Zeiger auf einen anderen Zeiger auf das Array von Zeigern, die die Argumente enthielten, residierte, und diese Speicherstelle bei jedem Durchlauf manuell so modifizierte, dass sie den richtigen Zeiger enthielt. Dies funktionierte für die Funktionen __getmainargs und __wgetmainargs, aber andere PEs rufen alternative Funktionen wie __p___argv und __p___argc auf, bei denen diese Methode nicht funktionierte.

Um das PE in einen Zustand zurückzusetzen, in dem es tatsächlich die gehookedten Funktionen aufruft, um Argumente abzurufen, griff ich darauf zurück, während peload eine zweite Kopie des PE im Speicher zu erstellen. Diese Kopie ist ebenfalls XOR-verschlüsselt und während des gesamten Lebenszyklus von Inline-Execute-PE mit RX-Schutz versehen und wird lediglich verwendet, um die Kopie des PE zu überschreiben, die tatsächlich mit perun ausgeführt wird. Wie erwähnt, ist es keine perfekte Lösung, aber es ist eine pauschale Lösung, die alle PEs abdeckt, ohne sich in den Details zu verlieren, um eine Lösung für all die verschiedenen PEs und die verschiedenen APIs, die sie verwenden, zu finden.

Conhost.exe

Da eines der Hauptverkaufsargumente von Inline-Execute-PE darin besteht, dass Sie Tools ausführen können, ohne neue Prozesse zu erstellen, ist es ein schwerer Schlag, dass ich ... einen neuen Prozess (conhost.exe) erstellen muss, um dies zu tun. Diese Anforderung ergibt sich aus der Tatsache, dass die Standard-Streams (stdin/stdout/stderr) in Windows-Programmen nicht initialisiert werden, es sei denn, eine Konsole ist vorhanden. In unserem Fall brauchen wir die Konsole überhaupt nicht; die Standard-Streams werden in eine anonyme Pipe umgeleitet und auf diese Weise erfasst, aber ohne den conhost werden die Streams nicht initialisiert und können nicht umgeleitet werden.

Inline-Execute-PE nähert sich dem conhost-Problem auf die gleiche Weise wie Pezor: Es ruft AllocConsole auf und verbirgt sie dann sofort mit ShowWindow. Auf einer Windows 11 VM mit 8 GB RAM sehe ich nie, dass das Konsolenfenster aufblitzt und dann verschwindet, aber das kann je nach Zielsystem variieren.

Ich sprach mit einem Entwickler, der an einem sehr fortschrittlichen kommerziellen C2 arbeitet, das kürzlich ein natives Äquivalent (ok, eine viel fortschrittlichere Version) von Inline-Execute-PE herausgebracht hat, und der mir sagte, dass sie das Spawnen eines conhost.exe vermeiden konnten, indem sie "Windows dazu brachten, zu glauben, es hätte eine Konsole". Mit diesem Hinweis verbrachte ich etwa eine Woche damit, das Internet nach Dokumentation zu durchforsten, wie Windows-Programme mit conhost interagieren, versuchte, die API-Aufrufe im Zusammenhang mit Schreibfunktionen und der Konsole in WinDBG zu verfolgen, und untersuchte sogar den Quellcode von Windows Terminal, der überraschenderweise auf Github verfügbar ist. Obwohl ich viel über das PEB und Dinge im Zusammenhang mit Standard-Streams lernte, kam ich am Ende dieser Reise mit leeren Händen davon. Ich vermute, dass der Weg nach vorne darin bestehen könnte, bestimmte konsolenbezogene Funktionen in kernel32 zu patchen, aber ich weiß es nicht. Ich bin ehrlich gesagt ziemlich enttäuscht, dass ich hier keine Lösung finden konnte, aber da ich Autodidakt bin und erst seit ein paar Jahren in meiner Karriere bin, ist das wohl zu erwarten.### PE-Timeout und Rettung Alle, die jemals versucht haben, einen BOF zu schreiben, sind sich bewusst, dass trotz aller Vorteile, die sie mit sich bringen, eine große Gefahr darin liegt, dass ein Fehler oder Absturz in Ihrem BOF Ihr Beacon töten kann und wird. Die Gefahr wird in diesem Projekt dadurch verstärkt, dass die Benutzer viel Kontrolle über die an Inline-Execute-PE übergebenen Daten haben und wie wenige Sicherheitsmaßnahmen von mir, dem Entwickler, einfach oder zuverlässig implementiert werden können. Benutzer könnten beispielsweise ihr Beacon zum Absturz bringen, indem sie ein x86-PE in ein x64-Beacon laden, oder weitaus häufiger, indem sie, wie bereits erwähnt, falsche Argumente an das gemappte PE übergeben. Obwohl ich Benutzer nicht davon abhalten kann, ihre Beacons mit falschen Argumenten für ihre PEs zum Absturz zu bringen, kann ich versuchen, ihr Beacon im Falle eines endlos laufenden PE zu retten, wie im Fall von Mimikatz, wenn 'exit' nicht angegeben wird.

Idealerweise könnte ich die Ausführung des PE stoppen, sodass Beacon den normalen Betrieb wieder aufnehmen kann, und dann sofort dem Benutzer erlauben, es erneut mit den (hoffentlich) korrekten Argumenten zu versuchen. In der Praxis habe ich festgestellt, dass das Beenden des PE die mit stdout/stderr verbundenen FILE* zu beschädigen scheint, und selbst das vollständige Entladen des PE und erneute Laden behebt dies nicht; sie sind prozessweit beschädigt.

Um ein PE zu beenden, das länger als die 'timeout'-Option läuft, wird TerminateThread für das von CreateThread zurückgegebene Handle aufgerufen. Dies erlaubt dem Thread nicht, irgendetwas ordnungsgemäß zu beenden, daher ist es sinnvoll, dass einige Dinge kaputt gehen könnten. Ich habe versucht, dies durch Thread-Hijacking zu mildern, mit dem Ziel, den PE-Thread anzuhalten und seine Ausführung auf die ExitThread()-API umzuleiten. Die Hoffnung war, dass, wenn der Thread selbst die Exit-Prozeduren startet (im Gegensatz zu einer externen erzwungenen Beendigung), stdout/stderr möglicherweise weiterhin funktionieren, aber ich hatte letztendlich dasselbe Problem (und konnte den PE-Thread im Fall von Mimikatz nicht anhalten).

Da ich dieses Problem nicht beheben konnte, entschied ich mich einfach dafür, Benutzer daran zu hindern, das PE weiter auszuführen oder zusätzliche PEs in das betroffene Beacon zu laden (was zu einem Absturz führen würde). Dies ist ein weiteres Beispiel dafür, dass Inline-Execute-PE hinter meinen Erwartungen zurückbleibt, aber ich gab mich damit zufrieden, dass der Operator zumindest noch sein Beacon hätte und es für normale Funktionen nutzen könnte.

Inline-Execute-PE Datenstruktur

Eine herausfordernde Aufgabe dieses Projekts war es, sicherzustellen, dass die in Beacons geladenen PEs für alle mit dem Team Server verbundenen CobaltStrike-Clients verfügbar sind. Die Daten von Inline-Execute-PE werden in Strukturen gespeichert, die von Inline-Execute-PE.cna erstellt werden und in jeden Client geladen werden müssen, der das Tool verwenden möchte; folglich leben diese Datenstrukturen in jedem einzelnen Client, nicht auf dem Team Server. Wenn diese Daten an einem einzigen zentralen Ort (TS) leben würden, wäre es trivial, sie von jedem Client abzurufen, und das Ganze wäre kein Problem; wenn das CobaltStrike-Team eine Funktion wie Inline-Execute-PE formal in CobaltStrike integrieren würde, bin ich mir sicher, dass sie diesen Weg gehen würden. Aber da dies ein Community-Add-on ist, begnügen wir uns mit dem, was wir haben.

Es gibt einige verschiedene Szenarien, die wir berücksichtigen müssen, um sicherzustellen, dass jeder CobaltStrike-Client die aktuellen und genauen Daten zu den in Beacons geladenen PEs hat:

  1. Neue Clients, die sich mit dem TS verbinden und die aktuelle petable benötigen
  2. Fälle, in denen nur ein einzelner Client mit dem TS verbunden ist und CobaltStrike neu startet (wodurch die im Client-Speicher gespeicherte petable verloren geht)
  3. Client A nimmt eine Änderung an Inline-Execute-PE-Daten vor, die Client B mitgeteilt werden müssen

Es wurde ein mehrgleisiger Ansatz gewählt, um diese Szenarien zu bewältigen. Um den Fall zu handhaben, in dem nur ein einziger CobaltStrike-Client mit dem TS verbunden ist (und somit die einzige Entität mit den petable-Daten ist), schreibt der Client jedes Mal, wenn er die petable ändert (peload, peconfig, peunload, etc.), den Inhalt seiner petable in eine lokale Textdatei im CobaltStrike-Verzeichnis. Wenn der Client beendet/neu gestartet wird oder wenn Inline-Execute-PE.cna neu geladen wird, versucht er zunächst, aus der lokalen petable.txt-Datei zu lesen, um seine In-Memory-petable zu füllen.

Wenn mehrere Clients mit einem TS verbunden sind und ein neuer Client beitritt (wie im Ereignisprotokoll), ruft jeder Client eine Liste aller mit dem TS verbundenen Benutzer ab und sortiert sie alphabetisch. Der Client, der zuerst in dieser Liste steht, wird als 'Broadcast'-Client ausgewählt und sendet nach einer Wartezeit von 5 Sekunden (damit der neue Client initialisieren und seine lokale petable.txt lesen kann) Nachrichten (Aktionen) im Ereignisprotokoll für jeden Eintrag in seiner petable. Alle Clients (außer dem sendenden) lesen diese Nachrichten und aktualisieren ihre petables mit den Broadcast-Informationen; dies umfasst das Aktualisieren vorhandener Einträge sowie das Hinzufügen zusätzlicher Einträge, die ihre jeweiligen petables nicht enthalten.

Normale Vorgänge mit Inline-Execute-PE basieren ebenfalls auf dem Senden von Nachrichten im Ereignisprotokoll. Wenn Client A peload ausführt, wird eine Nachricht mit allen relevanten petable-Informationen gesendet; ALLE Clients aktualisieren ihre jeweiligen petables, indem sie diese gesendeten Ereignisprotokoll-Nachrichten mit dem 'on Event_Action'-Hook parsen. Änderungen an den Inline-Execute-PE-Daten werden auch vorgenommen, wenn peload und peunload die Ausführung ihrer BOFs abschließen; diese Änderungen werden von Beacon zurückgemeldet (z.B. nach dem Ausführen von peload meldet Beacon den Speicherort der pMemAddrs-Struktur zurück) und sind somit für alle verbundenen Clients sichtbar, die ihre jeweiligen petables mit dem 'on Beacon_Output'-Hook aktualisieren.

Diese getrennten Bemühungen zusammen führen dazu, dass Inline-Execute-PE in der Lage ist, kritische Daten effizient und zuverlässig zwischen mehreren Clients zu synchronisieren.

Danksagungen

Dieses Projekt wäre nicht möglich gewesen ohne die folgenden Projekte und Ressourcen, die stark referenziert wurden und aus denen Kernteile dieses Projekts stammen. Großer Dank an die Autoren für ihren Code und ihre Vision.

  1. RunPE-In-Memory
  2. Pezor
  3. Vieles von StackOverflow
Tool herunterladen