gdb / pwndbg Mind Map

Ogni Reverser/Exploit Developer così come qualsiasi appassionato che voglia analizzare un programma in esecuzione, e comprenderne i dettagli al bit, si affida tipicamente ad un debugger.

Alcuni dei più conosciuti strumenti per questo scopo sono: x64dbg, WinDBG, Immunity Debugger, OllyDBG per quanto riguarda Windows, e il famosissimo GNU Debugger (gdb) su ambiente Linux.

By Original: Jamie Guinan Vectorization: Lunavorax (talk · contribs) – This file was derived from: Archer.jpg:, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=65338278

GDB è davvero minimale nell’utilizzo e per ottenere le informazioni base sul processo in debugging bisogna ricorrere ad una miriade di comandi. Sicuramente è un bell’esercizio mentale se lo volete usare nudo e crudo. Per quanto mi riguarda penso che il gioco non valga la candela.

Per questo motivo, ad eccezione di rarissimi casi in cui non mi è possibile, utilizzo GDB con installato il plugin pwndbg.

A detta del suo autore “pwndbg is a GDB plug-in that makes debugging with GDB suck less, with a focus on features needed by low-level software developers, hardware hackers, reverse-engineers and exploit developers.“, come dargli torto…

Chi ogni giorno si imbatte in sessioni di reversing, o debugging in generale, troverà in questo plugin un validissimo aiuto per compensare le mancanze di GDB in termini di usabilità.

Parlando di gdb personalmente ritengo che, anche con PwnDBG, si tratta comunque di uno strumento complesso, con un bel po’ di comandi da ricordare. Nel corso del tempo mi sono spesso ritrovato a dover cercare online lo specifico comando necessario per fare quel “qualcosa”. Talvolta la pagina del manuale non mi è stata sufficiente e sono dovuto ricorrere ad esempi di utilizzo sui vari blog.

In questo modo ho perso spesso e volentieri del tempo prezioso solo per non essermi ricordato il comando specifico, o semplicemente qualche maledetta virgola che non sapevo ci volesse per separare due campi.

Ok, ci sono gli helper per singolo comando, ma comunque avere una visione d’insieme e una veloce mappa per avere le cose più comuni [e non 😉 ] sott’occhio a volte, sinceramente, mi è mancata.

Per questo motivo ho deciso, nel tempo, di crearmi una mind map con i comandi che ritengo più utili di GDB e PwnDBG. Sicuramente non è esauriente. Gdb, sia con pwndbg installato, che senza, è uno strumento che permette un controllo completo e molto granulare del processo sotto debug. Questo è vero al punto tale che riportare in una mappa mentale tutti i comandi sarebbe un’impresa folle, se non addirittura inutile. Infatti già così la mind map non è piccola, immaginatela quattro se non cinque volte più grande, sarebbe ingestibile e dispersiva.

GDB/PwnDBG Mind Map sample

Ho deciso di rendere pubblica la mind map per permettere sia ai novizi, che ai più esperti di trarne qualche beneficio. Trattandosi di un primo rilascio eventuali suggerimenti / correzioni sarebbero graditi.

Links per il download (media e alta risoluzione):

  • PDF [~160KB] – Link
    • SHA256: 2bb6d293912ad78e4fa2bedf4b7f6f14b6d0853d757174233707701c1742d486
  • Medium resolution PNG [~8MB] – Link
    • SHA256: 9172cbf87519e3665d4d4803b774d6462905c1127158fb3f74a7c1924c33c8ef
  • High resolution PNG [~12MB] – Link
    • SHA256: 26b2311a607f5d1f15976dc6af5d52519615525d8ede942ac5161a96c4a7bf22
  • Ultra High resolution PNG [~21MB] – Link
    • SHA256: 5bd002ef38ebb2d0cd762518ef72de03bb23597405429bc1d707fa616eb6fd92

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-