Root-me: Ringgit – prima challenge Very Hard risolta!

Dall’ultimo post è trascorso un po’ di tempo. No, non sono sparito. In quest’ultimo mese ho continuato il mio percorso sulla sezione Cracking di Root-Me.

root-me Cracking icon

Quando avevo scritto il precedente post ero a circa il 70% delle challenge di reversing risolte. Nel frattempo non ho mollato la presa. Avevo risolto un’altra buona parte delle challenge terminando tutte le Medium e le Hard, per arrivare ad avere tutte quelle della sezione Cracking complete ad eccezione delle due Very Hard:

Prima di parlare di Ringgit voglio spendere due parole riguardo il mio percorso su tutta la sezione.

Adesso sulle mie statistiche aggiornate iniziano a pesare le challenge Medium e Hard:

Devo ammettere che le challenge a livello Hard sono molto interessanti perché presentano delle tecniche usate da molti packer e malware. Se con qualcuna livello medio potreste annoiarvi qui, invece, la cosa si fa seria.

Su alcune sfide di livello difficile come ELF x64 – Nanomites, ELF x64 – Anti-debug et equations e soprattutto ELF x86 – VM ho fatto un po’ più di fatica. Nel caso specifico di ELF x64 – Anti-debug et equations, una volta capito come venivano usati i trick anti-debug e come era costruito l’algoritmo di validazione della chiave, ho preferito scrivermi un Keygen piuttosto che ricorrere a Z3 come suggerito dall’autore (in onore di tutti i reverser old-school <3) .

Arriviamo a Ringgit, è il primo Very Hard che risolvo sulla piattaforma e posso solo confermare che il livello di complessità è molto alto.

Se posso usare un termine per definire questo crackme, il termine che si avvicina più di tutti a quello che penso è CAPOLAVORO. La challenge, a mio avviso, è una chiara dimostrazione della genialità del suo autore. Ho impiegato un bel po’ di tempo a risolverlo, e per arrivare a questo risultato ho dovuto rispolverare un po’ di concetti sui processi in Linux e sul debugging sotto linux, decompilare, scrivere codice in C e soprattutto fare una delle cose più belle che si possono fare quando si fa reversing, re-implementare!

Ne è valsa la pena, e se vi doveste avventurare in questa challenge preparatevi a qualche notte insonne (probabilmente più di una). Ovviamente non posso dirvi nulla sulla soluzione e su come ci sono arrivato per due motivi principali:

  1. Non voglio rovinarsi il gusto di provare a risolverla, quindi niente spoiler;
  2. E’ vietato da regolamento di Root-Me discutere le soluzioni e credo fermamente sia stupido fornire la soluzione.

Quello che però posso dirvi è che a mano a mano che capirete ogni dettaglio di Ringgit, e vi renderete conto di come i vari pezzi sono collegati, quello che si nasconde dietro questa challenge sarà qualcosa di inatteso! Sono certo vi piacerà moltissimo averci giocato. I Nanomite presenti sono solo un primo ostacolo, è l’intera implementazione che rende affascinante, e allo stesso tempo intrigante, il tentare di risolverlo. Personalmente l’ho amato per come è stato programmato ed ideato, l’ho odiato per la complessità, ma fosse stato più semplice sono certo non l’avrei apprezzato così tanto.

Durante le diverse fasi che mi hanno poi portato a capire il vero senso del crackme, e i concetti astratti legati a come è costruito, sono passato dal “ah! ho capito” al “ah no… non avevo capito un cavolo”, ancora al “non ce la posso fare”, e poi ancora “ah ma forse questa cosa è così”, e ancora “è troppo difficile, se getto la spugna la sto gettando per una cosa veramente tosta”.

Dopo molte ore di sconforto ad un certo punto potrebbe accendersi per voi, come è stato per me, una lampadina e questo è il vero motivo per cui ho scritto questo post. Prima di arrivarci però sappiate che la vostra ragazza vi odierà, gli amici altrettanto, vostra mamma penserà che siete impazziti e le occhiaie prenderanno il sopravvento. Il vostro cervello dovrà abituarsi a leggere attraverso le righe di codice assembly. 😉

Mi è rimasta una sola challenge da risolvere nella sezione Cracking e so che sarà veramente dura, considerando che è stata creata dallo stesso autore di Ringgit. Non so se ce la farò, ma sono certo che ci proverò e non demorderò facilmente.

Alla fine, attualmente, il risultato di queste nottate è quello che vedete di seguito, e non me ne vergogno, sono felice come un bambino!

Per concludere posso solamente dirvi: Non mollate mai, neppure se è troppo difficile.

Prendetevi del tempo, fate delle pause, bevete una birra, uscite con la vostra ragazza/moglie o con gli amici ma non gettate la spugna. Prima o poi troverete la via, arriverete ad abbattere il problema, e alla fine potrete urlare “ce l’ho fatta!”.

-bdev-

Root-me: divertimento e non solo!

Recentemente ho cambiato un po’ la mia opinione sui CTF (Capture The Flag) e in generale sulle piattaforme come HTB, root-me, ecc. Confrontandomi su questo tema, con amici, colleghi ed ex colleghi ho notato che ognuno ha la propria linea di pensiero.

