際際滷

際際滷Share a Scribd company logo
Universit Degli Studi Di Catania

       Facolt di Ingegneria
   Corso di Laurea di 2属 Livello in
        Ingegneria Informatica
Corso di Sicurezza nei Sistemi Informativi




Giacomo Antonino Fazio
Cos竪 il Buffer Overflow
 Il buffer overflow (spesso abbreviato in BOF) 竪 una delle
tecniche pi湛 avanzate di hacking del software.
 Spesso si sente parlare di exploit, ossia metodi ad hoc che
utilizzano le vulnerabilit scoperte in questo o in quel software e
che permettono allutilizzatore di acquisire privilegi che non gli
spettano (ad esempio i tanto agognati privilegi di root) o di
portare al denial of service del computer attaccato. Molti di
questi exploit utilizzano per i loro scopi buffer overflow.
 Il buffer overflow consiste nel fornire ad un programma pi湛 dati
di quanti lo spazio di memoria ad essi assegnato ne possa
contenere, facendo in modo che una parte di questi dati vada
scritta in zone di memoria dove ci sono, o dovrebbero esserci,
altri dati (da ci嘆 il nome, che letteralmente significa Trabocco
dellarea di memoria).
Cos竪 il Buffer Overflow
Ad esempio, un programma definisce due variabili:
una stringa A di 8 byte e un intero B di 2 byte.
 A 竪 inizializzata con soli caratteri 0 (ognuno dei
quali occupa 1 byte, dunque sono 8 caratteri).
 B contiene il numero 3.
Cos竪 il Buffer Overflow
 Adesso supponiamo che sia previsto un inserimento della stringa A da
parte dellutente, ma che non si effettui un controllo sulla lunghezza
dellinput inserito. Proviamo ad inserire una stringa pi湛 lunga di 8
caratteri, ad esempio inseriamo excessive, che occuper 9 caratteri
pi湛 il carattere di fine stringa.
 La porzione di memoria successiva, che era occupata da B, verr
irrimediabilmente sovrascritta.




 Se si prova a leggere lintero che ci dovrebbe essere in B, un sistema
big-endian che utilizza lASCII, legger e seguita dallo 0 come 25856.
 Se invece proviamo a scrivere una stringa ancora pi湛 lunga, essa
invadere anche larea di memoria che si trova dopo di B. Risultato:
segmentation fault!!!
Cos竪 il Buffer Overflow
Un programma 竪 esposto a Buffer Overflow e
alle conseguenze che esso pu嘆 causare se:
     prevede l'input di dati di lunghezza
    variabile e non nota a priori;
     li immagazzina entro buffer allocati nel
    suo spazio di memoria dati vicino ad altre
    strutture dati vitali per il programma stesso;
     il programmatore non ha implementato
    alcun mezzo di controllo della correttezza
    dell'input inserito.
Tipi di Buffer Overflow
Esistono diversi modi per portare avanti un
BOF. Tra i pi湛 importanti:
 Arithmetic overflow: si ha quando il
risultato prodotto da un calcolo 竪 pi湛 grande
delle spazio che dovrebbe contenerlo.
 Buffer Overflow basati sulla memoria:
vengono distinti in base allarea di memoria
che vanno a interessare. Quelli pi湛 diffusi
sono i buffer overflow di heap e di stack.
Arithmetic Overflow
 Avviamo la calcolatrice di Windows scegliendo la modalit
scientifica dal menu, scriviamo -1 e premiamo su Hex.
Vedremo cos狸 il valore esadecimale di -1, che 竪
FFFFFFFFFFFFFFFF. Adesso premiamo Dec.
 Ci aspetteremmo di rivedere il nostro -1, ma invece
otteniamo il valore 18446744073709551615 e ci嘆 竪 dovuto
al fatto che la calcolatrice ha cambiato il valore da signed a
unsigned.
Struttura della memoria di un processo
Quando eseguiamo un programma, esso verr caricato in
memoria in maniera ben strutturata creando diverse zone:
    .TEXT, che contiene il codice del programma in
   esecuzione ed 竪 di sola lettura, infatti se si tentasse di
   scriverci sopra si incorrerebbe in un errore di Segmentation
   Fault;
    zona dati, che contiene le variabili globali, sia inizializzate
   (contenute in una regione detta .DATA) che non
   inizializzate (contenute in una regione detta .BSS);
    HEAP, generalmente posto dopo la zona dati, in cui
   vengono memorizzate le variabili allocate dinamicamente;
    STACK, che contiene le variabili locali, gli argomenti delle
   funzioni, le informazioni di stato del chiamante (ad esempio
   il contenuto di alcuni registri della CPU), lindirizzo di ritorno
   necessario per poter ritornare dalla funzione corrente e altre
Struttura della memoria di un processo
             Come si pu嘆 vedere dalla figura, lo heap e
            lo stack crescono in maniera diversa: il primo
            cresce verso lalto, il secondo verso il basso.
             Lo stack 竪 organizzato a pila, nel senso che
            lultimo dato inserito 竪 il primo ad essere letto
            (LIFO, Last In First Out);
             In Assembly esistono dei comandi (push e
            pop) che permettono rispettivamente di
            inserire e di prelevare valori in cima allo
            stack.
             Man mano che i dati vengono scritti nello
            stack, esso cresce verso il basso, quindi va
            da indirizzi di memoria alti ad indirizzi di
            memoria bassi.
E il processore?
Anche il processore 竪 interessato dallesecuzione del
programma, in particolare lo sono alcuni suoi registri,
strettamente legati alla situazione della memoria
durante lesecuzione:
     EBP, che 竪 il puntatore alla base dello stack e,
    nel caso stiamo eseguendo una funzione, punta
    alla base della porzione di stack utilizzata da essa;
     ESP, tramite il quale possiamo scorrere tutto lo
    stack per inserire o prelevare dati da un punto ben
    preciso di esso;
     EIP, che punta alla prossima istruzione che la
    CPU dovr eseguire dopo quella corrente.
