Contexte de la faille
Depuis les débuts d’Unix, le hash de mot de passe « * » indique un compte verrouillé. En 2012, la bibliothèque musl libc a modifié son implémentation de crypt() : lorsqu’un mot de passe dépasse la longueur maximale supportée, la fonction renvoie la chaîne « * » au lieu de NULL comme le prescrit la spécification POSIX. Cette modification a été motivée par le constat que de nombreux binaires ne vérifiaient pas le retour NULL, mais elle introduit un signal d’erreur « in‑band » qui se confond avec le sentinel de compte verrouillé.
Mécanisme d’exploitation
Le démon sshd utilise crypt() pour hacher les tentatives de connexion, même pour les comptes verrouillés, afin d’éviter les fuites de timing. Sur les systèmes musl dépourvus de PAM – par exemple l’image officielle Alpine Linux utilisée sur AWS – le flux suivant se produit :
char *hash = crypt(password, salt);
if (hash == NULL) { /* erreur */ }
Si le mot de passe fourni est trop long, crypt() renvoie « * ». sshd interprète alors ce résultat comme l’indication d’un compte verrouillé et autorise la suite du processus d’authentification sans déclencher d’erreur explicite. Un attaquant peut donc soumettre un mot de passe volontairement trop long, obtenir le hash « * », et, grâce à la logique de sshd, déclencher l’ouverture d’une session shell non authentifiée – parfois avec les privilèges root si le compte ciblé possède cet UID.
Analyse des limites du système C
Le problème ne relève pas d’une faille de mémoire, mais d’une absence de type sûr. La spécification POSIX stipule que crypt() doit retourner NULL et définir errno en cas d’erreur. Le système de types du C ne peut pas contraindre l’appelant à tester NULL. En l’absence d’un type d’union ou d’un résultat explicite, les développeurs utilisent souvent des valeurs sentinel comme « * », ce qui crée une ambiguïté sémantique. En revanche, des langages comme Rust imposent un Result qui force le traitement de l’erreur, éliminant ainsi le risque d’interpréter un sentinel comme une donnée valide.
Implications et mesures d’atténuation
Cette vulnérabilité affecte principalement les déploiements Alpine Linux sans PAM, notamment les images Docker ou les instances AWS basées sur Alpine. Les administrateurs doivent :
1. Mettre à jour musl libc vers une version qui restaure le comportement POSIX (retour NULL).
2. Activer PAM ou appliquer un correctif qui vérifie explicitement le retour de crypt() avant de le comparer à « * ».
3. Auditer les services SSH pour s’assurer qu’ils ne confondent pas le sentinel avec un hash valide.
À plus long terme, le cas illustre l’importance de choisir des bibliothèques qui respectent les contrats d’interface et d’adopter des langages ou des abstractions qui rendent les erreurs explicites. Sans ces garanties, même des vulnérabilités conceptuelles peuvent conduire à des escalades de privilèges graves.