Corso di Sicurezza Informatica - unirc.it · Altri errori logici. Buffer Overflow Un “buffer”...
Transcript of Corso di Sicurezza Informatica - unirc.it · Altri errori logici. Buffer Overflow Un “buffer”...
Corso di Sicurezza Informatica
Sicurezza del Software
Ing. Giuseppe D’Aquì
Sicurezza nell’informatica
“Un computer sicuro è un computer spento”(Kevin Mitnick)
Attacchi informatici
� Gli attacchi informatici, secondo Wikipedia“vengono fatti tramite la rete internet, daparte di utenti chiamati dalla società“cracker”, che tramite l’uso di software particolari, a volte creati da loro stessi, siintrufolano abusivamente all’internodel sistema, riuscendo ad ottenere pienadisponibilità della macchina, per gestirerisorse e dati senza avere i giusti requisitirichiesti”
Attacchi di massa
� Storicamente (fino a metà anni ’80) i software venivano scritti appositamente per l’utilizzosu specifiche macchine� Le macchine stesse a loro volta erano molto
diverse tra loro, come architettura e sistema operativo� Un attacco, per riuscire, necessitava di una
persona con grande esperienza e abilità
Attacchi di massa (2)
� Con la diffusione dell’informatica “di massa”:� Standardizzazione dell’architettura� Standardizzazione “de facto” dei sistemi operativi� Accesso di rete tramite protocolli standard
� Si è passati ad un ambiente in cui ci sono centinaiadi migliaia di macchine tutte uguali
� Se un attacco funziona su una, funzionapotenzialmente su tutte
� � “pacchettizzazione” degli attacchi e Script Kiddie
Vulnerabilità
� Una vulnerabilità è una “debolezza” del sistema che può essere sfruttata da un attacker� Causata da un errore di implemetazione o di
progettazione
� Un “exploit” è un insieme di istruzioni chesfruttano la vulnerabilità
Exploit
� Gli exploit vengono in genere pubblicati sui bollettinidi sicurezza perché sono una “prova tangibile”dell’esistenza di un bug
� Un exploit pubblicato ha ovviamente la conseguenza di aumentare gli attacchi da parte diScript Kiddie
� Ma, considerando che chi ha intenzioni malevole in qualche modo lo trova anche se non pubblicato, diffondere un exploit serve a “difendere” gli utentivisto che il produttore del software è costretto a rilasciare una correzione (patch)
Memory Leak
� Un “memory leak” è una scorretta gestionedella memoria da parte di un software� Per esempio, a seguito di un memory leak un
programa può occupare più memoria diquanta gliene necessiterebbe� Non sempre da un memory leak deriva una
vulnerabilità
Errori di programmazione
� Errori che causano accesso non controllato ai dati.� Errori che causano alterazione del flusso di
esecuzione del programma.� Mancanza di verifiche sui permessi di accesso alle
funzioni oppure ai dati (controlli inadeguati o incompleti).
� Errori sulle condizioni limite (primo o ultimo caso).� Altri errori logici.
Buffer Overflow
� Un “buffer” è un’area di memoria temporaneache contiene dei dati� Qualsiasi “variabile” può essere vista come
un buffer
Struttura di un software
� Un software viene scritto con linguaggi diprogrammazione “leggibili da umani” e poi tradotto in un linguaggio “leggibile dallamacchina”� I passi sono:� Compilazione� link
Richiami di architettura dei calcolatori
� Una CPU è composta da:� Control Unit: unità che gestisce il flusso di
esecuzione dei programmi� Arithmethic Logic Unit� Registri: aree di memoria interne, usate come
supporto alle operazioni
Codice macchina
� Qualunque software per essere eseguitodeve essere trasformato in codice macchina� Sequenza di byte, composta da codice e dati
� Il codice è composto da numeri (opcode e parametri) che rappresentano istruzioni� permettono di svolgere operazioni sui registri della
CPU, sulla memoria, sulle periferiche ecc.
Uso della memoria
� Importante: osservando una locazione dimemoria è impossibile sapere se il numeroche contiene rappresenta una istruzione o un dato� Il suo significato dipende dal flusso di
esecuzione!
Mappa della memoria
� Un software caricato in memoria e pronto per l’esecuzione si può suddividere in blocchichiamati “segmenti”� Blocchi che raggruppano locazioni di
memoria che svolgono una funzione simile
Mappa della memoria
Stack
Code
Heap
BSS
Data
Direzione dicrescita Heap
Direzione dicrescita Stack
Mappa della memoria
� Data: contiene le variabili globali e staticheinizializzate� Es. static int pippo=321;
� BSS: contiene le variabili globali e statichenon inizializzate, o inizializzate a zero� Es.� static int pippo;
� static int pluto=0;
Mappa della memoria
� Heap: contiene le variabili allocate con malloc()/new durante l’esecuzione del programma� Stack: contiene le variabili locali “semplici”
(int, char, short…) e informazioni ausiliarieper effettuare le chiamate di funzione
Struttura Stack
� Lo stack (pila) è una struttura dati LIFO� Last In First Out� L’ultimo elemento ad entrare è il primo ad uscire
� Uno stack possiede due funzioni:� Push: inserisce un dato in “cima” allo stack (top)� Pop: rimuove un dato dalla cima dello stack
Heap e Stack
� L’Heap e lo Stack rappresentano la memoriaa disposizione di un programma e sono didimensione variabile� L’Heap cresce spostando in avanti il
puntatore alla “cima” (top)� Lo Stack cresce spostando all’indietro il
puntatore alla “cima” (top)� Se i due puntatori si incontrano� Out of
Memory
Mappa della memoria*
Stack
Code
Heap
BSS
Data
Direzione dicrescita Heap
Direzione dicrescita Stack
* la disposizione dei blocchi cambia a seconda dell’architettura e del Sistema Operativo
Attacco Denial of Service (DoS)
� Conoscendo il funzionamento della memoria, un primo attacco che può venire in mente èriempire tutta la memoria a disposizione in modo che un programma smetta difunzionare� Un attacco di questo tipo porta a “negare il
servizio” agli altri utenti, perché il software èandato in crash
Attacco DoS (esempio)
� Un servizio erogato via internet (come web, email, etc) ha bisogno di una certa quantità dimemoria per gestire ogni richiesta� Al crescere delle richieste occuperà sempre
più memoria� Fino al punto in cui la memoria non basterà
più
Attacco DoS
� Per evitare il fallimento dell’esecuzione moltiservizi accettano solo un certo numero dirichieste, mettendo le altre in coda� In questo modo il programma non va in crash
� Dal punto di vista degli utenti, però, non cambia niente: durante un attacco DoS sivedono negare il servizio
Esecuzione del codice macchina
� L’esecuzione del codice macchina avviene in modo sequenziale� Normalmente c’è un registro (Program
Counter (PC) o Instruction Pointer (IP)) � memorizza l’indirizzo dell’istruzione corrente� Viene incrementato per passare all’istruzione
successiva
� Esistono istruzioni di Jump che servono ad eseguire le condizioni (if…else) e le chiamatea funzione
Chiamate di funzione
� Una chiamata di funzione è una cosa piùcomplicata di come sembra� Non è un semplice “salto condizionale”,
perché ha queste caratteristiche:� Può avere delle variabili come argomento� Le istruzioni che la compongono non possono
agire sulle variabili di altre funzioni e viceversa(visibilità)� Alla sua conclusione, l’esecuzione del programma
deve riprendere da dove era stata interrotta
Chiamate di funzione
� Per garantire queste caratteristiche il compilatoretraduce le chiamate di funzione in operazioni fattesullo Stack
� Inserisce (push) sullo Stack:� il Return Address: l’indirizzo a cui ritornare una volta finita
la funzione� Il Frame Pointer, che rappresenta l’indirizzo di riferimento
per tutte le variabili locali
� Opzionalmente:� Gli argomenti della funzione� Variabili locali della funzione
Call Stack
a (4 byte)
Frame Pointer (4 byte)
Return Address (4 byte)
b (4 byte)
void funzione(int a){int b;…
}
“Cima” dello Stack
Espansionedello Stack
Call Stack
a (4 byte)
Frame Pointer (4 byte)
Return Address (4 byte)
b (10 byte)
void funzione2(int a){char b[10];…
}
“Cima” dello Stack
Espansionedello Stack
Call Stack
b (8 bytes)
a (4 bytes)
Frame Pointer (4 bytes)
Return Address (4 bytes)
c (16 bytes)
void funzione3(int a){char b[8];char c[16];…
}
“Cima” dello Stack
Espansionedello Stack
Stack Buffer Overflow
� Molte funzioni per l’accesso alla memoria in linguaggi a basso/medio livello ( C/C++ ) non effettuano controlli sull’accesso alle locazionidi memoria� Questo per permettere la massima flessibilità
di utilizzo� … ma “da un grande potere derivano grandi
responsabilità” (cit.)
Stringhe
� Come si rappresenta una stringa in C?� Una stringa è una sequenza di caratteri e si
rappresenta come un array di char (interi a 8 bit)� Un array è caratterizzato da:� Dimensione: Una stringa in C è terminata da un
carattere “null string” \0� Punto di inizio: L’indirizzo iniziale della stringa è
memorizzato in un puntatore (char*)
Stringhe
…
\00x2349
o0x2348
a0x2347
i0x2346
C0x2345
…
char* saluto = “Ciao”; saluto=“0x2345”
Un char* è un puntatore ad un’area di memoria (buffer) che memorizza la stringa
Copia di stringhe
� Come si copia una stringa?� strcpy(char* dest, char* origine)
� Strcpy copia il contenuto di origine in dest� Prende il primo carattere di origine e lo copia
nella prima locazione di dest
� Prende il secondo carattere di origine e lo copia nella seconda locazione di dest
� …� E così via
Strcpy
� Strcpy ferma la copia solo quando ha esaurito tutti i caratteri di origine� Se origine > dest, inizierà a scrivere i
caratteri di origine al di fuori del buffer di dest(buffer overflow), potenzialmentedistruggendo l’esecuzione corretta del programma� Se dest è una variabile memorizzata nello
Stack allora avremmo un overflow chepotenzialmente può distruggere lo Stack
Call Stack
b (4 bytes)
a (4 bytes)
Frame Pointer (4 bytes)
Return Address (4 bytes)
c (4 bytes)
void funzione4(char* a){char b[4];char c[4];strcpy(c, a);
}
“Cima” dello Stack
Espansionedello Stack
Call Stack
… ……
\0aa
…
0x2345
Frame Pointer (4 bytes)
Return Address (4 bytes)
a
void funzione4(char* a){char b[4];char c[4];strcpy(c, a);
}
“Cima” dello Stack
Espansionedello Stack
char* a = “aaa”;
c
b
a
a = 0x2345
FP
Return
Call Stack
i …\0
ula
t
0x2345
Frame Pointer (4 bytes)
Return Address (4 bytes)
S
void funzione4(char* a){char b[4];char c[4];strcpy(c, a);
}
“Cima” dello Stack
Espansionedello Stack
char* a = “Saluti”;
c
b
a
a = 0x2345
FP
Return
!!!!
Stack Buffer Overflow
� Vengono sovrascritte altre locazioni dimemoria immediatamente successive nelloStack� Si può anche arrivare a sovrascrivere il
Return Address, modificando cosìl’esecuzione del programma
Casi di accesso non-malizioso
� Se la variabile che viene sovrascritta non è“pensata” in modo malizioso, si avrà un Return Address che punta ad un’area dimemoria che non appartiene al programma� In modalità protetta, questo significa
generare un Segmentation fault e interromprere l’esecuzione del programma
Attacco di Stack Buffer Overflow
� Per sfruttare in modo malizioso il bug, sipossono fare due cose:� Inserire nel buffer “sotto attacco” del codice
eseguibile, che faccia qualcosa di maliziosooppure esegua componenti del sistema operativo(shell)� Sovrascrivere il return address con l’indirizzo del
codice malizioso
� Al termine della funzione verrà eseguito in automatico il codice malizioso!
Proteggersi dallo Stack Buffer Overflow
� Usare funzioni “sicure” come strncpy(), cheprevedono un controllo sulla dimensionemassima del buffer� Sfruttare funzionalità dei compilatori per
produrre codice più difficile da attaccare� Sfruttare funzionalità della CPU per
“marcare” come non-eseguibili le aree dimemoria dei dati
Canarino
� Il “Canarino” (canary) è un campanellod’allarme per il buffer overflow, sfruttatoalcuni compilatori� È un numero, difficile da conoscere/scoprire,
che viene inserito nello Stack subito dopo ilReturn Address
Canarino
Canarino
b (8 bytes)
a (4 bytes)
Frame Pointer (4 bytes)
Return Address (4 bytes)
c (16 bytes)
void funzione5(int a){char b[8];char c[16];…
}
“Cima” dello Stack
Espansionedello Stack
Canarino
� Il principio è che se qualcuno/qualcosa vuolesovrascrivere il Return Address, allora dovràsovrascrivere anche il Canarino� Prima di chiamare il Return Address, il
compilatore inserisce codice che controlla se il Canarino corrisponde all’originale� Se è stato modificato, allora c’è un tentativo
di attacco in corso!
No-eXecute
� Il metodo del Canarino ha un problema: èpossibile scoprirlo e ricostruirlo� Per questo viene in aiuto l’hardware� I processori più recenti supportano un flag
per le pagine di memoria chiamato NX� No-eXecute
� Appena l’esecuzione del programma entra in una pagina marcata come NX l’esecuzione siferma con un errore
Problemi del No-eXecute
� Il flag NX risolve l’attacco Stack Buffer Overflow che abbiamo visto prima� Non può impedire però la sovrascrittura del
Return Address� Ovvero si può sovrascrivere il Return
Address per far saltare l’esecuzione in qualsiasi punto del programma
Attacco return-to-libc
� In qualunque programma vieneautomaticamente aggiunta, in fase di link, la Libreria Standard C (libc)� Pertanto si può sovrascrivere il Return
Address facendolo puntare a una funzionedella libreria C, eseguendola� Una funzione come system() (presente nella
Libreria C) permette di eseguire qualunqueprogramma del sistema attaccato
Address Space Layout Randomization
(ASLR)
� Per evitare anche questi ultimi attacchi si usala disposizione casuale degli spazi di indirizzi� I blocchi di memoria dedicati alle librerie, ai
segmenti di dati e del codice vengonodisposti in memoria in modo casuale� In questo modo è molto difficile conoscere in
anticipo l’indirizzo da inserire al posto diReturn Address
Heap Overflow
� Heap Buffer Overflow è simile alla versioneStack� È più raro, perché raramente l’heap contiene
puntatori a funzione che possono esseresovrascritti� Non per questo è meno pericoloso! Vedi
vulnerabilità JPG Microsoft
Integer Overflow
� Un Integer Overflow si ha quando sisuperano i limiti di memorizzazione di un intero� Unsigned Char � 8 bit � [0, 255]� Char � 8 bit � [-128, 127]� Unsigned Int -> 32 bit � [0, 232-1]� Int� 32 bit � [-231, 231-1]
Integer Overflow (Esempio)
� Un problema si ha con la conversioneimplicita da Signed a Unsigned� A livello di memoria, non cambia la
rappresentazione del dato ma solo la suainterpretazione
Tool per prevenzione e
attacco
Analisi della memoria
� Valgrind/DRMemory
Analisi del codice compilato
� Disassembler e Debugger� Ollydbg, IDA, gdb
Metasploit
� Piattaforma per la verifica di vulnerabilità
Riferimenti
� Mappa della memoria per differentiarchitetture: Notes on Assembly memory� “Smashing the Stack for fun and profit” di
Aleph One� SecurityFocus