
CVE-2026-11837: escalation dei privilegi locali nel modulo ansible.posix authorized_key tramite symlink-following chown. Scritto tecnico; fratello della CVE-2024-9902.
ansible.posix.authorized_keyEscalatione locale dei privilegi nel modulo Ansible ansible.posix.authorized_key tramite chown che segue i collegamenti simbolici (e creazione di file che segue i collegamenti) sulla directory ~/.ssh e sul file authorized_keys di un utente.
Questa è la descrizione di una vulnerabilità che ho segnalato e a cui è stato assegnato CVE-2026-11837 da Red Hat Product Security (il CNA). Viene pubblicata qui come documentazione tecnica, non come rivendicazione di novità: il problema è il fratello di CVE-2024-9902 in un modulo non coperto dalla correzione precedente.
Ringraziamento pubblicato da Red Hat: "Red Hat desidera ringraziare Valentino Paulon per aver segnalato questo problema."
Quando un playbook eseguito come root utilizza ansible.posix.authorized_key per gestire le chiavi di un utente locale, la funzione helper keyfile() del modulo:
~/.ssh dell'utente e del file ~/.ssh/authorized_keys con os.chown semplice (non os.lchown), eopen(..., "w") (senza O_NOFOLLOW).Con l'impostazione predefinita follow=False, il modulo non segue né sostituisce i collegamenti; opera direttamente sul percorso, quindi il kernel segue qualsiasi collegamento simbolico che l'utente target non privilegiato ha preposizionato all'interno della propria ~/.ssh. Questo produce due primitive che trasferiscono la proprietà di un percorso controllato da root all'utente non privilegiato, il quale poi scala a root. Entrambe sono state riprodotte end-to-end sulla raccolta a HEAD.
keyfile() (plugins/modules/authorized_key.py), con i predefiniti manage_dir=True, follow=False:
if manage_dir:
if not os.path.exists(sshdir): # os.path.exists SEGUE i collegamenti
try:
os.mkdir(sshdir, int('0700', 8))
...
os.chown(sshdir, uid, gid) # INCONDIZIONATO, chown semplice, segue il collegamento
os.chmod(sshdir, int('0700', 8)) # segue il collegamento
if not os.path.exists(keysfile): # segue il collegamento
...
f = open(keysfile, "w") # open() semplice, senza O_NOFOLLOW, segue il collegamento
...
try:
os.chown(keysfile, uid, gid) # INCONDIZIONATO, chown semplice, segue il collegamento
os.chmod(keysfile, int('0600', 8)) # segue il collegamento
except OSError:
pass
sshdir e keysfile sono derivati dalla home dell'utente target nel passwd (pwd.getpwnam(user).pw_dir), una directory di proprietà e controllo dell'utente target non privilegiato. Il parametro follow (predefinito False) sceglie solo se eseguire os.path.realpath() sul percorso; in nessun caso c'è un controllo os.path.islink, un open con O_NOFOLLOW, un os.lchown, o un unlink-e-sostituzione di un collegamento preesistente. Non viene eseguita alcuna riduzione dei privilegi (seteuid/setfsuid); tutto viene eseguito come root.
Le due chiamate os.chown vengono eseguite incondizionatamente, al di fuori delle guardie if not os.path.exists(...), quindi si attivano indipendentemente dal fatto che la directory/file esistesse già. Questo è ciò che rende affidabili entrambe le primitive.
Entrambe confermate end-to-end su una VM Linux usa-e-getta, con ansible.posix a HEAD. victim è un utente locale non privilegiato (uid 1000). Il modulo viene eseguito come root, esattamente come farebbe il playbook di un operatore. I canarini rappresentano obiettivi root sensibili.
Vettore 1: collegamento a directory (chown di una directory esistente di proprietà di root)
victim: ln -s /root/AD_CANARY ~/.ssh (con /root/AD_CANARY una directory esistente di proprietà di root).authorized_key per victim./root/AD_CANARY è passata da root:root a victim:victim. La chiamata os.chown(sshdir, ...) ha seguito il collegamento ~/.ssh e ha consegnato una directory di proprietà di root all'utente non privilegiato.Vettore 2: collegamento a file inesistente (creazione + chown di un nuovo file in path root)
victim (con ~/.ssh come directory normale): ln -s /root/AF_CANARY ~/.ssh/authorized_keys (la destinazione non esiste).authorized_key per victim./root/AF_CANARY è stato creato, di proprietà victim:victim, modalità 0600. open(keysfile, "w") ha creato la destinazione attraverso il collegamento e os.chown(keysfile, ...) l'ha consegnata.In entrambi i casi l'utente non privilegiato finisce per possedere un percorso controllato da root. Puntando il collegamento a una directory sotto /etc (Vettore 1) o a un nuovo file sotto /etc/cron.d/ o /etc/sudoers.d/ (Vettore 2) si ottiene root tramite il follow-on standard, in cui l'utente, ora proprietario, riscrive la destinazione.
Questo è un fratello di CVE-2024-9902 (il modulo user con generate_ssh_key, in ansible-core), che affrontava la stessa classe di seguito di collegamento. authorized_key si trova nella raccolta separata ansible.posix e non è stato modificato da quella correzione; il pattern di chown/creazione che segue il collegamento era ancora presente e non protetto. Viene segnalato come la stessa classe accettata in un modulo non raggiunto dalla correzione precedente, non come una causa principale nuova.
ansible.posix.authorized_key che ha come target l'account della vittima (l'uso comune di questo modulo). L'utente locale preposiziona il collegamento nella propria ~/.ssh; non esegue il playbook. Questo è innescato dall'operatore: lo stesso modello di innesco di CVE-2024-9902, che Red Hat ha accettato con la mitigazione "non eseguire su account non fidati". Il prerequisito è dichiarato esplicitamente, piuttosto che valutato come auto-innescato.os.open(keysfile, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600) e operare sul descrittore; rifiutare (o scollegare e sostituire, secondo il contratto della documentazione di follow=False) se sshdir o keysfile è un collegamento simbolico.os.chown(...) con os.lchown, o con os.fchown su un descrittore aperto in sicurezza, e analogamente os.fchmod invece di os.chmod, in modo che proprietà e permessi non possano essere reindirizzati attraverso un collegamento.chown/chmod incondizionati in modo che si applichino solo a un percorso che il modulo stesso ha appena creato in sicurezza, non a un percorso preesistente (possibilmente collegato).| Data | Evento |
|---|---|
| 2026-06-08 | Segnalato a [email protected] (Red Hat Product Security in CC come CNA), inquadrato come fratello di CVE-2024-9902 |
| 2026-06-10 | CVE-2026-11837 assegnato e pubblicato; credito accettato |
MIT. Vedi LICENSE.
| CVE | CVE-2026-11837 |
| Componente | Raccolta ansible.posix, file plugins/modules/authorized_key.py, funzione keyfile() |
| Tipo | Elevazione dei privilegi (locale). CWE-59 / CWE-61 (seguito di collegamento) con CWE-282 (gestione impropria della proprietà) |
| CVSS | 7.3 (Alta) |
| Pubblico | 2026-06-10 |
| Segnalato da | Valentino Paulon |