Programma esemplificativo




Una parte del programma
     disassemblato
Programma esemplificativo
 Le prime tre istruzioni sono tre operazioni di push, che inseriscono
i valori 2, 1 e 0 nello stack (in ordine inverso).
 Successivamente si ha una CALL, utilizzata per chiamare la
funzione example, infatti si salta allindirizzo 00401234. Da notare
che, ogni qualvolta bisogna fare una CALL, il processore salva il
valore attuale di EIP nello stack e poi lo modifica per effettuare un
salto incondizionato alla funzione, in modo da poterlo ripristinare al
termine di essa, per poter riprendere lesecuzione dallistruzione
successiva alla chiamata.
 Allinterno della funzione, per prima cosa EBP viene salvato sullo
stack, in EBP viene memorizzato il valore di ESP (cio竪 linizio dello
stack per la funzione) e poi viene sottratto a ESP lo spazio
necessario per le variabili con una operazione di SUB. Le istruzioni
successive riguardano lallocazione e lassegnazione delle variabili i
e buffer, inserite nello stack seguendo come sempre la modalit
LIFO.
 Alla fine, mediante listruzione LEAVE, i registri EBP e ESP
riacquisiscono i valori che avevano prima di chiamare la CALL e,
mediante listruzione RET, si ritorna alla funzione principale
utilizzando lindirizzo di ritorno presente nello stack.
Buffer Overflow di Stack
Questo tipo di BOF 竪 quello in assoluto pi湛 diffuso e
interessa lo stack.  necessario:
 Fare in modo che il codice sia nelladdress
space del programma . Si distinguono due casi:
     Inserirlo manualmente (Code injection): il
    programma chiede in input una stringa, che verr
    inserita dallattaccante in modo da contenere il
    codice di attacco, sotto forma di istruzioni per la
    CPU.
     Il codice si trova gi l狸: il codice che ci serve 竪 gi
    presente, bisogna solo parametrizzarlo a dovere.
Buffer Overflow di Stack
 Fare in modo che il programma salti al codice di attacco e lo esegua :
     Activation Records: si utilizza allinterno di una funzione e consiste nelleffettuare
    loverflow di un buffer, con lo scopo di arrivare a sovrascrivere lEIP con lindirizzo
    del codice di attacco.
     Puntatori a funzioni: si effettua loverflow di un buffer vicino ad un puntatore, in
    modo da corrompere questultimo e da farlo puntare alla locazione del codice di
    attacco.
     Longjmp buffers: sfrutta un meccanismo presente in C che consente di salvare
    lo stato di un buffer mediante il comando setjmp(buffer) e di ripristinarlo in seguito
    mediante il comando longjmp(buffer). Se abbiamo un buffer adiacente di cui 竪
    possibile effettuare loverflow, potremmo corrompere anche lo stato del buffer di
    checkpoint in modo che, non appena viene chiamato il comando longjmp, si salta
    alla locazione del codice di attacco.

 Spesso linserimento del codice di attacco e la sua esecuzione sono effettuati in una
volta sola, ma non necessariamente.

 Lo shellcode 竪 un pezzo di codice macchina eseguito per sfruttare una vulnerabilit.
Deve essere altamente specifico e verificato nei minimi dettagli.
Esempio 1 di BOF di Stack
                Il programma non fa altro che
                chiamare la funzione example(),
                la quale alloca la variabile
                command con valore calc, che
                rappresenta il comando che
                vogliamo eseguire (la semplice
                calcolatrice     di    Windows).
                Successivamente viene allocata
                la variabile name, in cui
                vogliamo inserire un nome da
                dare allo script, cosa che viene
                fatta richiamando la funzione
                gets(). La restante parte serve
                per farci capire cosa sta
                succedendo in memoria, infatti ci
                mostra unistantanea dello stack.
Esempio 1 di BOF di Stack
Se come nome dello script inseriamo hello:



                                                   Tutto ok




                                               Abbiamo inserito
                                               hello nella
                                               variabile name e
                                               poi abbiamo
                                               eseguito il
                                               contenuto di
                                               command, cio竪
                                               calc.
Esempio 1 di BOF di Stack
Se come nome dello script inseriamo xxxxxxxxxxxxxxxxcmd :



                                                         Buffer overflow e
                                                         shell in locale!!!



                                                        Abbiamo scritto
                                                        al di l della
                                                        variabile name,
                                                        sovrascrivendo
                                                        anche command
                                                        che adesso
                                                        contiene cmd
Esempio 2 di BOF di Stack
             Il programma prende una stringa in
            ingresso e la inserisce allinterno della
            variabile var, utilizzando la funzione
            strcpy(). La funzione function() non viene
            mai chiamata dal programma. Il nostro
            obiettivo sar quello di causare un buffer
            overflow, inserendo in ingresso una stringa
            pi湛 lunga dei 10 caratteri a disposizione e di
            sostituire lindirizzo di ritorno di main() con
            quello della funzione function(), in modo che
            essa venga eseguita.
             Facendo un po di prove, ci accorgiamo
            che il programma va in segmentation fault
            non appena inseriamo 14 caratteri.
             Se diamo in input 14 caratteri e lindirizzo
            della funzione function() (che possiamo
            trovare disassemblando il codice) il gioco
            竪 fatto!
Esempio 2 di BOF di Stack
             Come fare a passargli lindirizzo? Esso
             infatti 竪 scritto in caratteri esadecimali e
             non ASCII. Scriviamo un piccolo
             exploit, che si occupa di convertire in
             ASCII e di passare al programma
             lindirizzo     da     noi      inserito  in
             esadecimale.
Buffer Overflow di Heap
Questo tipo di BOF 竪 noto e sfruttato da molto tempo, ma se ne parla
sempre meno di quello di stack, soprattutto perch辿 竪 in genere pi湛 difficile
da sfruttare rispetto a questultimo. Esistono diverse tecniche per portarlo
avanti:
 Attacchi basati su malloc() e funzioni simili: per ogni variabile allocata
