Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
ShellGhost — Una tecnica di evasione basata sulla memoria che rende lo shellcode invisibile dall'inizio alla fine del processo. | Kitploit
Strumenti/GitHubGitHub/lem0nsec/shellghost
Strumenti di Crittografia/DecrittografiaMemory ForensicsExploitShellcodeAnalisi di BinariRed TeamingSviluppo Payload
GitHublem0nsec/shellghost

ShellGhost

Una tecnica di evasione basata sulla memoria che rende lo shellcode invisibile dall'inizio alla fine del processo.

Vedi Repository
1.2k1402 anni faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

ShellGhost

Una tecnica di evasione basata sulla memoria che rende lo shellcode invisibile dall'inizio alla fine del processo.


Motivazione

Volevo condividere questo POC di self-injection di shellcode per illustrare alcuni concetti di evasione AV/EDR che potrebbero rivelarsi utili per il Red Teaming. Solo poche settimane fa ho ideato una tecnica di evasione personalizzata in memoria che ho chiamato ShellGhost. Questa tecnica nasce dall'esigenza di avere un codice che esegue uno shellcode 'invisibile' dall'inizio alla fine del processo.


Gestione del Flusso di Esecuzione del Thread

ShellGhost si basa sulla Vectored Exception Handling in combinazione con breakpoint software per fermare ciclicamente l'esecuzione del thread, sostituire il breakpoint eseguito con un'istruzione dello shellcode crittografata con RC4, decrittare l'istruzione e riprendere l'esecuzione dopo aver ripristinato la protezione della memoria a RX. Quando viene sollevata la successiva EXCEPTION_BREAKPOINT, il gestore delle eccezioni sostituisce l'istruzione precedente dello shellcode con un nuovo breakpoint in modo che l'allocazione non riveli mai lo shellcode completo in uno stato non crittografato. Ciò avviene all'interno di una pagina di memoria privata inizialmente contrassegnata come READ/WRITE. Avere un'allocazione RW PRV non sarà considerato un 'Indicatore di Compromissione' dai memory scanner come PE-Sieve e Moneta. Quando l'allocazione diventa RX e la pagina viene scansionata, non si troverà altro che breakpoint. Ciò accade mentre lo shellcode è effettivamente in esecuzione. L'immagine seguente mostra che una reverse shell è in esecuzione, ma Moneta non rileva alcun IOC (a parte il fatto che il binario non è firmato).

Provare a scansionare il processo con Pe-Sieve ha un risultato ancora migliore:


Mapping dello Shellcode

Il mapping dello shellcode è la funzionalità principale di ShellGhost. Questa tattica consente al thread di eseguire istruzioni in modo intermittente senza mai esporre l'intero shellcode in memoria. Ciò è possibile perché la posizione di ogni singola istruzione dello shellcode che il thread esegue corrisponde alla posizione di un certo breakpoint all'interno della pagina di memoria allocata. ShellGhost risolve questa posizione calcolando l'indirizzo virtuale relativo (RVA) dal RIP del thread all'indirizzo base della pagina di memoria allocata e lo aggiunge all'indirizzo base dello shellcode crittografato / delle istruzioni crittografate. Il numero di breakpoint che verranno sostituiti non è sempre lo stesso, ma varia a seconda del numero di opcode necessari affinché ogni istruzione venga generata e interpretata correttamente (QUOTA). Ad esempio, l'istruzione 'POP RBP' equivale a '5D', quindi verrà sostituito un solo breakpoint. Al contrario, l'istruzione 'JMP RAX' richiede gli opcode 'FF E0', quindi verranno sostituiti due breakpoint. Per questo motivo ho creato la seguente struttura dati in C.

root@kitploit:~
typedef struct CRYPT_BYTES_QUOTA {

	DWORD RVA;		// offset per l'istruzione crittografata 
	DWORD quota;	// numero di opcode che generano l'istruzione

} CRYPT_BYTES_QUOTA, * PCRYPT_BYTES_QUOTA;

I breakpoint non vengono immediatamente sostituiti con le corrispondenti istruzioni. Questo perché le istruzioni devono essere sottoposte a una routine di decrittazione prima di essere eseguite. È qui che entra in gioco il DWORD quota. ShellGhost si basa sull'ormai popolare 'SystemFunction032' per eseguire la decrittazione RC4. A differenza di XOR, RC4 non è uno schema di crittografia a byte singolo. Ciò significa che lo shellcode non può essere crittografato e decrittato tutto in una volta. Questo è anche un altro motivo per cui ogni istruzione viene trattata separatamente. Dopo la sostituzione dei breakpoint, la lunghezza del buffer necessaria a SystemFunction032 sarà uguale alla 'quota dell'istruzione', che rappresenta il numero di opcode di cui è composta l'istruzione specifica. Quindi, ad esempio, considera il seguente frammento.

root@kitploit:~

CRYPT_BYTES_QUOTA instruction[200];
instruction[5].quota = 2

USTRING buf = { 0 }; 	// conterrà il buffer da decrittare e la sua lunghezza
USTRING key = { 0 }; 	// conterrà la chiave RC4 e la sua lunghezza

buf.Length = 2 		// lunghezza del buffer, o lunghezza dell'istruzione da decrittare