Io, in primis, fino a qualche tempo fa, ero abbastanza scettico sull’utilità di queste piattaforme se non per il puro e semplice divertimento. Infatti, come alcuni pensano, queste challenge talvolta presentano scenari e casistiche molto lontane dalla realtà, in cui la soluzione potrebbe trovarsi in posti che non vi sareste mai aspettati. Risolvere alcune challenge rischia, quindi, di diventare una perdita di tempo perché quello che si deve trovare è una flag che richiede diversi passaggi, ma applicabili a quella sola CTF.

Mi sono in parte ricreduto e in questo post volevo spendere due parole su root-me. Non è ancora passato un mese da quando mi sono iscritto (link al mio profilo) e devo ammettere che mi sto divertendo un sacco a cercare di rompere diverse challenge.

Indovinate un po’ da quale categoria sono partito? Vista la mia forte addiction per il reversing e per smanettare con gli assembly vari non potevo che iniziare ovviamente con Cracking!

Dopo circa 3 settimane sono riesco ad ottenere il badge “Reverser Rookie , avendo superato il 50% di challenge risolte nell’area Cracking.

Le mie validation per difficoltà finora sono distribuite come segue:

Anche se sono su root-me da poco, mi sono fatto una prima idea sulla qualità delle challenge e della piattaforma.

Per quanto riguarda esclusivamente la sezione Cracking, avendo completato fino quasi il 70% delle challenge, posso permettermi di dirvi che si tratta di crackme ben fatti. Voglio aggiungere, di seguito, qualche mia considerazione (prima) e qualche consiglio (dopo) se mai vi imbatterete in quest’area:

  • I punteggi assegnati alle singole challenge, a mio parere, non sono troppo veritieri circa il grado di difficoltà. È vero che, talvolta, un crackme che risulta facile per un reverser potrebbe non esserlo per un altro. Sicuramente, se partite con i crackme con punteggi <20 non c’è praticamente nulla di complesso sono ottimi per iniziare. Penso che un buon modo per capire se la difficoltà assegnata alla challenge sia bilanciata consiste nel vedere quante validation ha e confrontarla con altre challenge con lo stesso punteggio. Vi posso dire che sotto le 500 validation si tratta di sfide impegnative e che possono richiedere skill specifiche.
  • Ci sono crackme per diverse piattaforme hardware come Intel x86, Intel x64, MIPS, ARM. C’è, persino, una challenge che dovrete risolvere reversando l’assembly del Gameboy! Personalmente mi sono divertito molto a reversare un crackme per un’architettura RISC così arcaica. Quest’ultimo è uno di quegli scenari che, come dicevo al punto precedente, non troverete mai nella realtà, ma che, per il divertimento che danno nelle ore che ci dedicate, possono valere la pena di installare i tool e studiare qualche buon paper per risolverli.
  • Alcuni dei crackme sono ottimi per capire il funzionamento di sistemi di protezione che potete trovare in scenari reali. Vi dovrete scontrare, ad esempio, con Nanomites, trick anti-debug di vario genere, tecniche di offuscamento del codice, concetti di crittografia. Coloro che pensano di risolvere tutto con un colpo di Ghidra quasi sicuramente ne usciranno scottati, soprattutto nelle challenge medio-complesse in cui servono nervi saldi, pazienza e sedute di debugging di ore.

Consigli:

  • Non cercate la strada difficile, né quella troppo facile! Ok, questo è un consiglio che si applica molto al Reverse Engineering. Se vedete che ci state mettendo troppo a risolvere una challenge, facendo mille supposizioni, probabilmente state sbagliando approccio. Questa cosa è vera, soprattutto, per le challenge fino alla media difficoltà, per quelle hard e very hard la musica cambia!
  • Bisogna essere metodici, nel Reverse Engineering quasi più che nel Penetration Testing. Aprendo un eseguibile su IDA o su qualsiasi tool di analisi statica o dinamica non bisognerebbe partire velocemente ad analizzare dall’entry point. Un’attenta analisi delle librerie importate, delle tecnologie impiegate, dei segmenti dell’eseguibile può aiutare moltissimo a risparmiare tempo nella risoluzione e dare dettagli su come catturare in maniera astuta la preda senza doverla rincorrere a lungo.

Conclusioni

Chiudo questo brevissimo post con una mia personalissima opinione sui CTF. Secondo me, investire tempo sulle challenge è utile. Si impara molto, si fa molta pratica, si comprendono i propri limiti, ci si fa male, a volte si soffre, ma alla fine ci si prendono soddisfazioni quando si ottengono i risultati.

Nel mio caso è sempre un sacrificio perché lavorando devo impiegare il mio tempo libero fuori dall’orario di lavoro e nei fine settimana per dedicarmi a questa passione.

