
CVE-2026-11837 : élévation de privilèges locale dans le module ansible.posix authorized_key via un chown suivant les liens symboliques. Note technique ; jumeau de CVE-2024-9902.
authorized_key de ansible.posixEscalade de privilèges locale dans le module Ansible ansible.posix.authorized_key via chown suivant les liens symboliques (et création de fichier suivant les liens symboliques) sur le répertoire ~/.ssh d'un utilisateur et le fichier authorized_keys.
Ceci est une description d'une vulnérabilité que j'ai signalée et à laquelle a été attribué CVE-2026-11837 par Red Hat Product Security (le CNA). Elle est publiée ici à titre de documentation technique, et non comme une revendication de nouveauté : le problème est le jumeau de CVE-2024-9902 dans un module que le correctif précédent n'a pas couvert.
Accusé de réception publié par Red Hat : "Red Hat remercie Valentino Paulon pour avoir signalé ce problème."
Lorsqu'un playbook s'exécutant en tant que root utilise ansible.posix.authorized_key pour gérer les clés d'un utilisateur local, l'assistant keyfile() du module :
~/.ssh de l'utilisateur et du fichier ~/.ssh/authorized_keys avec un simple os.chown (pas os.lchown), etopen(..., "w") (pas de O_NOFOLLOW).Avec la valeur par défaut follow=False, le module ne suit ni ne remplace les liens symboliques et ne les refuse pas non plus ; il opère directement sur le chemin, donc le noyau suit tout lien symbolique que l'utilisateur cible non privilégié a pré-positionné dans son propre ~/.ssh. Cela donne deux primitives qui transfèrent la propriété d'un chemin contrôlé par root à l'utilisateur non privilégié, qui élève ensuite ses privilèges à root. Les deux ont été reproduites de bout en bout sur la collection à HEAD.
keyfile() (plugins/modules/authorized_key.py), avec les valeurs par défaut manage_dir=True, follow=False :
if manage_dir:
if not os.path.exists(sshdir): # os.path.exists SUIT les liens symboliques
try:
os.mkdir(sshdir, int('0700', 8))
...
os.chown(sshdir, uid, gid) # INCONDITIONNEL, chown simple, suit le lien symbolique
os.chmod(sshdir, int('0700', 8)) # suit le lien symbolique
if not os.path.exists(keysfile): # suit le lien symbolique
...
f = open(keysfile, "w") # open() simple, pas de O_NOFOLLOW, suit le lien symbolique
...
try:
os.chown(keysfile, uid, gid) # INCONDITIONNEL, chown simple, suit le lien symbolique
os.chmod(keysfile, int('0600', 8)) # suit le lien symbolique
except OSError:
pass
sshdir et keysfile sont dérivés du répertoire personnel de l'utilisateur cible dans passwd (pwd.getpwnam(user).pw_dir), un répertoire que l'utilisateur cible non privilégié possède et contrôle. Le paramètre follow (par défaut False) choisit uniquement d'appeler os.path.realpath() sur le chemin ; dans aucun cas il n'y a de vérification os.path.islink, d'ouverture avec O_NOFOLLOW, d'utilisation de os.lchown, ou de suppression et remplacement d'un lien symbolique préexistant. Aucune baisse de privilège (seteuid/setfsuid) n'est effectuée ; tout s'exécute en tant que root.
Les deux appels os.chown s'exécutent inconditionnellement, en dehors des gardes if not os.path.exists(...), donc ils se déclenchent que le répertoire/fichier existe déjà ou non. C'est ce qui rend les deux primitives fiables.
Les deux ont été confirmées de bout en bout sur une machine Linux jetable, avec ansible.posix à HEAD. victim est un utilisateur local non privilégié (uid 1000). Le module est exécuté en tant que root, exactement comme le ferait le playbook d'un opérateur. Des leurres représentent des cibles root sensibles.
Vecteur 1 : lien symbolique de répertoire (chown d'un répertoire existant appartenant à root)
victim : ln -s /root/AD_CANARY ~/.ssh (avec /root/AD_CANARY un répertoire existant appartenant à root).authorized_key pour victim./root/AD_CANARY est passé de root:root à victim:victim. L'appel os.chown(sshdir, ...) a suivi le lien symbolique ~/.ssh et a remis un répertoire appartenant à root à l'utilisateur non privilégié.Vecteur 2 : lien symbolique de fichier cassé (création + chown d'un nouveau fichier dans un chemin root)
victim (avec ~/.ssh un répertoire normal) : ln -s /root/AF_CANARY ~/.ssh/authorized_keys (la cible n'existe pas).authorized_key pour victim./root/AF_CANARY a été créé, appartenant à victim:victim, mode 0600. open(keysfile, "w") a créé la cible via le lien symbolique et os.chown(keysfile, ...) l'a remise à l'utilisateur non privilégié.Dans les deux cas, l'utilisateur non privilégié finit par posséder un chemin contrôlé par root. Pointer le lien vers un répertoire sous /etc (Vecteur 1) ou vers un nouveau fichier sous /etc/cron.d/ ou /etc/sudoers.d/ (Vecteur 2) permet d'obtenir root par la suite standard, où l'utilisateur, désormais propriétaire, réécrit la cible.
Ceci est un jumeau de CVE-2024-9902 (le generate_ssh_key du module user, dans ansible-core), qui traitait la même classe de suivi de liens symboliques. authorized_key réside dans la collection distincte ansible.posix et n'a pas été modifié par ce correctif ; le motif chown/création suivant les liens symboliques était toujours présent et non protégé. Il est signalé comme la même classe acceptée dans un module que le correctif précédent n'a pas atteint, et non comme une cause racine nouvelle.
ansible.posix.authorized_key ciblant le compte de la victime (l'utilisation courante de ce module). L'utilisateur local pré-positionne le lien symbolique dans son propre ~/.ssh ; il n'exécute pas le playbook. C'est déclenché par l'opérateur : le même modèle de déclenchement que CVE-2024-9902, que Red Hat a accepté avec l'atténuation « ne pas exécuter contre des comptes non fiables ». La précondition est énoncée explicitement plutôt que considérée comme auto-déclenchée.os.open(keysfile, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600) et opérer sur le descripteur ; refuser (ou supprimer et remplacer, selon le contrat de la docstring de follow=False) si sshdir ou keysfile est un lien symbolique.os.chown(...) par os.lchown, ou par os.fchown sur un descripteur ouvert en toute sécurité, et de même os.fchmod au lieu de os.chmod, afin que la propriété et les permissions ne puissent pas être redirigées via un lien.chown/chmod inconditionnels pour qu'ils ne s'appliquent qu'à un chemin que le module lui-même vient de créer en toute sécurité, et non à un chemin préexistant (éventuellement lié par un lien symbolique).| Date | Événement |
|---|---|
| 2026-06-08 | Signalé à [email protected] (Red Hat Product Security en CC en tant que CNA), présenté comme le jumeau de CVE-2024-9902 |
| 2026-06-10 | CVE-2026-11837 attribué et publié ; crédit accepté |
MIT. Voir LICENSE.
| CVE | CVE-2026-11837 |
| Composant | Collection ansible.posix, fichier plugins/modules/authorized_key.py, fonction keyfile() |
| Type | Élévation de privilège (locale). CWE-59 / CWE-61 (suivi de lien) avec CWE-282 (gestion incorrecte de la propriété) |
| CVSS | 7.3 (Élevé) |
| Publication | 2026-06-10 |
| Signalé par | Valentino Paulon |