
Thread Stack Spoofing - PoC per una tecnica avanzata di evasione in-memory che permette di nascondere meglio l'allocazione di memoria dello shellcode iniettato da scanner e analisti.
Una implementazione PoC per una tecnica avanzata di evasione in-memory che falsifica lo Stack dei Thread. Questa tecnica permette di bypassare le regole di esame della memoria basate sui thread e nascondere meglio gli shellcode mentre sono in memoria di processo.
Questa è un'implementazione d'esempio per la tecnica Thread Stack Spoofing che mira a eludere Analisti Malware, AV e EDR che cercano riferimenti ai frame dello shellcode nella call stack di un thread esaminato. L'idea è nascondere i riferimenti allo shellcode sulla call stack del thread, mascherando così le allocazioni che contengono il codice del malware.
Questa implementazione, insieme al mio ShellcodeFluctuation, fornisce alla comunità Offensive Security implementazioni di esempio per recuperare il terreno rispetto all'offerta dei prodotti C2 commerciali, in modo da non essere da meno nei nostri tooling Red Team. 💪
L'implementazione attuale differisce pesantemente da quella originariamente pubblicata.
Questo perché ho realizzato che esiste un approccio molto più semplice per terminare l'elaborazione della call stack del thread e nascondere i frame relativi allo shellcode, semplicemente scrivendo 0 all'indirizzo di ritorno del primo frame che controlliamo:
void WINAPI MySleep(DWORD _dwMilliseconds)
{
[...]
auto overwrite = (PULONG_PTR)_AddressOfReturnAddress();
const auto origReturnAddress = *overwrite;
*overwrite = 0;
[...]
*overwrite = origReturnAddress;
}
La precedente implementazione, che utilizzava StackWalk64, è accessibile in questo commit c250724.
Questa implementazione è molto più stabile e funziona bene sia in Debug che in Release su due architetture - x64 e x86.
Ecco come potrebbe apparire una call stack quando NON è falsificata:

Questa, invece, quando la falsificazione dello stack del thread è abilitata:

Sopra possiamo vedere che l'ultimo frame sulla nostra call stack è la nostra callback MySleep.
Ci si potrebbe chiedere se ciò porti immediatamente a nuovi IOC? Le regole di caccia possono cercare thread con call stack che non si srotolano nei seguenti punti di ingresso attesi del thread, situati nelle librerie di sistema:
kernel32!BaseThreadInitThunk+0x14
ntdll!RtlUserThreadStart+0x21
Tuttavia la call stack del thread falsificato potrebbe sembrare strana a prima vista; un breve esame del mio sistema ha mostrato che ci sono anche altri thread che non si srotolano fino ai punti di ingresso sopra:

Lo screenshot sopra mostra un thread di Total Commander x64 non modificato. Come possiamo vedere, la sua call stack assomiglia molto alla nostra in termini di frame iniziali della call stack.
Perché dovremmo preoccuparci di falsificare attentamente la nostra call stack quando ci sono processi che mostrano tratti che possiamo semplicemente imitare?
L'algoritmo approssimativo è il seguente:
dbghelp.dll, chiamare SymInitializekernel32!Sleep puntando alla nostra callback.VirtualAlloc + memcpy + CreateThread. Il thread dovrebbe partire dalla nostra funzione runShellcode per evitare che l'Indirizzo di Inizio del Thread punti in un luogo inaspettato e anomalo (come ntdll!RtlUserThreadStart+0x21).MySleep viene invocata.0, che dovrebbe effettivamente terminare la call stack.::SleepEx per permettere al Beacon di dormire mentre attende ulteriori comunicazioni.Gli indirizzi di ritorno delle funzioni sono sparsi in tutta l'area di memoria dello stack del thread, puntati dal registro RBP/EBP.
Per trovarli sullo stack, dobbiamo prima raccogliere i puntatori ai frame, poi dereferenziarli per sovrascriverli:

(l'immagine sopra è stata presa dal post di Eli Bendersky intitolato Stack frame layout on x86-64)
*(PULONG_PTR)(frameAddr + sizeof(void*)) = Fake_Return_Address;
L'implementazione iniziale di ThreadStackSpoofer faceva questo nelle funzioni walkCallStack e spoofCallStack, tuttavia l'implementazione attuale mostra che questi sforzi non sono necessari per mantenere una call stack furtiva.
Caso d'uso:
C:\> ThreadStackSpoofer.exe <shellcode> <spoof>
Dove:
<shellcode> è il percorso del file shellcode<spoof> quando è 1 o true abiliterà la falsificazione dello stack del thread, qualsiasi altra cosa la disabilita.Esempio di esecuzione che falsifica la call stack del thread di beacon:
PS D:\dev2\ThreadStackSpoofer> .\x64\Release\ThreadStackSpoofer.exe .\tests\beacon64.bin 1
[.] Lettura dei byte dello shellcode...
[.] Aggancio di kernel32!Sleep...
[.] Iniezione dello shellcode...
[+] Shellcode ora in esecuzione.
[>] Indirizzo di ritorno originale: 0x1926747bd51. Terminazione della call stack...
===> MySleep(5000)
[<] Ripristino dell'indirizzo di ritorno originale...
[>] Indirizzo di ritorno originale: 0x1926747bd51. Terminazione della call stack...
===> MySleep(5000)
[<] Ripristino dell'indirizzo di ritorno originale...
[>] Indirizzo di ritorno originale: 0x1926747bd51. Terminazione della call stack...
Guarda il codice e la sua implementazione, comprendi il concetto e reimplementalo all'interno dei tuoi Loader di Shellcode che usi per le tue attività Red Team. Questa è un'ulteriore tecnica di evasione avanzata in-memory che aumenta le possibilità del tuo Team di non essere scoperto da Antivirus, EDR e Analisti Malware che esaminano i tuoi impianti.
Mentre sviluppi il tuo loader di shellcode avanzato, potresti anche voler implementare:
BeaconEyeRW (da RX/RWX) e crittografarne il contenuto - usando la tecnica Shellcode Fluctuation - proprio prima di dormire (che potrebbe eludere scanner come Moneta o pe-sieve)