Come offuscare un semplice shellcode - parte 1
Una delle cose che mi è piaciuta di più, mentre preparavo l’esame per OSCE, esame che devo riprovare in questo inverno, è stata la parte di evasione dagli anti virus.
Questo ha portato a far nascere il progetto Shellerate, di cui ne ho parlato in questp post.
Oggi parliamo ancora di offuscare uno shellcode, partendo da un esempio semplice e proseguendo per passi successivi.
(Il codice utilizzato in questo post, e successivi, è presente in questo repository Github).
Trivia
Utilizzeremo una macchina virtuale Ubuntu 19.04 e compileremo il nostro shellcode a 32bit con nasm:
Perché offuscare il proprio shellcode?
Il motivo alla base di questa attività è quello di mascherare il codice che vogliamo inserire all’interno di un exploit, per andare ad far eseguire del codice arbitrario al nostro processo vittima.
Lo voglio mascherare per un semplice motivo, rendere la vita difficile a chi analizza il codice dell’exploit o semplicemente per cercare di ingannare il nostro anti-virus.
Shellcode di partenza
Lo shellcode di partenza è un semplice execve(“/bin/sh”, NULL, NULL). Il codice assembler che la realizza è molto semplice, basta ricordare che nel registro EAX deve essere il contenuto il numero della system call da utilizzare e che i registri EBX, ECX ed EDX vanno utilizzati per i parametri da passare alla chiamata.
In particolare EBX dovra contenere il puntatore alla stringa del comando da eseguire, stringa che andremo a caricare nello stack; ECX ed EDX dovrebbero contenere rispettivamente un puntatore alla lista degli argomenti del comando e un puntatore alle variabili d’ambiente che si intende passare al comando. Nel nostro caso possiamo risparmiare codice e dare un NULL come valore.
Per trasformare il mio codice binario in un codice eseguibile che può essere incluso poi in un exploit, utilizzo questo pipe di comandi che ho recuperato tempo fa in Internet:
La nostra shellcode di partenza è questa e non ha byte \x00 che ne impedirebbero poi l’utilizzo all’atto pratico all’interno di un exploit:
Qui vediamo la nostra shell originale in esecuzione ed è quello che vogliamo ottenere da ogni codice offuscato, un prompt di una shell.
Primo passaggio: code refactoring
Nel primo passaggio di offuscamento, cambio il modo con cui i registri ECX ed EDX sono inizializzati a 0.
Dopo aver inizializzato a 0 il registro EAX, ne salvo il valore all’interno dello stack per 3 volte. Il primo sarà per costituire il terminatore di stringa per il comando “/bin/sh”, mentre gli altri due saranno immediatamente consumati da una POP per inizializzare i registri ECX ed EDX.
Codice esadecimale dello shellcode:
Secondo passaggio: cambio meccanismo di inizializzazione
Nel secondo passaggio cerchiamo di complicare un po’ le cose visto che, come si può vedere dall’equivalente esadecimale il primo passaggio non ha inciso particolarmente.
Ora cambiamo il meccanismo con cui inizializziamo a 0 i nostri registri. Usiamo la matematica per mettere a 0 il registro ECX, caricando un valore costante a 32bit e poi sottraendo lo stesso valore.
Con ECX a 0, possiamo impostare EAX ed EDX a 0 semplicemente invocando l’istruzione MUL che moltiplica il valore in EAX con il valore passato per argomento, in questo caso il valore del registro ECX e memorizzando il risultato della moltiplicazione in EAX ed EDX.
Codice esadecimale dello shellcode:
Come si può vedere, in questo caso lo shellcode che otteniamo ha effettivamente un aspetto diverso da quello di partenza. La strada è ancora lunca, ma stiamo iniziando a rendere un po’ meno leggibile quello che vogliamo mettere nel nostro payload.
Terzo passaggio: usiamo la matematica per /bin/sh
Quello che possiamo cambiare ora è il modo con cui salviamo nello stack il comando da eseguire. Per ora facciamo delle PUSH passando la codifica esadecimale di /bin/sh in modo tale che, quando verrà letta dallo stack, che ricordiamo è una struttura LIFO (Last In First Out), verrà ricostruito in maniera corretta.
Cambiamo quindi le PUSH in operazioni matematiche per valorizzare il registro EAX al valore che voglio salvare nello stack. I numeri sono stati scelti a caso, tranne ovviamente l’ultimo ADD per il quale siamo dovuti atterrare al valore esagerato. Unico vincolo era quello di non causare un overflow del registro EAX e di non superare il valore che voglio salvare nello stack per evitare di dover utilizzare anche una SUB.
Quindi, ad esempio, l’istruzione push 0x68732f2f
diventa:
Alla fine il valore del registro EAX sarà proprio 0x68732f2f. Il mio shellcode in assembler diventa quindi:
Una volta compilato, ottengo il seguente esadecimale:
Ad una prima occhiata è nettamente diverso dal codice di partenza anche se non rappresenta alcuna difficoltà per un qualsiasi membro di un blue team.
Off by one
Offuscare il codice attraverso il refactoring e la riscrittura creativa di istruzioni è una cosa estremamente divertente, che ci permette di ripassare il nostro assembler e che ci permette di costruire un exploit con un payload che difficilmente verrà rilevato da un approccio solamente basato su signature.
Nella seconda parte, continueremo il lavoro del nostro ufficio degli affari semplici e andremo a complicare ancora di più il codice assembler della nostra execve.
Enjoy it!
Vuoi aiutarmi a portare avanti il progetto Codice Insicuro con una donazione? Fantastico, allora non ti basta che premere il pulsante qui sotto.
Supporta il progetto