Sappiamo che l'istruzione numero 5 dello shellcode è composta da 2 opcode, quindi una lunghezza del buffer di 2 verrà passata a SystemFunction032. Questo è importante perché provare a decrittare l'intero shellcode con una singola chiamata a SystemFunction032 lo corromperebbe completamente.

Come viene eseguito il Mapping dello Shellcode?

Lo shellcode deve essere mappato con ShellGhost_mapping.py prima della compilazione. Lo script estrae ogni singola istruzione e la tratta come un piccolo shellcode indipendente. Le istruzioni vengono crittografate una per una e stampate in formato C tutte insieme come unsigned char. Il risultato può essere codificato all'interno del codice C. Di seguito è riportato un esempio di come appaiono le istruzioni crittografate di uno shellcode MSF per calc.exe.

Questo shellcode ha 98 istruzioni, quindi vengono dichiarati 98 struct CRYPT_BYTES_QUOTA. Quando il codice viene eseguito, questi struct devono essere popolati con i corretti RVA e QUOTA delle istruzioni. Il parametro '-1' indica allo script di mapping di stampare il pezzo di codice che fa questo.

Regolazione dei Parametri Winapi

Gli shellcode x64 di Metasploit tipicamente memorizzano i parametri stringa delle winapi tra le istruzioni. In altre parole, uno shellcode x64 di MSF che chiama Winexec non pusha una serie di byte con un byte nullo alla fine per avere la prima stringa di parametri nello stack. Piuttosto, il registro RCX (primo parametro) è un puntatore all'interno dello stesso shellcode, proprio come nell'immagine seguente.

Ciò significa che i breakpoint la cui posizione è relativa alla stringa non verranno mai risolti, perché il RIP non toccherà mai quella posizione. In effetti, questo codice risolve le istruzioni effettive dello shellcode attraverso cui passa il RIP, non i parametri che non verranno mai eseguiti come istruzioni. Per risolvere questo problema, ho notato che gli shellcode di MSF memorizzano sempre un puntatore alla winapi che stanno chiamando nel registro RAX, quindi saltano al registro stesso. Quindi, quando ShellGhost VEH rileva che il breakpoint risolto è 'JMP RAX' e il registro RCX contiene un puntatore a una posizione all'interno dello shellcode, tenta di risolvere anche ciò a cui punta RCX. Successivamente, l'esecuzione non viene restituita alla memoria allocata. Piuttosto, RAX (indirizzo della winapi) viene copiato in RIP e l'esecuzione del thread riprende dalla winapi, sovrascrivendo così 'JMP RAX' e mantenendo la memoria allocata RW. Questo è necessario per le reverse shell che chiamano WaitForSingleObject, che farebbero addormentare il thread dopo 'JMP RAX' lasciando la memoria RX per tutto il tempo in cui la shell rimane attiva. Il seguente frammento di codice contiene le due condizioni che devono essere soddisfatte affinché ShellGhost possa regolare il registro RCX quando contiene una stringa di parametri winapi e permettere allo shellcode MSF di emettere correttamente la chiamata di funzione (WinExec nell'esempio qui).

root@kitploit:~
<snip>
	
if (*(PWORD)exceptionData->ContextRecord->Rip == 0xe0ff) // se RIP è 'JMP RAX'

<snip>

if ((contextRecord->Rcx >= (DWORD_PTR)allocation_base) && (contextRecord->Rcx <= ((DWORD_PTR)allocation_base + sizeof(sh)))) // se RCX è all'interno dell'allocazione

<snip>

RDX, R8 e R9 (secondo, terzo e quarto parametro) non sono ancora coperti.

Differenze e Somiglianze con Altre Tecniche

ShellcodeFluctuation è un concetto di evasione in memoria molto simile. Come in esso, la memoria allocata qui 'oscilla' da RW a RX. Al contrario, ShellGhost introduce i seguenti miglioramenti:

  • Crittografia RC4 più 'Mapping dello Shellcode' anziché XOR a singolo byte
  • Nessuna necessità di agganciare funzioni
  • Supporto per shellcode Metasploit

ShellGhost è tuttavia ben lontano dall'essere una tecnica perfetta. Soffre ancora del più grande svantaggio di tutte queste tecniche, ovvero la necessità di avere memoria eseguibile privata in un certo momento durante l'esecuzione. Tecniche più avanzate come Foliage hanno già trovato un modo per aggirare questo problema. Inoltre, un'allocazione di memoria piena di breakpoint software può essere rilevata da una regola YARA. L'immagine seguente mostra Moneta che rileva correttamente un IOC per l'allocazione RX PRV.

Quando si tratta di eludere una soluzione EDR, la scansione della memoria è solo una parte di un quadro più ampio. L'assenza completa di IOC non significa necessariamente che un binario che utilizza questa tecnica si riveli efficace contro un dato EDR. Per quanto ne so, ho sperimentato situazioni in cui la soluzione non ti consente nemmeno di avviare il binario nel modo in cui lo stai facendo. L'altra faccia della medaglia è che gli IOC non sono sempre indicatori precisi, e alcuni di essi potrebbero rivelarsi falsi positivi. Detto questo, questa è solo una tecnica grezza e un'ispirazione che spero il lettore apprezzi. Il Red Teamer sa che, proprio come i componenti di un EDR, l'evasione in memoria è solo un componente del motore.

Note

La compilazione richiede la disabilitazione del linking incrementale. Questo progetto VS ha già impostate tutte le opzioni del compilatore/linker.

Scarica lo strumento