Format string attack: differenze tra le versioni

Contenuto cancellato Contenuto aggiunto
Nessun oggetto della modifica
m Introduzione: Aggiunto il collegamento a "C (linguaggio di programmazione)"
 
(67 versioni intermedie di 19 utenti non mostrate)
Riga 1:
{{S|sicurezza informatica}}
Il '''format string attack''' (''vulnerabilità di formato della stringa'') sono una classe di vulnerabilità scoperte nel 1999.
 
Le '''format string attack''' (''vulnerabilità di formato della stringa'') sono una classe di vulnerabilità scoperte nel 1999.
== Funzionamento ==
Se si passa a una funzione che stampa una stringa a schermo, tipicamente una [[printf]] del [[C (linguaggio)|linguaggio C]], una stringa che in realtà contiene una serie di parametri di specifica dell'input (tipicamente si usano gli specificatori di formato %s e %x per esaminare il contenuto della memoria e %n per sovrascrivere parti della memoria, in particolare dello [[stack]], si permette l'avvio di un'attacco di tipo ''[[stack overflow]]'' e ''return to libc''). Per proteggersi da questo attacco, quando si vuole stampare una stringa s usando la ''printf()'' o un'altra funzione C che accetta un numero illimitato di specificatori di formato, bisogna scrivere la funzione
 
== Introduzione ==
printf("%s",s)
Il tipo di attacco Format String è una classe di vulnerabilità scoperte nel 1999, presenti prevalentemente in linguaggi di programmazione imperativi come il [[C (linguaggio di programmazione)|C]]. Un Format String Attack è formato da tre componenti fondamentali:
* Format Function: in ANSI C è una funzione che converte una variabile di tipo primitivo, in una stringa user-friendly cioè leggibile dall’uomo.
 
printf: per stampare su stdout
e non scrivere
fprintf: per stampare la format string su file
sprintf: per stampare una stringa dentro un buffer
snprintf: per stampare esattamente 'n' caratteri della stringa in ingresso nella seconda
 
* Format String: è l’argomento della format function ed è una stringa ASCII che contiene sia testo che parametri di formato.
printf(s)
printf(“The magic number is %d\n”, 3);
 
* Format String Parameter definiscono il tipo di conversione da effettuare in relazione alla variabile presente nella format string.
in quanto in questo modo l'input dell'utente non viene validato. La stringa StringPointer potrebbe contenere una serie di specificatori di formato. Questo tipo di attacco, comunque, diventa sempre più difficile grazie alla diffusione di una maggiore consapevolezza della necessità di gestire i rischi associati alla programmazione e alla conseguente diffusione di tecniche di programmazione sicura.
“%s”: per stampare una stringa
“%d”: per stampare degli interi
“%c”: per stampare dei caratteri
“%x”: per stampare caratteri esadecimali
 
