4.8.14.2 Tester les débordements de pile

Sommaire
Les débordement de pile (Stack overflows) surviennent lorsque des données de taille variable sont copiées dans des tampons de taille fixe alloué sur la pile du programme, sans vérifier les limites. Les vulnérabilités de ce type sont généralement considérées comme très critiques puisque leur exploitation permet l'exécution de code ou des dénis de service. On les trouve rarement sur les plateformes interprêtées, mais elles sont fréquentes dans les programme écrits en C et langages similaires. En fait, presque toutes les plateformes sont vulnérables aux débordement de pile, à l'exception notable de :
 * J2EE – tant qu'aucune méthode native ou appel système n'est appelé
 * .NET – tant qu'aucun code /unsafe ou non-managé n'est invoqué (tel que P/Invoke ou COM Interop)
 * PHP – tant que des programmes externes ou des extensions vulnérables écrites en C ou C++ ne sont pas appelés

Les vulnérabilités de débordement de pile permettent souvent à un attaquant de prendre le contrôle du pointeur d'instruction, et donc de modifier l'exécution du programme et d'exécuter du code arbitraire. En outre, des résultats similaires peuvent être obtenus en écrasant d'autres variable et structures que le pointeur d'instruction, comme les Exception Handlers qui sont stockés sur la pile.

Test en boite noire
La clef pour tester les vulnérabilités de débordement de pile d'une application est de fournir des données de taille plus grande que ce qui est attendu. Cependant, ce n'est pas suffisant. Il devient nécessaire d'inspecter le flux d'exécution de l'application et les réponses pour être certain qu'un débordement a bien été déclenché. Ainsi, les étapes requises pour localiser et confirmer les dépassement de pile seront d'attacher un débogueur à l'application ou au processus ciblé, de générer des entrées malformées pour l'application, de les lui envoyer, et d'inspecter les réponses dans le débogueur. Le débogueur permet au testeur de visualiser le flux d'exécution et l'état des registres quand la vulnérabilité est déclenchée.

D'autre part, une forme plus passive de test peut être employée, qui implique d'inspecter le code assembleur de l'application avec des désassembleurs. Dans ce cas, diverses sections sont scannées pour chercher des des signatures de fragments vulnérables. Cette technique est souvent appelée ingénierie inverse et est un processus complexe.

A guise d'exemple simple, considérons la technique suivante pour rechercher des débordements de pile dans un exécutable "sample.exe".

int main(int argc, char *argv[]) { char buff[20]; printf("copying into buffer"); strcpy(buff,argv[1]); return 0; }
 * 1) include

Le fichier sample.exe est lancé dans un débogueur, OllyDbg dans notre cas.



Puisque l'application attend des arguments en ligne de commande, une large séquence de caractères comme des 'A' peut être fourni dans le champ argument montré plus haut.

A l'ouverture, l'exécutable ave les arguments fournis et l'exécution continue vont fournir les résultats.



Comme on le voit dans la fenêtre des registres du débogueur, l'EIP ou Extended Instruction Pointer, qui pointe sur la prochaine instruction à exécuter, contient la valeur ‘41414141’. '41' est la représentation hexadécimale du caractère 'A' et donc la chaîne 'AAA' est traduite par 41414141.

Cela montre clairement comment des données en entrée peuvent être utilisées pour écraser le pointeur d'instruction avec des données fournies par l'utilisateur et prendre le contrôle de l'exécution. Un débordement de pile peut également permettre l'écrasement de structure basées sur la pile, comme SHH (Structured Exception Handler) pour contrôler l'exécution du code et contourner certains mécanismes de protection de la pile.

Comme indiqué précédemment, d'autres méthodes pour tester de telles vulnérabilités existent, comme l'ingénierie inverse des binaires de l'application, qui est complexe et difficile, et les techniques de fuzzing.

