Lezione del 19/03/2024

Sta facendo un ripasso delle cose che sono state viste la scorsa lezione, quindi sempre buffer overflow. Se fai buffer overflow sufficientemente lungo arrivi a fare Segmentation Fault. Quando vai in segfault succede che viene creato un core files per andare a debuggare e cercare di capire che cosa e’ successo. Magari ci starebbe andare a provare a fare questa cosa e vedere con gdb che cosa succede (essenzialmente copiare i comandi che ha fatto il prof).
Ci sono programmini motlo carini:

Cioe’ con questo io vado direttamente a scrivere l’indirizzo su cui voglio andare a finire (8049a6, si era sbagliato all’inizio). Questo numero. Idealmente lo sto spammando completamente nel buffer e finisce nell’indirizzo di di ritorno. Adesso devo fare in modo che questo sia il buffer che viene dato in pasto al mio programma quando lo invoco. per fare questo posso andare ad usare la pipe:
./ret | ./adamas
Guarda caso ci ritroviamo quello che vogliamo (42) ma poi muore. Cosi’ siamo entrati dentro accedi senza sapere la password. Abbiamo sovrascritto un instruction pointer.
Solitamente andiamo ad iniettare del bytecode, questo che cosa vuol dire. Che praticamente quello che succede e’ che ci compiliamo codice malevole e poi lo andiamo ad inserire in formato hex.
Solitamente si inserisce lo shell code perche’ cosi’ abbiamo una shell e quindi alla fine mi ritrovo un interprete dei comandi e tutta quanta una shell interattiva quando non dovrei avere modo di fare queste cose. Se abbiamo un buffer piccolo non e’ detto che ci sia shellcode dentro. Devo andare ad individuare dove vado a scrivere il return address dello stack e quello che diventera’ l’instruction pointer e devo fare in modo che punti a dove inizi il mio shell code! Non e’ assolutamente detto che io sappia tutte queste informazioni.

Solitamente si lavora usando le jmp per fare questo trucco.
Ci sono delle tecniche per andare a conoscere meglio dove si trova la CALL e dove mettere mano all’interno dello stack. Praticamente uso le NOP, ne metto tantissime e queste mi fanno incrementare l’instruction pointer di 1 fino a che non raggiungo lo shell code (sto creando una NOP sled). Questo mi serve per evitare dove incomincia precisamente la mia shell code. In qualunque punto salto sono in una nop e tanto va a avanti di uno fino a che arriva al /bin/sh, quindi piu’ e’ larga e piu’ sono safe. Dopo la shell code rimetto l’indirizzo di ritorno tante volte per cui non mi interessa quale delle copie andava a sovrascrivere, basta che sia una a farlo. Metto tante volte un indirizzo che appartiene ad una mia nop sled, sono a cavallo. Spesso ho un buffer cosi’:

Gli indirizzi di ritorno portano alla NOP Sled. Questa sequenza viene usata per fare detection di comportamenti strani (lunga slitta di 0x90).
Ora fa vedere come funziona il buffer overflow 2.
La shell che si apre ha gli stessi permessi del programma in esecuzione che l’ha lanciata. Morale della favela: conviene exploitare programmi con privilegi elevati.
Posso anche andare a corrompere l’heap e possiamo anche andare a fare dei magheggi con printf e scanf. Per esempio con un uso improprio di %n posso andare a scrivere/leggere informazioni in memoria. Per altro posso andare a scrivere in posizione arbitrarie della memoria dei byte a scelta.
Il problema e’ che vanno messi gli input sanificati, devo verificare sempre gli input altrimenti succedono queste cose. Ogni volta che accedo ad un buffer devo anche controllare di esserci dentro e di non andare MAI fuori. Posso anche usare delle funzioni di libreria piu’ sicure di quelle viste. Ci sono delle versioni chiamate “n” in cui sono sicure perche’ usano come parametro anche la dimensione e dico esplicitamente quanti byte salvare. Pero’ mi devo andare a fixare tutti i programmi vulnerabili e non e’ detto perche’ potrei non avere codice sorgente, fare patching a livello di binario non e’ divertente. Ci sono pero’ anche altre contromisure: eseguiamo audit sul software. Non fai fare i test a chi scrive il codice, mai. Bisogna che ci sia un terzo che faccia audit al codice scritto da qualcuno. Ci sono strumenti per cercare in modo massimo all’interno del codice l’utilizzo di funzioni insicure. Ma ci sono anche strumenti per analisi statica che fanno cose anche piu’ compicate. Altra buona norma: non ignorare i warning del compilatore. Che cosa possiamo fare sul codice? Fuzzing: prenod il programma e faccio un brute force su tutti gli input possibili.