dinamicamente, viene allocato uno spazio di lunghezza prestabilita ma se
non ci sono controlli, 竪 molto semplice scrivere oltre esso, sovrascrivendo
larea adiacente occupata possibilmente da unaltra variabile.
 Attacchi basati sulla sovrascrittura di puntatori: si effettua loverflow di
un buffer adiacente ad un puntatore in modo da corrompere questultimo
e farlo puntare alla locazione del codice di attacco.
 Attacchi basati su puntatori a funzioni: si effettua loverflow di un buffer
vicino ad un puntatore a funzione, in modo da corrompere questultimo e
farlo puntare alla funzione di attacco.
Esempio di BOF di Heap




Il programma appartiene allutente root ma 竪 impostato il bit SUID, che
consente a chiunque di eseguirlo con privilegi di root.
In particolare, il programma alloca una parte di memoria nello heap e vi copia
dentro lo shellcode. Subito dopo lindirizzo di ritorno del main 竪 sovrascritto
dallindirizzo dello shellcode, in modo che quando il main ritorna, fornisce una
shell.
Alla ricerca di Buffer Overflow
 Gli esempi finora visti sono scritti per puro scopo didattico,
in quanto non troveremo in giro programmi cos狸, pronti per
essere sfruttati per accedere al sistema di turno.
 Chi attacca generalmente non prova a casaccio, analizza il
codice del programma alla ricerca di vulnerabilit da
sfruttare, o aspetta che sia qualcun altro a farlo.
 Quando si sa che la versione x del programma y 竪 affetta
da una certa vulnerabilit, allora 竪 il momento di creare
lexploit che permetta di utilizzarla.
 Lanalisi del codice pu嘆 essere fatta a diversi livelli:
lessicale, semantico, basato su tecniche di intelligenza
artificiale, a runtime, reverse engineering, ricerca di bug
specifici, ecc.
Blaster: un worm costruito su un BOF

 Rilevato l11 Agosto 2003 sui primi computer, si diffuse a
macchia dolio nel giro di appena 2 giorni
 Infetta i computer con SO Microsoft Windows XP o 2000
 Lobiettivo finale era colpire Microsoft, mediante un attacco
DDoS al sito di Windows Update.
 Lavversione nei confronti di Microsoft 竪 dimostrata anche
dalla stringa trovata nel codice del worm: billy gates why do
you make this possible ? Stop making money and fix your
software!!
 Effetti: danni in tutto il mondo per oltre 3 milioni di dollari
 Colpevole: un ragazzo di 18 anni del Minnesota (USA)
Blaster: un worm costruito su un BOF
 Blaster si sviluppa su una vulnerabilit descritta da
Microsoft stessa nel Microsoft Security Bulletin MS03-026.
Si tratta di una falla nellinterfaccia RPC (Remote
Procedure Call) di un oggetto DCOM (Distributed
Component Object Model).
 DCOM: tecnologia che abilita componenti software che
non si trovano sulla stessa macchina a comunicare
direttamente utilizzando una rete;
 RPC: protocollo usato per la comunicazione e la
richiesta di servizi tra le due parti di software, permettendo
ad un programma che gira su un certo computer di
eseguire codice su un sistema remoto.
Blaster: un worm costruito su un BOF
 Se la richiesta di comunicazione viene posta
allinterfaccia RPC in modo errato, ci possono essere
problemi perch辿 essa non controlla opportunamente le
dimensioni dei messaggi ricevuti in input.
 Un malintenzionato potrebbe scrivere un exploit che
invia alloggetto DCOM un messaggio non corretto e
costruito in modo da causare un buffer overflow, che gli
permetterebbe di avere il controllo completo sulla
macchina. Per poter fare ci嘆, il malintenzionato deve
utilizzare una tra le porte aperte per RPC, tra cui 135,
139, 445 e 593.
 Blaster fa proprio questo!!!
Blaster: un worm costruito su un BOF
Supponiamo di avere un computer infetto. Il tutto si svolge in diverse fasi:
     Attesa: A deve prima controllare di essere connesso ad Internet, mediante la
    funzione InternetGetConnectedState(). Se lesito 竪 positivo, si va alla fase 2
     Generazione indirizzi IP: il programma genera gli indirizzi IP dei computer a cui
    lanciare il contagio. Effettua uno scan di essi alla ricerca di computer vulnerabili, tra cui
    supponiamo ci sia un ipotetico B.
     Attacco al RPC: utilizzando la porta TCP 135, A invia pacchetti formulati in modo
    errato (ma costruiti ad hoc per ottenere leffetto nefasto) al servizio RPC/DCOM di B
    che, essendo affetto dalla falla, non effettua controlli sulla lunghezza di essi. Risultato:
    buffer overflow!!!
     Controllo del contagio: attraverso la porta 135, A controlla se B 竪 gi infetto e in
    caso affermativo termina; in caso negativo, invece, attiva i socket per comunicare con
    B.
     La shell CMD.EXE: A questo punto, A lancia su B la shell tramite il comando
    cmd.exe, necessaria ad A per far eseguire a B dei comandi.
     Download del worm: tramite la shell, B richiede ad A leseguibile msblast.exe, che
    scarica nella cartella %systemroot%/system32 (cartella di sistema). Il file appena
    scaricato viene lanciato.
     Aggiornamento delle Registry Keys: utilizzando la shell lanciata nella fase 5, A
    apporta delle modifiche ad alcune Registry Keys di B, in modo che il worm venga
    eseguito ad ogni avvio del pc.
Blaster: un worm costruito su un BOF

 Il worm 竪 stato progettato affinch竪 i computer infetti
effettuino un attacco DDoS in momenti ben precisi: ogni
giorno (nel caso di mesi compresi tra Settembre e
Dicembre) e dal 16 del mese in poi per gli altri mesi.
 I sintomi che permettono di accorgersi della presenza di
