
Una tecnica avanzata di evasione in memoria che fa oscillare la protezione della memoria dello shellcode tra RW/NoAccess e RX e poi ne crittografa/decrittografa i contenuti.
Una implementazione PoC per un'altra tecnica di evasione in-memory che cifra e decifra ciclicamente il contenuto dello shellcode per farlo fluttuare tra protezioni di memoria RW (o NoAccess) e RX.
Quando il nostro shellcode risiede in pagine di memoria RW o NoAccess, scanner come Moneta o pe-sieve non saranno in grado di individuarlo e dumpare per ulteriori analisi.
Dopo aver rilasciato ThreadStackSpoofer ho ricevuto alcune domande su questo punto del README:
Cambia la protezione delle pagine di memoria del tuo Beacon in RW (da RX/RWX) e cifra il loro contenuto prima di andare in sleep (questo potrebbe eludere scanner come Moneta o pe-sieve)
In precedenza ero abbastanza sicuro che la community sapesse già come cifrare/decifrare i propri payload e cambiare le protezioni di memoria per eludere gli scanner di memoria che cercano regioni eseguibili anomale. Le domande hanno dimostrato il contrario, così ho deciso di rilasciare questo PoC non weaponizzato per documentare un'altra strategia di evasione e offrire un'implementazione di esempio con cui la community possa lavorare.
Questo PoC è una dimostrazione di una tecnica piuttosto semplice, già nota alla community offensiva (quindi non sto portando nulla di davvero nuovo qui), nella speranza di svelare il segreto dietro la magia mostrata da alcuni framework commerciali che dimostrano le loro capacità di evasione mirate a entrambi gli scanner di memoria sopra citati.
Ecco un confronto quando si fluttua a RW (un'altra opzione è fluttuare a PAGE_NOACCESS - descritta sotto):

Questa implementazione insieme al mio ThreadStackSpoofer offre alla community di Offensive Security implementazioni di esempio per recuperare terreno rispetto all'offerta dei prodotti C2 commerciali, così da non fare peggio nei nostri tooling Red Team. 💪
Questo programma esegue un self-injection di shellcode (approssimativamente tramite il classico VirtualAlloc + memcpy + CreateThread).
Quando lo shellcode viene eseguito (questa implementazione mira specificamente agli impianti Cobalt Strike Beacon) una funzione di Windows viene hookkata per intercettare il momento in cui Beacon va in sleep kernel32!Sleep.
Ogni volta che la funzione hookkata MySleep viene invocata, localizza i confini della propria allocazione di memoria, ne cambia la protezione in RW e applica xor32 a tutti i byte ivi memorizzati.
Dopo aver atteso il tempo previsto, quando lo shellcode torna al nostro handler MySleep, decifriamo i dati dello shellcode e ripristiniamo la protezione a RX.
PAGE_READWRITE funziona cosìkernel32!Sleep facendolo puntare al nostro callback.VirtualAlloc + memcpy + CreateThread. Contrariamente a quanto avevamo in ThreadStackSpoofer, qui non hookkiamo nulla in ntdll per avviare il nostro shellcode, ma saltiamo ad esso dalla nostra stessa funzione. Questo tenta di evitare di lasciare in memoria semplici IOC che puntano a memoria ntdll modificata.MySleep viene invocato.RWkernel32!Sleep originale per evitare di lasciare in memoria un semplice IOC che segnali che Sleep sia stato trampolinato (hook inline).::Sleep originale per lasciare Beacon in sleep in attesa di ulteriori comunicazioni.RX e poi ri-hookkiamo per garantire l'intercettazione dei successivi sleep.PAGE_NOACCESS funziona cosìkernel32!Sleep facendolo puntare al nostro callback.VirtualAlloc + memcpy + CreateThread ...MySleep viene invocato.PAGE_NOACCESSkernel32!Sleep originale per evitare di lasciare in memoria un semplice IOC che segnali che Sleep sia stato trampolinato (hook inline).::Sleep originale per lasciare Beacon in sleep in attesa di ulteriori comunicazioni.kernel32!Sleep per garantire l'intercettazione dei successivi sleep.La tecnica non è nuova di zecca, non è qualcosa che ho ideato io. È semplicemente un'implementazione che mostra il concetto e il suo utilizzo pratico per permettere alla nostra community di Offensive Security di recuperare terreno rispetto all'offerta dei framework C2 commerciali.
In realtà, sono stato introdotto all'idea di cambiare la protezione di memoria dello shellcode un paio di anni fa grazie al lavoro di Josh Lospinoso nel suo fantastico Gargoyle.
Ecco ulteriori riferimenti:
Gargoyle porta il concetto di shellcode auto-consapevole e auto-fluctuante molto più avanti, sfruttando una sequenza ROP che chiama VirtualProtect.
Tuttavia la tecnica è impressionante, ma è altrettanto difficile da sfruttare con il Beacon di Cobalt Strike senza dover terminare il suo thread e mantenere la re-inizializzazione del Beacon in memoria.
Non è affatto perfetto, tuttavia, poiché operiamo già dalla base del nostro processo loader di self-injection, possiamo fare quello che vogliamo con l'ambiente in cui opera lo shellcode e nasconderlo come preferiamo. Questa tecnica (e la precedente, cioè ThreadStackSpoofer) mostra i vantaggi dell'eseguire i nostri shellcode in questo modo.
L'implementazione della fluttuazione a PAGE_NOACCESS è ispirata al lavoro di ORCA666 presentato nel suo https://github.com/ORCA666/0x41 injector.
Ha dimostrato che:
Questa implementazione contiene questa idea, disponibile con l'opzione 2 in <fluctuate>.
Date un'occhiata anche agli altri suoi progetti.
Il tool ShellcodeFluctuation accetta tre parametri: il primo è il percorso dello shellcode e il secondo è il modificatore della nostra funzionalità.```
Usage: ShellcodeFluctuation.exe
:
-1 - Read shellcode but dont inject it. Run in an infinite loop.
0 - Inject the shellcode but don't hook kernel32!Sleep and don't encrypt anything
1 - Inject shellcode and start fluctuating its memory with standard PAGE_READWRITE.
2 - Inject shellcode and start fluctuating its memory with ORCA666's PAGE_NOACCESS.
### Moneta (apparentemente) Falso Positivo```
C:\> ShellcodeFluctuation.exe beacon64.bin -1
Quindi, per prima cosa vediamo cosa pensa lo scanner Moneta64 di un processo che non fa nulla di losco e si limita a eseguire un ciclo infinito:

Come possiamo vedere, c'è un falso positivo (almeno per come lo considero io) che presumibilmente rileva Mismatching PEB module / Phantom image.
I confini di memoria puntano al modulo ShellcodeFluctuate.exe stesso e potrebbero indicare che questo modulo, pur essendo di tipo MEM_IMAGE, non è collegato nella PEB del processo - il che è insolito e sembra piuttosto strano.
Il motivo di questo IOC non mi è noto e non ho tentato di comprenderlo meglio, ma non è qualcosa di cui dovremmo davvero preoccuparci.
Se qualcuno sa qual è il motivo di questa rilevazione, sarei molto curioso di saperlo! Non esitate a contattarmi.
C:> ShellcodeFluctuation.exe beacon64.bin 0
Il secondo caso d'uso presenta gli IOC di memoria di un Beacon in esecuzione all'interno del nostro processo, che non utilizza alcun tipo di `Artifact Kit` personalizzati, `User-Defined Reflective Loaders` (come il mio [`ElusiveMice`](https://github.com/mgeeky/ElusiveMice)), né alcuna azione iniziale che possa compromettere i nostri risultati.

Possiamo vedere che `Moneta64` riconosce correttamente `Abnormal private executable memory` che punta alla posizione in cui risiede il nostro shellcode.
Questo è un IOC di memoria davvero forte che espone il nostro shellcode a essere dumpato e analizzato da scanner automatici. Non è bello.
### Beacon cifrato con protezioni RW```
C:\> ShellcodeFluctuation.exe beacon64.bin 1
Ora il terzo caso d'uso, il più interessante dal punto di vista di questa implementazione, è il Beacon fluttuante.

A parte il primo IOC, considerato in qualche modo un falso positivo, ne vediamo uno nuovo che indica che la memoria di kernel32.dll è stata modificata.
Tuttavia, questa volta nessun IOC Abnormal private executable memory. La nostra fluttuazione (ripetuta crittografia/decrittografia e alternanza delle protezioni della memoria) è attiva.
E per la cronaca, pe-sieve rileva anche PE impiantati quando viene usato con l'opzione /data 3 (a meno che questa opzione non venga fornita, non verrà effettuato alcun rilevamento):

La mia attuale ipotesi è che PE-Sieve stia rilevando le stesse caratteristiche di Moneta (descritte più avanti in Codice modificato in kernel32.dll) - il fatto che un modulo PE mappato abbia un Working set non vuoto, essendo una prova evidente di qualche forma di iniezione di codice. Questo viene etichettato come Implanted PE / Implanted. Se è così, la conclusione è simile all'osservazione di Moneta. Non penso che dovremmo preoccuparci più di tanto di questo IOC dal punto di vista del rilevamento.
Attualmente non ho pensato a un'opzione migliore per intercettare l'esecuzione dello shellcode a metà (parlando ora di Cobalt Strike), se non agganciare kernel32!Sleep. Pertanto, siamo costretti a lasciare questi tipi di IOC.
Ma ehi, nessuno dei byte differisce ancora rispetto a ciò che si trova sul filesystem (C:\Windows\System32\kernel32.dll) e nessuna funzione è agganciata, allora qual è il problema? 😉
C:> ShellcodeFluctuation.exe beacon64.bin 2

Ciò farà sì che lo shellcode oscilli effettivamente tra pagine `RX` e `NA`.
Al momento non sono sicuro dei vantaggi del passaggio a `PAGE_NOACCESS` invece di `PAGE_READWRITE`.
### Codice modificato in kernel32.dll
Allora, che dire di quell'IOC del `kernel32` modificato?
Ora, proviamo ad andare a fondo di questo IOC e vediamo di cosa si tratta.
Innanzitutto, scaricheremo la regione di memoria menzionata, ovvero la sezione `.text` (codice) di `kernel32.dll`. Usiamo `ProcessHacker` a tale scopo, per utilizzare strumenti pubblicamente noti e stabili:

Scarichiamo la sezione del codice di un kernel32 presumibilmente modificato e poi facciamo lo stesso per il kernel32 in esecuzione in un processo che non ha modificato quell'area.
Dopo aver ottenuto due dump, possiamo confrontarli byte per byte (usando il mio [expdevBadChars](https://github.com/mgeeky/expdevBadChars)) per cercare eventuali incongruenze:

Solo per vedere che corrispondono tra loro. Chiaramente non c'è un solo byte modificato in `kernel32.dll` e il motivo è che stiamo rimuovendo l'hook di `kernel32!Sleep` prima di chiamarla:
`main.cpp:31:````
HookTrampolineBuffers buffers = { 0 };
buffers.originalBytes = g_hookedSleep.sleepStub;
buffers.originalBytesSize = sizeof(g_hookedSleep.sleepStub);
//
// Unhook kernel32!Sleep to evade hooked Sleep IOC.
// We leverage the fact that the return address left on the stack will make the thread
// get back to our handler anyway.
//
fastTrampoline(false, (BYTE*)::Sleep, &MySleep, &buffers);
// Perform sleep emulating originally hooked functionality.
::Sleep(dwMilliseconds);
Allora, cosa causa l'attivazione dell'IOC? Ispezioniamo Moneta più da vicino:

Entrando nel Ioc.cpp di Moneta, proprio attorno alla riga 104 dove riporta l'IOC MODIFIED_CODE, possiamo modificare leggermente il codice per esporre meglio il momento esatto in cui analizza il pool di kernel32.
Ora:
a = truekernel32 ha b = 0x1000 byte privati. Come mai? Dovrebbero essere 0.a && b), l'IOC viene segnalatoQuando Windows Image Loader mappa un modulo DLL nello spazio di memoria del processo, le pagine di memoria sottostanti verranno etichettate come MEM_MAPPED o MEM_IMAGE a seconda dello scenario.
Ogni volta che modifichiamo anche un solo byte dell'allocazione MEM_MAPPED/MEM_IMAGE, il sistema separerà una singola pagina di memoria (supponendo che abbiamo modificato meno di PAGE_SIZE byte e non abbiamo attraversato il confine di pagina) per indicare un frammento che non viene mappato nuovamente all'immagine originale.
Questa osservazione viene quindi utilizzata come IOC: un'immagine non dovrebbe avere allocazioni MEM_PRIVATE all'interno della propria regione di memoria, perché ciò indicherebbe che alcuni byte sono stati modificati in quella regione. Moneta rileva correttamente la modifica del codice, anche se i byte corrispondevano ai byte del modulo originale al momento del confronto.
Per una spiegazione esauriente di come funzionano internamente Moneta, l'implementazione dell'iniezione di processo e i relativi IOC, leggi i seguenti articoli di alta qualità di Forrest Orr:
Questa è una ricerca e una documentazione davvero eccezionale fatta da Forrest, grande lavoro amico!
In particolare il secondo articolo delinea la giustificazione di questo rilevamento, come leggiamo ciò che Forrest ci insegna:
Nel caso in cui il modulo fosse stato caricato legittimamente e aggiunto al PEB, l'impianto shellcode sarebbe comunque stato rilevato a causa dei 0x1000 byte (1 pagina) di memoria mappata privatamente nello spazio degli indirizzi e recuperati da Moneta interrogando il suo working set - risultando in un IOC di codice modificato come visto sopra.
Per riassumere, stiamo lasciando un IOC dietro di noi, ma dovremmo preoccuparcene? Anche se c'è un IOC, non ci sono byte rubati visibili, quindi nessun riferimento immediato che riconduca al nostro shellcode o che distingua la tecnica del nostro shellcode dalle altre.
In breve, non dovremmo preoccuparci più di tanto di questo IOC. :-)
Si potrebbe dire che questa implementazione è tutt'altro che perfetta perché lascia qualcosa, ci sono ancora IOC e i prodotti commerciali dimostrano di non avere caratteristiche simili.
Quando questo argomento viene messo sul tavolo, devo ricordare che i framework commerciali hanno il controllo completo sul codice sorgente dei loro implant e dei loro caricatori di shellcode, e quindi possono integrarsi bene l'uno con l'altro per evitare la necessità di effettuare hook e di armeggiare attorno al proprio shellcode. Qui, dobbiamo agganciare kernel32!Sleep per intercettare l'esecuzione del Beacon di Cobalt Strike appena prima che si addormenti, per poter avviare le nostre attività di manutenzione. Se ci fosse un meccanismo migliore per intervenire senza dover agganciare Sleep, sarebbe perfetto.
Tuttavia, esiste il concetto di Sleep Mask introdotto in Cobalt Strike; i limiti di dimensione, essendo di poche centinaia di byte, ci rendono del tutto impossibile introdurre questa logica nella maschera stessa (altrimenti potremmo anche non agganciare Sleep, senza lasciare IOC proprio come fanno i prodotti commerciali).
Un altro argomento potrebbe essere che i framework commerciali integrino questo tipo di logica nei loro Reflective Loader, mentre qui la lasciamo invece nell'EXE harness. È vero, ma la ragione di questa decisione è duplice:
Devo stare molto attento a rilasciare questo tipo di tecnologia per evitare il rischio di contribuire ad armare i criminali del mondo reale con un'implementazione che potrebbe tornarci a perseguitare con un'altra Petya. In tal senso, ho deciso di tralasciare alcuni dei dettagli cruenti che uso nei miei strumenti professionali impiegati per esercitazioni commerciali di Adversary Simulation su contratto. Diffondere il seme, si spera, incontrerà professionisti della community in grado di sviluppare il concetto nei propri strumenti, supponendo che abbiano le competenze appropriate.
Preferirei di gran lunga spostare tutta questa logica nello User-Defined Reflective Loader di Cobalt Strike, offrendo ai gruppi Red Team maggiori possibilità nella loro fase di consegna. Ma in primo luogo, vedi il punto (1); in secondo luogo, quella tecnologia è attualmente limitata a 5KB di dimensione per i loro RDLL, rendendomi completamente impossibile implementarla anche lì. Per coloro che costruiscono C2 e implant personalizzati per impegni interni di Adversary Simulation, ora hanno ricevuto un'implementazione di esempio che li aiuterà sicuramente a migliorare i propri strumenti di conseguenza.
Guarda il codice e la sua implementazione, comprendi il concetto e re-implementalo nei tuoi Shellcode Loader che utilizzi per portare a termine i tuoi impegni Red Team. Questa è un'altra tecnica per l'evasione avanzata in memoria che aumenta le probabilità del tuo Team di non essere scoperto da Antivirus, EDR e Malware Analyst che esaminano i tuoi implant.
Mentre sviluppi il tuo avanzato shellcode loader, potresti anche voler implementare:
BeaconEyeMEM_PRIVATE referenziate da questi thread)Caso d'uso:``` Usage: ShellcodeFluctuation.exe : -1 - Read shellcode but dont inject it. Run in an infinite loop. 0 - Inject the shellcode but don't hook kernel32!Sleep and don't encrypt anything 1 - Inject shellcode and start fluctuating its memory with standard PAGE_READWRITE. 2 - Inject shellcode and start fluctuating its memory with ORCA666's PAGE_NOACCESS.
Dove:
- `<shellcode>` è un percorso al file shellcode
- `<fluctuate>` come descritto sopra, accetta `-1`, `0` o `1`
Esempio di esecuzione che spoofa lo stack di chiamate del thread del beacon:```
C:\> ShellcodeFluctuation.exe ..\..\tests\beacon64.bin 1
[.] Reading shellcode bytes...
[.] Hooking kernel32!Sleep...
[.] Injecting shellcode...
[+] Shellcode is now running. PID = 9456
[+] Fluctuation initialized.
Shellcode resides at 0x000002210C091000 and occupies 176128 bytes. XOR32 key: 0x1e602f0d
[>] Flipped to RW. Encoding...
===> MySleep(5000)
[.] Decoding...
[>] Flipped to RX.
[>] Flipped to RW. Encoding...
===> MySleep(5000)
Se hai intenzione di aggiungere questa funzionalità ai tuoi loader / tooling shellcode, assicurati di EVITARE di rimuovere gli hook da kernel32.dll.
Un tentativo di rimuovere gli hook da kernel32 ripristinerà la funzionalità originale di Sleep, impedendo che la nostra callback venga chiamata.
Se la callback non viene chiamata, il thread non sarà in grado di spoofare il proprio call stack da solo.
Se è quello che vuoi, allora potresti dover eseguire un altro thread watchdog, assicurandoti che il thread del Beacon venga spoofato ogni volta che dorme.
Se stai usando Cobalt Strike e un BOF unhook-bof di Raphael's Mudge, assicurati di dare un'occhiata alla mia Pull Request che aggiunge un parametro opzionale al BOF per specificare le librerie a cui non dovrebbero essere rimossi gli hook.
In questo modo puoi mantenere i tuoi hook in kernel32:``` beacon> unhook kernel32 [*] Running unhook. Will skip these modules: wmp.dll, kernel32.dll [+] host called home, sent: 9475 bytes [+] received output: ntdll.dll <.text> Unhook is done.
[Modificato `unhook-bof` con opzione per ignorare moduli specificati](https://github.com/mgeeky/unhook-bof)
---
## Osservazione finale
Questo PoC è stato progettato per funzionare con gli shellcode Beacon di Cobalt Strike. Il Beacon è noto per chiamare `kernel32!Sleep` per attendere ulteriori istruzioni dal suo C2.
Questo loader sfrutta questo fatto agganciando `Sleep` per eseguire le sue operazioni di manutenzione.
Questa implementazione potrebbe non funzionare con altri shellcode in circolazione (come _Meterpreter_) se non usano `Sleep` per mettersi in pausa.
Poiché questa è solo una _Proof of Concept_ che mostra la tecnica, non ho intenzione di aggiungere il supporto per nessun altro framework C2.
Quando comprendi il concetto, sicuramente sarai in grado di trasporlo nei requisiti del tuo shellcode e adattare la soluzione a tuo vantaggio.
Per favore, non aprire issue Github relative a "questo codice non funziona con XYZ shellcode", verranno chiuse immediatamente.
---
### ☕ Mostra Supporto ☕
Questi e altri progetti sono il risultato di notti insonni e **molto duro lavoro**. Se ti piace quello che faccio e apprezzi il fatto che restituisco sempre qualcosa alla comunità,
[Considera di offrirmi un caffè](https://github.com/sponsors/mgeeky) _(o meglio una birra)_ solo per dire grazie! 💪
---
## Autore```
Mariusz Banach / mgeeky, 21
<mb [at] binary-offensive.com>
(https://github.com/mgeeky)
kernel32!SleepRX e l'esecuzione dello shellcode riprende.