Test en boite grise
Quand on fait de la reuve de code pour chercher les débordement de pile, il est conseillé de chercher les appels à des fonctions de bibliothèques non sécurisées comme gets, strcpy, strcat, etc. qui ne valident pas la longueur de la chaîne source et copie aveuglément les données vers des tampons de tailles fixes.

Par exemple, considérons la fonction suivante :

void log_create(int severity, char *inpt) {

char b[1024];

if (severity == 1) { strcat(b,”Error occurred on”); strcat(b,":"); strcat(b,inpt);

FILE *fd = fopen ("logfile.log", "a"); fprintf(fd, "%s", b); fclose(fd);

. . . . . . }

La ligne strcat(b,inpt) ci-dessus va causer un débordement de pile si inpt fait plus que 1024 octets. Cela ne montre pas seulement une utilisation non sécurisée de strcat, mais aussi combien il est important de vérifier la longueur des chaînes référencées par un pointeur de caractères passé en argument à une fonction ; dans ce cas, la longueur de la chaîne référencée par *inpt. C'est donc toujours une bonne idée de tracer la source des arguments d'une fonction et de vérifier la longueur des chaînes pendant la revue de code.

L'utilisation de la fonction strncpy, relativement plus sécurisée, peut aussi causer des débordement de pile, puisqu'elle ne restreint que le nombre d'octets copiés dans le tampon de destination. Si l'argument taille utilisé pour ça est généré dynamiquement, basé sur une entrée utilisateur ou calculé de manière incorrecte dans une boucle, il est possible de déborder les tampons de pile. Par exemple :

void func(char *source) { Char dest[40]; … size=strlen(source)+1 …. strncpy(dest,source,size) }

où source est une donnée contrôlable par l'utilisateur. Un bon exemple serait la vulnérabilité de débordement de pile de trans2open de samba (http://www.securityfocus.com/archive/1/317615).

Des vulnérabilité peuvent aussi apparaître dans le code d'analyse des URLs et d'adresses. Dans de tels cas, une fonction comme memccpy est usuellement employée pour copiers des données vers un tampon de destination depuis une source jusqu'à ce qu'un caractère soit rencontré. considérons la fonction :

void func(char *path) { char servaddr[40]; … memccpy(servaddr,path,'\'); …. }

Dans ce cas, l'information contenue dans path pourrait être plus grande que 40 octets avant que '\' soit rencontré. Si c'est le cas, cela cause un débordement de pile. Une vulnérabilité similaire a été découverte dans le sous-système RPCSS de Windows (MS03-026). Le code vulnérable copiait des nom de serveru depuis des chemins UNC dans des tampons de taille fixe jusqu'à ce que '\' soit rencontré. La longueur du nom du serveur était dans ce cas contrôlable par l'utilisateur.

En dehors des revue de code manuelles, les outils d'analyse de code statiques peuvent aussi être très utiles. Cependant, il ont tendance à générer de nombreux faux positifs et ne vont permettre de localiser qu'une petite proportion de dafauts. Ils aident à réduire la surcharge en trouvant les vulnérabilités les plus évidentes, comme strcpy et sprintf. Beaucoup d'outils comme RATS, Flowfinder et ITS4 sont disponibles pour analyser le langage C et ses dérivés.

Outils

 * OllyDbg: "A windows based debugger used for analyzing buffer overflow vulnerabilities" - http://www.ollydbg.de
 * Spike, A fuzzer framework that can be used to explore vulnerabilities and perform length testing - http://www.immunitysec.com/downloads/SPIKE2.9.tgz
 * Brute Force Binary Tester (BFB), A proactive binary checker - http://bfbtester.sourceforge.net/
 * Metasploit, A rapid exploit development and Testing frame work - http://www.metasploit.com

Références
Whitepapers
 * Aleph One: "Smashing the Stack for Fun and Profit" - http://insecure.org/stf/smashstack.html
 * The Samba trans2open stack overflow vulnerability - http://www.securityfocus.com/archive/1/317615
 * Windows RPC DCOM vulnerability details - http://www.xfocus.org/documents/200307/2.html