Blaster sul proprio sistema sono: prestazioni ridotte,
continui riavvii, traffico irregolare sulle porte TCP 135 e
4444 e UDP 69.
 Presenza di tool per leliminazione automatica del worm
 Rilevate ad oggi 4 varianti: Lovesan A, Lovesan B,
Lovesan C e Lovesan F.
Difesa contro i BOF e nuovi attacchi
Diverse tecniche sono state inventate per cercare di frenare il pi湛 possibile i buffer
overflow e nuovi attacchi sono stati messi a punto per bypassare esse.
 Difesa - scelta del linguaggio di programmazione: C e C++ non
forniscono la giusta protezione contro laccesso e la sovrascrittura dei dati in
memoria (attraverso i puntatori 竪 possibile praticamente spostarsi e scrivere in
memoria pressoch辿 dovunque) e contro la scrittura in un array al di fuori dei suoi
confini (竪 il problema principale che causa il buffer overflow). Altri linguaggi
effettuano controlli (es. Java, Python, Ada, Lisp).
 Difesa - Scrivere codice corretto: utopia! Per quanto si possa controllare il
proprio codice, i bug potrebbero risiedere nelle funzioni delle librerie utilizzate.
 Difesa  Attenzione ai programmi SUID: programmi che vanno in
esecuzione con privilegi di root, chiunque sia ad eseguirli. Alcuni di essi sono
necessari per effettuare operazioni comuni, altri non lo sono affatto o non vengono
mai usati, ma possono rappresentare un problema, dato che possono essere
sfruttati da un malintenzionato attraverso un buffer overflow, al termine del quale
si trover con privilegi di root e quindi avr il controllo della macchina.
Difesa contro i BOF e nuovi attacchi
 Difesa - uso di librerie safe: sostituiscono le funzioni incriminate di LibC
(es. gets(), scanf(), printf(), ecc.) con versioni safe (in teoria) offrendo in alcuni
casi una completa nuova implementazione delle stringhe. Es.:
     Libsafe
     The Better String Library
     Arri Buffer API
     Vstr
     Funzione strlcpy()

 Difesa  Protezione contro lo stack smashing: viene scritto nello stack
un canary, cio竪 un valore noto sistemato tra un buffer e i dati di controllo. In caso di
buffer overflow, il canary viene sovrascritto, dunque al ritorno dalla funzione ci si
accorge dellavvenuta manipolazione dello stack ed 竪 possibile correre ai ripari. 3 tipi di
canaries: Terminator, Random e Random XOR canaries. Esempio di programmi che
implementano questo tipo di protezione:
      ProPolice
      StackGuard
      StackGhost
Difesa contro i BOF e nuovi attacchi
 Difesa  Protezione dello spazio eseguibile: protezione
implementata sia a livello hardware che software. Lidea 竪 quella di rendere
parte della memoria non scrivibile o non eseguibile, in modo da evitare la
maggior parte dei BOF, ad esempio quelli basati sulla code injection (se la
memoria non 竪 eseguibile, inserisco il codice di attacco ma non posso
eseguirlo).
      Soluzione hardware: NX bit
      Soluzione software: DEP (Windows), W^X (OpenBSD), PaX (Linux),
     Exec Shield (Linux).

 Nuovo attacco  Gli attacchi return-to-libc: Lo scopo 竪 quello di
sovrascrivere lindirizzo di ritorno di una funzione non con quello della
locazione di memoria dove si trova lo shellcode, bens狸 con quello di una
funzione di libC, spesso system(), magari passandogli come argomento
qualcosa come /bin/sh (che ci d una shell in locale). In questo modo non 竪
necessario eseguire codice che si trova nello stack o nello heap, aggirando
quindi lostacolo rappresentato dalla protezione dello spazio eseguibile.
Difesa contro i BOF e nuovi attacchi
 Difesa  Address Space Layout Randomization (ASLR):
lidea di base 竪 quella di organizzare alcune parti chiave della
memoria di un processo (ad esempio stack, heap, librerie e parti
eseguibili) in maniera casuale nelladdress space di un processo. Ci嘆
rende difficili alcuni tipi di attacco, in particolare gli activation records
e i return-to-libc, a causa della difficolt di trovare lindirizzo del codice
da eseguire. Implementata anche in PaX e Exec Shield.

 Difesa  Deep Packet Inspection (DPI): consente di
esaminare i pacchetti che transitano in una rete, confrontandoli con le
informazioni a disposizione presenti in un database e riguardanti
attacchi conosciuti. Ci嘆 permette di trovare gli eventuali pacchetti che
portano le tracce di un buffer overflow o di un altro tipo di attacco e di
evitare che passino. Utile ma spesso poco efficace: previene solo gli
attacchi conosciuti.
Difesa contro i BOF e nuovi attacchi
Difesa  Intrusion Detection Systems (IDS): riconoscono i pacchetti che
transitano in rete e che mirano ad effettuare manipolazioni sui sistemi o attacchi contro
servizi vulnerabili e applicazioni. Sono composti da diverse parti:
      Sensori: osservano gli eventi che avvengono sul sistema;
      Analizzatori: analizzano gli eventi passati loro dai sensori;
      Gestore: riceve gli eventi degni di nota dagli analizzatori e prende provvedimenti
     sia passivi che attivi.

 Nuovo attacco  Shellcode alfanumerici, polimorfici, metamorfici e
automodificanti: utilizzano tecniche spesso messe in pratica dai worm per non farsi
scovare. In particolare:
     Shellcode polimorfici: variano continuamente, lasciando immutato lalgoritmo
    originale. Spesso per ottenere ci嘆 utilizzano la crittografia, lasciando per嘆 una
    parte non criptata che contiene le informazioni per decriptare il resto. Gli IDS
    mirano a riconoscere proprio questa parte, attraverso una scansione basata su
    pattern.
     Shellcode metamorfici: ancora peggio di quelli polimorfici, con lobiettivo di
    vanificare le scansioni degli IDS basate su pattern.