=== EsempioFunzionamento (Linux)dello stack ===
Lo stack è un'area di memoria in cui vengono salvate le variabili locali e i parametri passati alla funzione. La sua gestione è di tipo LIFO e cresce verso indirizzi bassi. Eseguendo questo semplice codice, ad esempio:
Dato il seguente programma C (compilato con l'opzione <code>-fstack-protector</code>)
<sourcesyntaxhighlight lang="textc">
#include <stdio.h>
void main(int argc , char* argv){
int A=3, B=5;
printf("Valore var. A: %d - Indirizzo var. A: %x\n", A, &A);
printf("Valore var. B: %d - Indirizzo var. B: %x\n", B, &B);
}
</syntaxhighlight>
il programma stamperà:
<syntaxhighlight lang="c">
Valore var. A: 3 - Indirizzo var. A: 7fdc
Valore var. B: 5 - Indirizzo var. B: 7fd8
</syntaxhighlight>
se invece la printf() riceve come parametro una stringa "maligna" (senza controllarne le caratteristiche) il risultato sarebbe proprio un attacco di tipo format string.
 
== Attacchi possibili ==
main(int argc, char *argv[])
 
{
'''Crash del programma'''
char buf[64];
 
strcpy(buf,argv[1]);
Un modo molto semplice per mandare in crash un programma è passare alla funzione printf() una stringa formattata in questo modo: <syntaxhighlight lang="c">printf("%s%s%s%s%s%s%s%s%s%s%s");</syntaxhighlight>
printf(buf);
 
};
Ogni "%s" cerca di visualizzare il contenuto di un buffer di caratteri (una stringa) dall'indirizzo iniziale presente sullo stack fino al carattere terminatore ("0"). Inserendo quindi molti “%s", la funzione sposterà il puntatore in avanti fino ad aree di memoria non mappate nel frame dell'applicazione, così facendo cercherà di leggere da "indirizzi illegali", causando l'errore “''segmentation fault”'' che manda in crash il programma.
</source>
 
sia <code>0x0804a010</code> l'indirizzo del record di rilocazione dinamica per la funzione <code>__stack_chk_fail</code>. Dopo aver verificato che l'inizio della stringa di formato è, nello stack, a 8 word (di 4 byte) di distanza dal puntatore a <code>buf</code>
'''Leggere lo stack'''
<source lang="text">
 
# ./a.out `python -c 'print "AAAA" + "_0x%x"*8'`
Utilizzando sempre la printf(), è possibile leggere parte del contenuto dello stack, usando una format string di questo tipo:
AAAA_0xbffff9ad_0x0_0x0_0xbffff894_0x0_0x0_0x0_0x41414141
<syntaxhighlight lang="c">printf("%08x-%08x-%08x-%08x-%08x\n");</syntaxhighlight>
</source>
l'output potrebbe essere il seguente:
con
<syntaxhighlight lang="c">4306ea40-43074920-0062de90-70706970-02000002</syntaxhighlight>
<source lang="text">
Questo perché essendo il numero degli argomenti della printf() variabili, questa utilizza la format string per sapere quanti gliene sono stati passati. In questo caso quindi crede (erroneamente) che siano cinque parametri, e andrà quindi a stampare i prossimi cinque indirizzi sullo stack, pensando siano i parametri richiesti.
gdb --args ./a.out `python -c 'print "\x10\xa0\x04\x08" + "\x12\xa0\x04\x08" + "%16697x%8$n%9$n" + "A"*41 + "!"'`
 
[...]
Il parametro di formato "%08x" è costituito dai seguenti componenti:
Program received signal SIGSEGV, Segmentation fault.
* x (converti i caratteri in esadecimale),
0x41414141 in ?? ()
* 8 (mostra otto caratteri),
</source>
* 0 (riempi di zeri i caratteri nulli, se presenti).
* scriviamo 2 word di 2 byte (<code>16697 + 8 = 0x4141</code>), agli indirizzi <code>0x0804a010 / 0x0804a012</code>, specificati all'inizio della stringa di formato e raggiunti con <code>%8$n / %9$n</code>
 
* la chiamata a <code>__stack_chk_fail</code> viene attivata da <code>"!"</code> che sovrascrive il primo byte dello stack canary
'''Leggere memoria in ogni locazione di memoria'''
* <code>0x41414141</code> è in questo caso un indirizzo non valido, che genera quindi un <code>Segmentation fault</code>.
 
È possibile anche leggere aree di memoria diverse dallo stack. Dobbiamo però creare una format function che visualizza il contenuto della memoria all’indirizzo che gli forniamo. Ci servirà quindi l’indirizzo da cui andare a leggere ed il giusto parametro di formato per leggerlo.
 
La nostra format function mantiene all’interno dello stack il puntatore alla locazione di memoria del parametro di formato. Se fossimo in grado di far puntare quel puntatore all’area di memoria che ci interessa, potremmo dare quell’indirizzo al formato parametro "%s" così che, invocando la printf(), questa leggerà la memoria da quell’indirizzo fino a che non trova un carattere terminatore ‘0’.
 
Utilizzando ad esempio la seguenti stringa di formato:[[File:StackMemory.png|miniatura|446x446px|Salvataggio variabili sullo stack]]<syntaxhighlight lang="c"> printf ("\x10\x01\x48\x08_%08x-%08x-%08x-|%s|"); </syntaxhighlight>
la sequenza di "%08x" incrementa il puntatore interno della format function verso il top dello stack. Dopo alcuni di questi incrementi, lo stack pointer punterà esattamente all'indirizzo della nostra format string. Con il giusto numero di ‘%08x’ potremmo visualizzare la memoria (codificata in ASCII) da un qualsiasi indirizzo.
 
Bisogna quindi sostituire la parte iniziale della format string con un vero indirizzo.
 
Vogliamo leggere aree di memoria vicino a questo indirizzo ‘0x08480110’:
* Per prima cosa bisogna codificare l'indirizzo in una stringa a 32 bit secondo la regola little-endian.
* l'indirizzo "0x08480110" verrà quindi ribaltato e separato da caratteri speciali diventando: "\x10\x01\x48\x08"
La codifica dell’indirizzo scelto in una stringa a 32 bit "\x10\x01\x48\x08" dove il carattere "\x10" inserito in una stringa dice al compilatore di mettere un carattere esadecimale "0x10" nella posizione corrente.
 
Il carattere "\x" dice al compilatore di mettere il carattere esadecimanle "0x10" nella posizione corrente sullo stack. Altrimenti il valore ASCII salvato nella stringa sarebbe ‘1’ e ‘0’ i cui rispettivi valori ASCII sono ‘49’ e ‘48’, che risulterebbe errato.
 
Quindi la seguente invocazione andrà a leggere memoria dall’indirizzo che gli abbiamo fornito finché non raggiunge un NULL byte. Incrementando l’indirizzo di memoria dinamicamente possiamo mappare l’intera memoria di processo!
 
'''Scrivere un intero in (qualsiasi) locazione di memoria'''
 
La printf() ha un altro parametro di formato molto interessante: "%n".
<syntaxhighlight lang=c>int i;
printf ("12345%n", &i);</syntaxhighlight>
Con questo parametro il numero di caratteri scritti prima di lui verrà salvato nella variabile "i"!
Quindi usando lo stesso metodo implementato per visualizzare il contenuto della memoria, possiamo andare a scrivere un intero (praticamente) ovunque.
Basta sostituire "%n" al posto di "%s" ed il contenuto della cella "0x10014808" verrà sovrascritto. L'attaccante potrebbe quindi andare a cambiare certi flag che gli garantiscano il controllo o sovrascrivere celle con indirizzi di ritorno a nostro piacimento.
 
== Voci correlate ==
* [[Cross Application Scripting]]
* [[Stack overflow]]
* [[Linguaggio C]]
 
== Collegamenti esterni ==
* {{en}}scut / team teso [http://julianor.tripod.com/teso-fs1bc/formatstring-1.2.pdf Come sfruttare le Format String Vulnerabilities] v1.12 MarchSept 24, [[2001]]
* {{en}}[cita web|https://www.securecoding.cert.org |CERT standard di programmazione sicura ]|lingua=en}}
* {{en}}[httpcita web|https://www.cert.org/secure-coding |CERT iniziativa per la programmazione sicura ]|lingua=en}}
* {{en}}[httpcita web|https://www.cert.org/books/secure-coding |programmazione sicura in C e C++]|lingua=en}}
 
{{Portale|Sicurezzasicurezza informatica}}
 
[[Categoria:Tecniche di attacco informatico]]
 
[[de:Formatstring-Angriff]]
[[en:Format string attack]]
[[ja:書式文字列攻撃]]
[[pl:Format string attack]]