
CVE-2026-11837: lokale Privilegieneskalation im ansible.posix authorized_key Modul durch symlink-following chown. Technischer Bericht; Schwester von CVE-2024-9902.
authorized_key in ansible.posixLokale Privilegieneskalation im Ansible-Modul ansible.posix.authorized_key durch Symlink-verfolgendes chown (und Symlink-verfolgende Dateierstellung) auf das ~/.ssh-Verzeichnis und die authorized_keys-Datei eines Benutzers.
Dies ist eine Dokumentation einer von mir gemeldeten Sicherheitslücke, der von Red Hat Product Security (dem CNA) die CVE-2026-11837 zugewiesen wurde. Sie wird hier als technischer Nachweis veröffentlicht, nicht als Anspruch auf Neuheit: Das Problem ist das Geschwister von CVE-2024-9902 in einem Modul, das der frühere Fix nicht abdeckte.
Red Hats veröffentlichte Bestätigung: "Red Hat möchte Valentino Paulon für die Meldung dieses Problems danken."
Wenn ein Playbook, das als root ausgeführt wird, ansible.posix.authorized_key verwendet, um die Schlüssel eines lokalen Benutzers zu verwalten, dann führt die keyfile()-Hilfsfunktion des Moduls Folgendes durch:
~/.ssh-Verzeichnisses des Benutzers sowie von ~/.ssh/authorized_keys mit einem einfachen os.chown (nicht os.lchown), undopen(..., "w") (kein O_NOFOLLOW).Mit dem Standardwert follow=False folgt das Modul weder Symlinks und ersetzt sie noch lehnt es Symlinks ab; es arbeitet direkt mit dem Pfad, sodass der Kernel jedem Symlink folgt, den der unprivilegierte Zielbenutzer in seinem eigenen ~/.ssh vorbereitet hat. Dies ergibt zwei Primitiven, die den Besitz eines root-kontrollierten Pfads an den unprivilegierten Benutzer übertragen, der dann zu root eskaliert. Beide wurden End-to-End gegen die Collection auf HEAD reproduziert.
keyfile() (plugins/modules/authorized_key.py), mit den Standardwerten manage_dir=True, follow=False:
if manage_dir:
if not os.path.exists(sshdir): # os.path.exists FOLLOWS symlinks
try:
os.mkdir(sshdir, int('0700', 8))
...
os.chown(sshdir, uid, gid) # UNCONDITIONAL, plain chown, follows symlink
os.chmod(sshdir, int('0700', 8)) # follows symlink
if not os.path.exists(keysfile): # follows symlink
...
f = open(keysfile, "w") # plain open(), no O_NOFOLLOW, follows symlink
...
try:
os.chown(keysfile, uid, gid) # UNCONDITIONAL, plain chown, follows symlink
os.chmod(keysfile, int('0600', 8)) # follows symlink
except OSError:
pass
sshdir und keysfile werden aus dem Passwd-Home des Zielbenutzers abgeleitet (pwd.getpwnam(user).pw_dir), einem Verzeichnis, das dem unprivilegierten Zielbenutzer gehört und von diesem kontrolliert wird. Der Parameter follow (Standardwert False) wählt nur aus, ob os.path.realpath() auf den Pfad angewendet wird; in keinem Fall gibt es eine os.path.islink-Prüfung, keinen O_NOFOOPEN-Open, kein os.lchown und kein Unlink-and-Replace eines vorhandenen Symlinks. Es wird kein Privilegienabwurf (seteuid/setfsuid) durchgeführt; alles läuft als root.
Die beiden os.chown-Aufrufe werden unbedingt ausgeführt, außerhalb der if not os.path.exists(...)-Wächter, sodass sie immer ausgelöst werden, unabhängig davon, ob das Verzeichnis/die Datei bereits existierte. Das macht beide Primitiven zuverlässig.
Beide bestätigt End-to-End auf einer Wegwerf-Linux-VM mit ansible.posix auf HEAD. victim ist ein unprivilegierter lokaler Benutzer (uid 1000). Das Modul wird als root ausgeführt, genau wie ein Operator-Playbook es tun würde. Kanarienvögel stehen für sensible root-Ziele.
Vektor 1: Verzeichnis-Symlink (chown eines bestehenden root-eigenen Verzeichnisses)
victim: ln -s /root/AD_CANARY ~/.ssh (mit /root/AD_CANARY als bestehendes root-eigenes Verzeichnis).authorized_key-Aufgabe für victim aus./root/AD_CANARY änderte sich von root:root zu victim:victim. Der os.chown(sshdir, ...)-Aufruf folgte dem ~/.ssh-Symlink und übergab ein root-eigenes Verzeichnis an den unprivilegierten Benutzer.Vektor 2: Dangling-Datei-Symlink (create + chown eines neuen root-Pfad-Datei)
victim (mit ~/.ssh als normalem Verzeichnis): ln -s /root/AF_CANARY ~/.ssh/authorized_keys (Ziel existiert nicht).authorized_key-Aufgabe für victim aus./root/AF_CANARY wurde erstellt, Besitzer victim:victim, Modus 0600. open(keysfile, "w") erstellte das Ziel durch den Symlink und os.chown(keysfile, ...) übergab es.In beiden Fällen besitzt der unprivilegierte Benutzer am Ende einen root-kontrollierten Pfad. Wenn der Link auf ein Verzeichnis unter /etc (Vektor 1) oder auf eine neue Datei unter /etc/cron.d/ oder /etc/sudoers.d/ (Vektor 2) zeigt, führt dies durch die übliche Folgemaßnahme zu root, bei der der Benutzer, jetzt der Besitzer, das Ziel überschreibt.
Dies ist ein Geschwister von CVE-2024-9902 (das user-Modul generate_ssh_key in ansible-core), das dieselbe Symlink-verfolgende Klasse adressierte. authorized_key lebt in der separaten Collection ansible.posix und wurde durch diesen Fix nicht geändert; das Symlink-verfolgende chown/create-Muster war immer noch vorhanden und ungeschützt. Es wird als dieselbe akzeptierte Klasse in einem Modul gemeldet, das der vorherige Fix nicht erreichte, nicht als neuartige Grundursache.
ansible.posix.authorized_key-Aufgabe aus, die auf das Konto des Opfers abzielt (der übliche Verwendungszweck dieses Moduls). Der lokale Benutzer bereitet den Symlink in seinem eigenen ~/.ssh vor; er führt das Playbook nicht aus. Dies ist operator-getriggert: dasselbe Triggermodell wie CVE-2024-9902, das Red Hat mit der Gegenmaßnahme "führe keine Aufgaben gegen nicht vertrauenswürdige Konten aus" akzeptierte. Die Vorbedingung wird explizit angegeben und nicht als selbstauslösend bewertet.os.open(keysfile, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600) und arbeite mit dem Deskriptor; verweigere (oder unlink-and-replace, entsprechend dem follow=False-Docstring-Vertrag), wenn sshdir oder keysfile ein Symlink ist.os.chown(...) durch os.lchown oder durch os.fchown auf einem sicher geöffneten Deskriptor, und ebenso os.fchmod anstelle von os.chmod, sodass Besitz und Berechtigungen nicht durch einen Link umgeleitet werden können.chown/chmod so ab, dass es nur auf einen Pfad angewendet wird, den das Modul selbst gerade sicher erstellt hat, nicht auf einen bereits vorhandenen (möglicherweise symlink-verknüpften) Pfad.| Datum | Ereignis |
|---|---|
| 2026-06-08 | Gemeldet an [email protected] (Red Hat Product Security CC als CNA), dargestellt als Geschwister von CVE-2024-9902 |
| 2026-06-10 | CVE-2026-11837 zugewiesen und veröffentlicht; Anerkennung akzeptiert |
MIT. Siehe LICENSE.
| CVE | CVE-2026-11837 |
| Komponente | ansible.posix collection, file plugins/modules/authorized_key.py, function keyfile() |
| Typ | Elevation of Privilege (local). CWE-59 / CWE-61 (link following) with CWE-282 (improper ownership management) |
| CVSS | 7.3 (High) |
| Veröffentlicht | 2026-06-10 |
| Gemeldet von | Valentino Paulon |