Difesa contro i BOF e nuovi attacchi
Conclusioni: La soluzione non esiste!!!

 Scrivere codice corretto 竪 unutopia, perch辿 竪 facile
sbagliare o commettere una leggerezza o utilizzare codice
di terzi che involontariamente contiene dei bug.
  possibile comunque affrontare il problema, sia
utilizzando il buon senso, che mediante svariate tecniche
che si possono spesso combinare tra loro.
 Ricordare che se qualcuno lavora per produrre armi che
possano competere con le armi del nemico, il nemico non
sta con le mani in mano e nello stesso tempo lavora per
migliorare le sue.

More Related Content

Attacchi alle applicazioni basati su buffer overflow

  • 1. Universit Degli Studi Di Catania Facolt di Ingegneria Corso di Laurea di 2属 Livello in Ingegneria Informatica Corso di Sicurezza nei Sistemi Informativi Giacomo Antonino Fazio
  • 2. Cos竪 il Buffer Overflow Il buffer overflow (spesso abbreviato in BOF) 竪 una delle tecniche pi湛 avanzate di hacking del software. Spesso si sente parlare di exploit, ossia metodi ad hoc che utilizzano le vulnerabilit scoperte in questo o in quel software e che permettono allutilizzatore di acquisire privilegi che non gli spettano (ad esempio i tanto agognati privilegi di root) o di portare al denial of service del computer attaccato. Molti di questi exploit utilizzano per i loro scopi buffer overflow. Il buffer overflow consiste nel fornire ad un programma pi湛 dati di quanti lo spazio di memoria ad essi assegnato ne possa contenere, facendo in modo che una parte di questi dati vada scritta in zone di memoria dove ci sono, o dovrebbero esserci, altri dati (da ci嘆 il nome, che letteralmente significa Trabocco dellarea di memoria).
  • 3. Cos竪 il Buffer Overflow Ad esempio, un programma definisce due variabili: una stringa A di 8 byte e un intero B di 2 byte. A 竪 inizializzata con soli caratteri 0 (ognuno dei quali occupa 1 byte, dunque sono 8 caratteri). B contiene il numero 3.
  • 4. Cos竪 il Buffer Overflow Adesso supponiamo che sia previsto un inserimento della stringa A da parte dellutente, ma che non si effettui un controllo sulla lunghezza dellinput inserito. Proviamo ad inserire una stringa pi湛 lunga di 8 caratteri, ad esempio inseriamo excessive, che occuper 9 caratteri pi湛 il carattere di fine stringa. La porzione di memoria successiva, che era occupata da B, verr irrimediabilmente sovrascritta. Se si prova a leggere lintero che ci dovrebbe essere in B, un sistema big-endian che utilizza lASCII, legger e seguita dallo 0 come 25856. Se invece proviamo a scrivere una stringa ancora pi湛 lunga, essa invadere anche larea di memoria che si trova dopo di B. Risultato: segmentation fault!!!
  • 5. Cos竪 il Buffer Overflow Un programma 竪 esposto a Buffer Overflow e alle conseguenze che esso pu嘆 causare se: prevede l'input di dati di lunghezza variabile e non nota a priori; li immagazzina entro buffer allocati nel suo spazio di memoria dati vicino ad altre strutture dati vitali per il programma stesso; il programmatore non ha implementato alcun mezzo di controllo della correttezza dell'input inserito.
  • 6. Tipi di Buffer Overflow Esistono diversi modi per portare avanti un BOF. Tra i pi湛 importanti: Arithmetic overflow: si ha quando il risultato prodotto da un calcolo 竪 pi湛 grande delle spazio che dovrebbe contenerlo. Buffer Overflow basati sulla memoria: vengono distinti in base allarea di memoria che vanno a interessare. Quelli pi湛 diffusi sono i buffer overflow di heap e di stack.
  • 7. Arithmetic Overflow Avviamo la calcolatrice di Windows scegliendo la modalit scientifica dal menu, scriviamo -1 e premiamo su Hex. Vedremo cos狸 il valore esadecimale di -1, che 竪 FFFFFFFFFFFFFFFF. Adesso premiamo Dec. Ci aspetteremmo di rivedere il nostro -1, ma invece otteniamo il valore 18446744073709551615 e ci嘆 竪 dovuto al fatto che la calcolatrice ha cambiato il valore da signed a unsigned.
  • 8. Struttura della memoria di un processo Quando eseguiamo un programma, esso verr caricato in memoria in maniera ben strutturata creando diverse zone: .TEXT, che contiene il codice del programma in esecuzione ed 竪 di sola lettura, infatti se si tentasse di scriverci sopra si incorrerebbe in un errore di Segmentation Fault; zona dati, che contiene le variabili globali, sia inizializzate (contenute in una regione detta .DATA) che non inizializzate (contenute in una regione detta .BSS); HEAP, generalmente posto dopo la zona dati, in cui vengono memorizzate le variabili allocate dinamicamente; STACK, che contiene le variabili locali, gli argomenti delle funzioni, le informazioni di stato del chiamante (ad esempio il contenuto di alcuni registri della CPU), lindirizzo di ritorno necessario per poter ritornare dalla funzione corrente e altre
  • 9. Struttura della memoria di un processo Come si pu嘆 vedere dalla figura, lo heap e lo stack crescono in maniera diversa: il primo cresce verso lalto, il secondo verso il basso. Lo stack 竪 organizzato a pila, nel senso che lultimo dato inserito 竪 il primo ad essere letto (LIFO, Last In First Out); In Assembly esistono dei comandi (push e pop) che permettono rispettivamente di inserire e di prelevare valori in cima allo stack. Man mano che i dati vengono scritti nello stack, esso cresce verso il basso, quindi va da indirizzi di memoria alti ad indirizzi di memoria bassi.
  • 10. E il processore? Anche il processore 竪 interessato dallesecuzione del programma, in particolare lo sono alcuni suoi registri, strettamente legati alla situazione della memoria durante lesecuzione: EBP, che 竪 il puntatore alla base dello stack e, nel caso stiamo eseguendo una funzione, punta alla base della porzione di stack utilizzata da essa; ESP, tramite il quale possiamo scorrere tutto lo stack per inserire o prelevare dati da un punto ben preciso di esso; EIP, che punta alla prossima istruzione che la CPU dovr eseguire dopo quella corrente.
  • 11. Programma esemplificativo Una parte del programma disassemblato
  • 12. Programma esemplificativo Le prime tre istruzioni sono tre operazioni di push, che inseriscono i valori 2, 1 e 0 nello stack (in ordine inverso). Successivamente si ha una CALL, utilizzata per chiamare la funzione example, infatti si salta allindirizzo 00401234. Da notare che, ogni qualvolta bisogna fare una CALL, il processore salva il valore attuale di EIP nello stack e poi lo modifica per effettuare un salto incondizionato alla funzione, in modo da poterlo ripristinare al termine di essa, per poter riprendere lesecuzione dallistruzione successiva alla chiamata. Allinterno della funzione, per prima cosa EBP viene salvato sullo stack, in EBP viene memorizzato il valore di ESP (cio竪 linizio dello stack per la funzione) e poi viene sottratto a ESP lo spazio necessario per le variabili con una operazione di SUB. Le istruzioni successive riguardano lallocazione e lassegnazione delle variabili i e buffer, inserite nello stack seguendo come sempre la modalit LIFO. Alla fine, mediante listruzione LEAVE, i registri EBP e ESP riacquisiscono i valori che avevano prima di chiamare la CALL e, mediante listruzione RET, si ritorna alla funzione principale utilizzando lindirizzo di ritorno presente nello stack.
  • 13. Buffer Overflow di Stack Questo tipo di BOF 竪 quello in assoluto pi湛 diffuso e interessa lo stack. necessario: Fare in modo che il codice sia nelladdress space del programma . Si distinguono due casi: Inserirlo manualmente (Code injection): il programma chiede in input una stringa, che verr inserita dallattaccante in modo da contenere il codice di attacco, sotto forma di istruzioni per la CPU. Il codice si trova gi l狸: il codice che ci serve 竪 gi presente, bisogna solo parametrizzarlo a dovere.
  • 14. Buffer Overflow di Stack Fare in modo che il programma salti al codice di attacco e lo esegua : Activation Records: si utilizza allinterno di una funzione e consiste nelleffettuare loverflow di un buffer, con lo scopo di arrivare a sovrascrivere lEIP con lindirizzo del codice di attacco. Puntatori a funzioni: si effettua loverflow di un buffer vicino ad un puntatore, in modo da corrompere questultimo e da farlo puntare alla locazione del codice di attacco. Longjmp buffers: sfrutta un meccanismo presente in C che consente di salvare lo stato di un buffer mediante il comando setjmp(buffer) e di ripristinarlo in seguito mediante il comando longjmp(buffer). Se abbiamo un buffer adiacente di cui 竪 possibile effettuare loverflow, potremmo corrompere anche lo stato del buffer di checkpoint in modo che, non appena viene chiamato il comando longjmp, si salta alla locazione del codice di attacco. Spesso linserimento del codice di attacco e la sua esecuzione sono effettuati in una volta sola, ma non necessariamente. Lo shellcode 竪 un pezzo di codice macchina eseguito per sfruttare una vulnerabilit. Deve essere altamente specifico e verificato nei minimi dettagli.
  • 15. Esempio 1 di BOF di Stack Il programma non fa altro che chiamare la funzione example(), la quale alloca la variabile command con valore calc, che rappresenta il comando che vogliamo eseguire (la semplice calcolatrice di Windows). Successivamente viene allocata la variabile name, in cui vogliamo inserire un nome da dare allo script, cosa che viene fatta richiamando la funzione gets(). La restante parte serve per farci capire cosa sta succedendo in memoria, infatti ci mostra unistantanea dello stack.
  • 16. Esempio 1 di BOF di Stack Se come nome dello script inseriamo hello: Tutto ok Abbiamo inserito hello nella variabile name e poi abbiamo eseguito il contenuto di command, cio竪 calc.
  • 17. Esempio 1 di BOF di Stack Se come nome dello script inseriamo xxxxxxxxxxxxxxxxcmd : Buffer overflow e shell in locale!!! Abbiamo scritto al di l della variabile name, sovrascrivendo anche command che adesso contiene cmd
  • 18. Esempio 2 di BOF di Stack Il programma prende una stringa in ingresso e la inserisce allinterno della variabile var, utilizzando la funzione strcpy(). La funzione function() non viene mai chiamata dal programma. Il nostro obiettivo sar quello di causare un buffer overflow, inserendo in ingresso una stringa pi湛 lunga dei 10 caratteri a disposizione e di sostituire lindirizzo di ritorno di main() con quello della funzione function(), in modo che essa venga eseguita. Facendo un po di prove, ci accorgiamo che il programma va in segmentation fault non appena inseriamo 14 caratteri. Se diamo in input 14 caratteri e lindirizzo della funzione function() (che possiamo trovare disassemblando il codice) il gioco 竪 fatto!
  • 19. Esempio 2 di BOF di Stack Come fare a passargli lindirizzo? Esso infatti 竪 scritto in caratteri esadecimali e non ASCII. Scriviamo un piccolo exploit, che si occupa di convertire in ASCII e di passare al programma lindirizzo da noi inserito in esadecimale.
  • 20. Buffer Overflow di Heap Questo tipo di BOF 竪 noto e sfruttato da molto tempo, ma se ne parla sempre meno di quello di stack, soprattutto perch辿 竪 in genere pi湛 difficile da sfruttare rispetto a questultimo. Esistono diverse tecniche per portarlo avanti: Attacchi basati su malloc() e funzioni simili: per ogni variabile allocata dinamicamente, viene allocato uno spazio di lunghezza prestabilita ma se non ci sono controlli, 竪 molto semplice scrivere oltre esso, sovrascrivendo larea adiacente occupata possibilmente da unaltra variabile. Attacchi basati sulla sovrascrittura di puntatori: si effettua loverflow di un buffer adiacente ad un puntatore in modo da corrompere questultimo e farlo puntare alla locazione del codice di attacco. Attacchi basati su puntatori a funzioni: si effettua loverflow di un buffer vicino ad un puntatore a funzione, in modo da corrompere questultimo e farlo puntare alla funzione di attacco.
  • 21. Esempio di BOF di Heap Il programma appartiene allutente root ma 竪 impostato il bit SUID, che consente a chiunque di eseguirlo con privilegi di root. In particolare, il programma alloca una parte di memoria nello heap e vi copia dentro lo shellcode. Subito dopo lindirizzo di ritorno del main 竪 sovrascritto dallindirizzo dello shellcode, in modo che quando il main ritorna, fornisce una shell.
  • 22. Alla ricerca di Buffer Overflow Gli esempi finora visti sono scritti per puro scopo didattico, in quanto non troveremo in giro programmi cos狸, pronti per essere sfruttati per accedere al sistema di turno. Chi attacca generalmente non prova a casaccio, analizza il codice del programma alla ricerca di vulnerabilit da sfruttare, o aspetta che sia qualcun altro a farlo. Quando si sa che la versione x del programma y 竪 affetta da una certa vulnerabilit, allora 竪 il momento di creare lexploit che permetta di utilizzarla. Lanalisi del codice pu嘆 essere fatta a diversi livelli: lessicale, semantico, basato su tecniche di intelligenza artificiale, a runtime, reverse engineering, ricerca di bug specifici, ecc.
  • 23. Blaster: un worm costruito su un BOF Rilevato l11 Agosto 2003 sui primi computer, si diffuse a macchia dolio nel giro di appena 2 giorni Infetta i computer con SO Microsoft Windows XP o 2000 Lobiettivo finale era colpire Microsoft, mediante un attacco DDoS al sito di Windows Update. Lavversione nei confronti di Microsoft 竪 dimostrata anche dalla stringa trovata nel codice del worm: billy gates why do you make this possible ? Stop making money and fix your software!! Effetti: danni in tutto il mondo per oltre 3 milioni di dollari Colpevole: un ragazzo di 18 anni del Minnesota (USA)
  • 24. Blaster: un worm costruito su un BOF Blaster si sviluppa su una vulnerabilit descritta da Microsoft stessa nel Microsoft Security Bulletin MS03-026. Si tratta di una falla nellinterfaccia RPC (Remote Procedure Call) di un oggetto DCOM (Distributed Component Object Model). DCOM: tecnologia che abilita componenti software che non si trovano sulla stessa macchina a comunicare direttamente utilizzando una rete; RPC: protocollo usato per la comunicazione e la richiesta di servizi tra le due parti di software, permettendo ad un programma che gira su un certo computer di eseguire codice su un sistema remoto.
  • 25. Blaster: un worm costruito su un BOF Se la richiesta di comunicazione viene posta allinterfaccia RPC in modo errato, ci possono essere problemi perch辿 essa non controlla opportunamente le dimensioni dei messaggi ricevuti in input. Un malintenzionato potrebbe scrivere un exploit che invia alloggetto DCOM un messaggio non corretto e costruito in modo da causare un buffer overflow, che gli permetterebbe di avere il controllo completo sulla macchina. Per poter fare ci嘆, il malintenzionato deve utilizzare una tra le porte aperte per RPC, tra cui 135, 139, 445 e 593. Blaster fa proprio questo!!!
  • 26. Blaster: un worm costruito su un BOF Supponiamo di avere un computer infetto. Il tutto si svolge in diverse fasi: Attesa: A deve prima controllare di essere connesso ad Internet, mediante la funzione InternetGetConnectedState(). Se lesito 竪 positivo, si va alla fase 2 Generazione indirizzi IP: il programma genera gli indirizzi IP dei computer a cui lanciare il contagio. Effettua uno scan di essi alla ricerca di computer vulnerabili, tra cui supponiamo ci sia un ipotetico B. Attacco al RPC: utilizzando la porta TCP 135, A invia pacchetti formulati in modo errato (ma costruiti ad hoc per ottenere leffetto nefasto) al servizio RPC/DCOM di B che, essendo affetto dalla falla, non effettua controlli sulla lunghezza di essi. Risultato: buffer overflow!!! Controllo del contagio: attraverso la porta 135, A controlla se B 竪 gi infetto e in caso affermativo termina; in caso negativo, invece, attiva i socket per comunicare con B. La shell CMD.EXE: A questo punto, A lancia su B la shell tramite il comando cmd.exe, necessaria ad A per far eseguire a B dei comandi. Download del worm: tramite la shell, B richiede ad A leseguibile msblast.exe, che scarica nella cartella %systemroot%/system32 (cartella di sistema). Il file appena scaricato viene lanciato. Aggiornamento delle Registry Keys: utilizzando la shell lanciata nella fase 5, A apporta delle modifiche ad alcune Registry Keys di B, in modo che il worm venga eseguito ad ogni avvio del pc.
  • 27. Blaster: un worm costruito su un BOF Il worm 竪 stato progettato affinch竪 i computer infetti effettuino un attacco DDoS in momenti ben precisi: ogni giorno (nel caso di mesi compresi tra Settembre e Dicembre) e dal 16 del mese in poi per gli altri mesi. I sintomi che permettono di accorgersi della presenza di Blaster sul proprio sistema sono: prestazioni ridotte, continui riavvii, traffico irregolare sulle porte TCP 135 e 4444 e UDP 69. Presenza di tool per leliminazione automatica del worm Rilevate ad oggi 4 varianti: Lovesan A, Lovesan B, Lovesan C e Lovesan F.
  • 28. Difesa contro i BOF e nuovi attacchi Diverse tecniche sono state inventate per cercare di frenare il pi湛 possibile i buffer overflow e nuovi attacchi sono stati messi a punto per bypassare esse. Difesa - scelta del linguaggio di programmazione: C e C++ non forniscono la giusta protezione contro laccesso e la sovrascrittura dei dati in memoria (attraverso i puntatori 竪 possibile praticamente spostarsi e scrivere in memoria pressoch辿 dovunque) e contro la scrittura in un array al di fuori dei suoi confini (竪 il problema principale che causa il buffer overflow). Altri linguaggi effettuano controlli (es. Java, Python, Ada, Lisp). Difesa - Scrivere codice corretto: utopia! Per quanto si possa controllare il proprio codice, i bug potrebbero risiedere nelle funzioni delle librerie utilizzate. Difesa Attenzione ai programmi SUID: programmi che vanno in esecuzione con privilegi di root, chiunque sia ad eseguirli. Alcuni di essi sono necessari per effettuare operazioni comuni, altri non lo sono affatto o non vengono mai usati, ma possono rappresentare un problema, dato che possono essere sfruttati da un malintenzionato attraverso un buffer overflow, al termine del quale si trover con privilegi di root e quindi avr il controllo della macchina.
  • 29. Difesa contro i BOF e nuovi attacchi Difesa - uso di librerie safe: sostituiscono le funzioni incriminate di LibC (es. gets(), scanf(), printf(), ecc.) con versioni safe (in teoria) offrendo in alcuni casi una completa nuova implementazione delle stringhe. Es.: Libsafe The Better String Library Arri Buffer API Vstr Funzione strlcpy() Difesa Protezione contro lo stack smashing: viene scritto nello stack un canary, cio竪 un valore noto sistemato tra un buffer e i dati di controllo. In caso di buffer overflow, il canary viene sovrascritto, dunque al ritorno dalla funzione ci si accorge dellavvenuta manipolazione dello stack ed 竪 possibile correre ai ripari. 3 tipi di canaries: Terminator, Random e Random XOR canaries. Esempio di programmi che implementano questo tipo di protezione: ProPolice StackGuard StackGhost
  • 30. Difesa contro i BOF e nuovi attacchi Difesa Protezione dello spazio eseguibile: protezione implementata sia a livello hardware che software. Lidea 竪 quella di rendere parte della memoria non scrivibile o non eseguibile, in modo da evitare la maggior parte dei BOF, ad esempio quelli basati sulla code injection (se la memoria non 竪 eseguibile, inserisco il codice di attacco ma non posso eseguirlo). Soluzione hardware: NX bit Soluzione software: DEP (Windows), W^X (OpenBSD), PaX (Linux), Exec Shield (Linux). Nuovo attacco Gli attacchi return-to-libc: Lo scopo 竪 quello di sovrascrivere lindirizzo di ritorno di una funzione non con quello della locazione di memoria dove si trova lo shellcode, bens狸 con quello di una funzione di libC, spesso system(), magari passandogli come argomento qualcosa come /bin/sh (che ci d una shell in locale). In questo modo non 竪 necessario eseguire codice che si trova nello stack o nello heap, aggirando quindi lostacolo rappresentato dalla protezione dello spazio eseguibile.
  • 31. Difesa contro i BOF e nuovi attacchi Difesa Address Space Layout Randomization (ASLR): lidea di base 竪 quella di organizzare alcune parti chiave della memoria di un processo (ad esempio stack, heap, librerie e parti eseguibili) in maniera casuale nelladdress space di un processo. Ci嘆 rende difficili alcuni tipi di attacco, in particolare gli activation records e i return-to-libc, a causa della difficolt di trovare lindirizzo del codice da eseguire. Implementata anche in PaX e Exec Shield. Difesa Deep Packet Inspection (DPI): consente di esaminare i pacchetti che transitano in una rete, confrontandoli con le informazioni a disposizione presenti in un database e riguardanti attacchi conosciuti. Ci嘆 permette di trovare gli eventuali pacchetti che portano le tracce di un buffer overflow o di un altro tipo di attacco e di evitare che passino. Utile ma spesso poco efficace: previene solo gli attacchi conosciuti.
  • 32. Difesa contro i BOF e nuovi attacchi Difesa Intrusion Detection Systems (IDS): riconoscono i pacchetti che transitano in rete e che mirano ad effettuare manipolazioni sui sistemi o attacchi contro servizi vulnerabili e applicazioni. Sono composti da diverse parti: Sensori: osservano gli eventi che avvengono sul sistema; Analizzatori: analizzano gli eventi passati loro dai sensori; Gestore: riceve gli eventi degni di nota dagli analizzatori e prende provvedimenti sia passivi che attivi. Nuovo attacco Shellcode alfanumerici, polimorfici, metamorfici e automodificanti: utilizzano tecniche spesso messe in pratica dai worm per non farsi scovare. In particolare: Shellcode polimorfici: variano continuamente, lasciando immutato lalgoritmo originale. Spesso per ottenere ci嘆 utilizzano la crittografia, lasciando per嘆 una parte non criptata che contiene le informazioni per decriptare il resto. Gli IDS mirano a riconoscere proprio questa parte, attraverso una scansione basata su pattern. Shellcode metamorfici: ancora peggio di quelli polimorfici, con lobiettivo di vanificare le scansioni degli IDS basate su pattern.
  • 33. Difesa contro i BOF e nuovi attacchi Conclusioni: La soluzione non esiste!!! Scrivere codice corretto 竪 unutopia, perch辿 竪 facile sbagliare o commettere una leggerezza o utilizzare codice di terzi che involontariamente contiene dei bug. possibile comunque affrontare il problema, sia utilizzando il buon senso, che mediante svariate tecniche che si possono spesso combinare tra loro. Ricordare che se qualcuno lavora per produrre armi che possano competere con le armi del nemico, il nemico non sta con le mani in mano e nello stesso tempo lavora per migliorare le sue.