Sono scelte, e ovviamente, ognuno dedica il proprio tempo e le proprie risorse come preferisce. Non aspettatevi di essere degli dei in terra se, facendo le CTF, avete risolto tutto o se siete bravi nel risolvere le challenge di un’area, la realtà potrebbe darvi degli schiaffi pesanti. Un esempio sono i Penetration Test, in cui non avete le libertà che avete nelle CTF, dovrete fare un report dettagliato dei passaggi che avete fatto, e potreste, ad esempio, mandare in DoS un sistema se sbagliate payload con il risultato di avere un cliente arrabbiato e insoddisfatto.

Comprare corsi e fare certificazioni è un buon modo per migliorare e sono uno dei sostenitori di questa linea di pensiero. I corsi, però, non vi danno la garanzia di diventare dei guru di una tematica. Dovrete sempre metterci del vostro e compensare la pratica con delle challenge di qualità è un ottimo modo di integrare studio e pratica per diventare sempre più bravi.

-bdev-

Linux x64: Anti-Debug Shellcode + execve

Nutro da tempo un debole per tutto il mondo che ruota attorno alle tecniche di anti-reversing, specie se applicate agli shellcode.

In questo campo c’è poco e le motivazioni sono diverse, in primis il fatto che i check anti-debug occupano memoria nello shellcode e quando si fa shellcoding la dimensione in byte è un fattore cruciale. Shellcode di grandi dimensioni possono richiedere multi-stage, egghunting e altre tecniche, impattando la scelta del giusto payload da adottare in fase di exploit e complicando, talvolta, l’exploitation.

Un po’ di tempo fa mi sono imbattuto nello shellcode, scritto nel lontanissimo 2006 da izik e che lo stesso autore spiegava come segue:

(linux/x86) anti-debug trick (INT 3h trap) + execve("/bin/sh", ["/bin/sh", NULL], NULL) - 39 bytes 

The idea behind a shellcode w/ an anti-debugging trick embedded in it, is if for any reason the IDS would try to x86-emulate the shellcode it would *glitch* and fail. This also protectes the shellcode from running within a debugger environment such as gdb and strace. 

How this works? the shellcode registers for the SIGTRAP signal (aka. Breakpoint Interrupt) and use it to call the actual payload (e.g. _evil_code) while a greedy debugger or a confused x86-emu won't pass the signal handler to the shellcode, it would end up doing _exit() instead execuve() 

 - izik 

Cercando in giro non c’era una riscrittura della tecnica su Linux x64 così mi sono detto, perché non scriverne uno io prendendo spunto dal codice di izik?

Sfida bella e interessante la tecnica usata, che fa uso dell’exception handling per far gestire al debugger un finto breakpoint (via SIGTRAP) in modo che vada a fare lo skip dello shellcode, che rimane in mano all’handler (sorpassato dal debugger perché specializzato nel gestire le eccezioni INT3) e non viene eseguito. Nel caso dell’esecuzione fuori dal debugger invece la SIGTRAP viene catturata dall’handler impostato dallo shellcode con conseguente innesco della execve.

Bene, ottimi i presupposti, ed imbattersi in qualcosa che ancora non esiste è sempre intrigante.

Quindi ho proceduto nel seguente modo:

  1. Ho analizzato nel dettaglio lo shellcode x86 di izik;
  2. Ho cercato la documentazione relativa all’implementazione dei Signal per capire il funzionamento;
    • Nella versione x86 l’exception handling è delegato a signal, nella versione x64 le eccezioni vengono invece gestite da sigaction.
Linux x86: sighandler_t signal(int signum, sighandler_t handler);
Linux x64: int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
  1. Ho capito, a quel punto, quale fosse il problema e cosa avrei dovuto fare per poter scrivere lo shellcode:
    • Cambiare i registri utilizzati (Linux a 64 bit usa ovviamente un assembly differente confronto alla versione a 32 bit);
    • Cambiare le syscall e i relativi parametri;
    • Inizializzare correttamente le due strutture *act e *oldact per poter effettuare correttamente la chiamata, task non banale tenendo a mente di scrivere uno shellcode e quindi considerando tutta una serie di altri fattori come dimensione, caratteri NUL, ecc;
    • Modificare in parte la struttura base del codice di izik per adattarlo alle nuove caratteristiche richieste.

La struttura struct sigaction, che ricopre un ruolo fondamentale nel nuovo shellcode è definita come segue:

struct sigaction {
               void     (*sa_handler)(int);
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };

In casi come questo, il mio approccio preferito è quello di prendere un codice di esempio della funzionalità, compilarlo e fare Reverse Engineering sul codice assembly dell’eseguibile ottenuto, per capire come il compilatore ha trasformato il mio codice ad alto livello. Solo a quel punto, nel caso in cui io voglia farne uno shellcode, inizio a riscrivere il codice per cercare di limitare uso di memoria e per renderlo Position Independent e NUL free.

Dopo alcuni tentativi sono riuscito a scrivere un POC funzionante per Linux x64.

Potete trovare lo shellcode su Packet Storm Security oppure a pagina 2 di questo articolo.

Il codice è sufficientemente commentato per permettere facile comprensione delle operazioni effettuate.

Per oggi è tutto, ma se avete domande o considerazioni potete commentare o scrivermi all’indirizzo email che trovate nella sezione About. 🙂

-bdev-