SSD Nodes Learn Hosting plans →
Guide Matt ConnorDi Matt Connor

OpenSSH CVE-2026-123158: la tua VPS è davvero a rischio?

Il bypass delle password di OpenSSH colpisce solo sshd con musl e senza PAM. Su una VPS glibc il rischio sta nei container Alpine: come trovarli, aggiornarli e controllare i log.

CVE-2026-123158 in breve: chi è esposto davvero

CVE-2026-123158, il bypass delle password di OpenSSH che molti titoli chiamano "sshole", colpisce sshd solo su Linux con musl libc e senza PAM, nelle versioni precedenti alla 10.6p2. Una VPS con Ubuntu, Debian, Fedora, AlmaLinux o Rocky Linux usa glibc. Il demone SSH dell'host quindi non passa dal codice vulnerabile. Il rischio reale sta dentro la VPS: un container Alpine che esegue sshd, una piccola macchina Alpine di servizio, oppure un router OpenWrt dove qualcuno ha sostituito Dropbear con openssh-server.

Questa guida ti fa controllare ogni pezzo con comandi che esegui tu. Non riporto l'output dei comandi, perché dipende dalla tua macchina. Per ogni comando spiego invece cosa cercare e cosa significa.

Cosa dicono OpenSSH, il record CVE e l'ACN

OpenSSH Portable 10.6p2 è uscita il 5 novembre 2026. Le note di rilascio la descrivono come "a critical security workaround that affects Linux systems that use the musl libc and PAM disabled". Il record CVE è stato pubblicato lo stesso giorno. Assegna un punteggio CVSS 3.1 di 8.1 (High) e indica Alpine come esempio di piattaforma colpita. Secondo il record, con alcune configurazioni degli account "any password between 256 and 1024 characters succeeds": viene accettata qualunque password di quella lunghezza.

In Italia il CSIRT dell'ACN (Agenzia per la cybersicurezza nazionale) ha pubblicato un alert, non un bollettino: AL08/261105/CSIRT-ITA. È uscito il 5 novembre 2026 alle 20:36 ed è stato aggiornato il 6 novembre con l'ID del CVE. La gravità indicata è "critica". L'alert parla di password "di lunghezza compresa tra 257 e 1.024 byte", quindi le fonti differiscono di un carattere sul limite inferiore. Per difenderti questa differenza non conta.

Il commit di correzione si intitola "avoid password authn bypass". Il messaggio del commit dice: "We do not consider the root cause to be within OpenSSH but we are well equipped to avoid it." La modifica in auth-passwd.c aggiunge MIN_PASSWORD_HASH_LEN 4: l'hash salvato per l'account deve avere almeno 4 caratteri, altrimenti nessuna password può corrispondere. Un account bloccato con * nel campo password ha un "hash" di un solo carattere. Il commit non lo scrive in modo esplicito, ma è la lettura più diretta della correzione.

Le condizioni del bypass: servono tutte insieme

Ho messo insieme le note di OpenSSH, l'alert dell'ACN e la scheda di OX Security (un fornitore di sicurezza, quindi una fonte secondaria). Il bypass richiede tutte queste condizioni:

  • sshd di OpenSSH Portable precedente alla 10.6p2, oppure un pacchetto della distribuzione senza la correzione;
  • Linux con musl libc, che OX indica in Alpine, OpenWrt, Buildroot e Yocto;
  • PAM non usato per l'autenticazione con password;
  • autenticazione con password attiva, che in sshd è il default (PasswordAuthentication vale yes se non lo imposti);
  • un account senza password utilizzabile, con il campo in /etc/shadow uguale a * (la scheda di OX dice "shown as *");
  • almeno un altro account con una password vera, cioè con un hash completo.

Se manca anche una sola condizione, il bypass non funziona. Per questo la domanda utile non è "ho Alpine da qualche parte?". La domanda utile è: "questo sshd, con questa configurazione e questi account, soddisfa tutte le righe?"

Chi è al sicuro: glibc, PAM, Dropbear

Damien Miller, di OpenSSH, lo ha scritto nell'annuncio sulla mailing list openssh-unix-dev: "This vulnerability does not exist on non-Linux systems, Linux/glibc systems and Linux systems that use PAM for password authentication." Red Hat dichiara che "Red Hat products are not affected by this vulnerability." Il motivo è che RHEL compila OpenSSH contro glibc e usa PAM per default. OX Security cita come non colpiti Debian, Ubuntu, Fedora e gli altri sistemi glibc, oltre a Dropbear e all'SSH di OpenBSD.

SSD Nodes propone Ubuntu, Debian, CentOS, Fedora, AlmaLinux e Rocky Linux. Alpine non è nell'elenco. Rocky e AlmaLinux sono ricompilazioni di RHEL, ma la dichiarazione di Red Hat copre i prodotti Red Hat, non queste due distribuzioni. Non devi fidarti di nessuno: il passo 1 ti fa controllare la libc del tuo sshd con un solo comando.

Hai un sistema musl senza PAM? La regola di Damien Miller

Nello stesso messaggio Miller dà un'istruzione precisa a chi gestisce un sistema Linux con musl senza PAM, e cita Alpine come esempio: "then stop reading and set PasswordAuthentication=no or disable SSH for these systems IMMEDIATELY." L'ordine giusto è questo: prima disattivi le password, poi aggiorni. La sezione sul tampone, più sotto, mostra come farlo senza chiuderti fuori dal server.

Passo 1: quale libc usa sshd sull'host?

Sull'host Ubuntu o Debian esegui:

ldd /usr/sbin/sshd | grep -E 'libc|musl'

Una riga con libc.so.6 indica glibc. Una riga con ld-musl indica musl. La FAQ di musl spiega che il suo linker dinamico si chiama /lib/ld-musl-<arch>.so.1 e che contiene già le funzioni di ldd. Una nota di sicurezza: la pagina di manuale di ldd avverte di non usarlo mai su un eseguibile non fidato, perché può eseguire codice arbitrario. /usr/sbin/sshd arriva dal pacchetto della tua distribuzione, quindi qui il comando è sicuro.

Se il risultato è glibc, il demone SSH dell'host è fuori dal perimetro del CVE. Però non hai finito. Resta da controllare quello che gira sopra l'host.

Passo 2: trovare i container con musl e sshd

Elenca i container in esecuzione, con immagine e porte pubblicate:

docker ps --format '{{.Names}}\t{{.Image}}\t{{.Ports}}'

docker ps mostra solo i container attivi. Aggiungi -a per vedere anche quelli fermi, perché possono ripartire domani con un'immagine vecchia. Se il tuo utente non è nel gruppo docker, metti sudo davanti a ogni comando docker.

Poi chiedi a ogni container quale sistema usa e se contiene un binario sshd:

for c in $(docker ps --format '{{.Names}}'); do
  echo "== $c"
  docker exec "$c" sh -c 'grep -h PRETTY_NAME /etc/os-release; ls /lib/ld-musl-*.so.1 /usr/sbin/sshd /usr/sbin/sshd.pam 2>/dev/null'
done

Un container interessa questa guida solo se mostra sia un file ld-musl sia un binario sshd o sshd.pam. In un container senza shell docker exec fallisce. In quel caso cerca nella documentazione dell'immagine se include un server SSH.

Guarda anche la colonna delle porte. Una porta pubblicata da Docker può essere raggiungibile da Internet anche se ufw sembra bloccarla, come spiega la guida su perché le porte pubblicate da Docker scavalcano ufw. Un sshd vulnerabile su una porta interna è meno grave di uno esposto a tutti, ma va corretto comunque.

Passo 3: leggere la configurazione effettiva con sshd -T

sshd -T controlla la configurazione, stampa quella effettiva ed esce. È più affidabile che leggere il file, perché tiene conto dei valori di default e dei file inclusi. In un container:

docker exec <nome> sshd -T | grep -Ei '^(passwordauthentication|usepam) '

Cerca due valori. passwordauthentication a yes vuol dire che le password sono attive. usepam a no vuol dire che PAM non viene usato. sshd -T può fermarsi con un errore, per esempio sulle chiavi host. In quel caso leggi direttamente il file di configurazione con grep, come nell'esempio di linuxserver più sotto.

Un dettaglio conta molto: un sshd compilato con PAM non è per forza protetto. Guarda il sorgente di auth-passwd.c alla versione 10.6p1: la funzione auth_password() passa da PAM solo dentro if (options.use_pam). Con UsePAM no il codice ricade sul confronto diretto con l'hash di /etc/shadow, che è il percorso vulnerabile. Il manuale di sshd_config dice che il default di UsePAM è no. Questa è la mia lettura del codice, non una frase di OpenSSH. È però coerente con le note di rilascio, che parlano di sistemi "built and run with PAM support disabled".

Passo 4: cercare gli account con * in /etc/shadow

La correzione rifiuta gli hash salvati più corti di 4 caratteri. Quindi il controllo più prudente elenca ogni account con un campo password così corto:

docker exec <nome> awk -F: 'length($2) < 4 {print $1 ": [" $2 "]"}' /etc/shadow

Secondo la pagina di manuale shadow(5), un valore come * o ! nel campo password significa che l'utente non può accedere con una password UNIX. Può però entrare in altri modi. Il caso indicato dalle fonti è *. Un campo vuoto è un problema diverso: l'account non ha password, e va corretto in ogni caso.

Poi elenca gli account con un hash vero, senza stampare l'hash:

docker exec <nome> awk -F: '$2 ~ /^\$/ {print $1}' /etc/shadow

Il container ha tutte le condizioni in questo caso: il primo comando trova un account con *, il secondo trova almeno un nome e il passo 3 mostra password attive con PAM spento. Su una macchina Alpine fuori da Docker esegui gli stessi comandi awk come root, senza il prefisso docker exec <nome>.

linuxserver/openssh-server: PAM compilato, ma spento

Il Dockerfile di linuxserver/openssh-server parte da ghcr.io/linuxserver/baseimage-alpine:3.24 e installa openssh-server-pam. Il servizio avvia /usr/sbin/sshd.pam con -f /config/sshd/sshd_config. Gli script dell'immagine modificano PasswordAuthentication, la porta e pochi altri valori. Non scrivono mai una riga UsePAM. La configurazione di default, sia quella di OpenSSH sia quella con le patch di Alpine, ha solo la riga commentata #UsePAM no. Da qui deduco che PAM è compilato ma non attivo. È una deduzione dai sorgenti, non una dichiarazione del progetto. Se è giusta, l'immagine passa dal percorso vulnerabile quando PASSWORD_ACCESS=true, la variabile che secondo il README abilita l'accesso con utente e password.

Verifica nel tuo container, invece di fidarti della mia deduzione:

docker exec <nome> printenv PASSWORD_ACCESS
docker exec <nome> grep -Ei '^[[:space:]]*(UsePAM|PasswordAuthentication)' /config/sshd/sshd_config
docker exec <nome> apk policy openssh-server-pam

Secondo l'advisory di Alpine, la versione corretta per il ramo 3.24 è 10.3_p1-r2. Il tag 10.3_p1-r2-ls242 su Docker Hub porta nel nome proprio quella versione. Il tag precedente, 10.3_p1-r1-ls241, no. apk policy ti dice cosa c'è davvero nel container. Se la versione è vecchia, scarica l'immagine nuova e ricrea il container dalla cartella del tuo file Compose:

docker compose pull
docker compose up -d

Finché non aggiorni, togli PASSWORD_ACCESS=true. Senza quel valore lo script dell'immagine imposta PasswordAuthentication no.

Gitea e Forgejo: password già spente nella configurazione fornita

Le immagini Docker standard di Gitea e Forgejo, cioè quelle senza il suffisso -rootless, partono da Alpine 3.24. Installano i pacchetti openssh e linux-pam. Il modello di sshd_config che usano entrambe contiene PasswordAuthentication no, PermitRootLogin no e una riga AllowUsers limitata all'utente del servizio. Con la configurazione fornita, quindi, manca la condizione delle password attive.

Ci sono due cautele. Gitea genera il file da un modello e accetta un file di include scelto da te, che può cambiare questi valori. Controlla con sshd -T come al passo 3. Inoltre le immagini pubblicate prima del 5 novembre 2026 possono contenere un pacchetto openssh senza correzione. Controlla con docker exec <nome> apk policy openssh e aggiorna l'immagine quando esce un tag ricostruito. Le varianti -rootless non le ho verificate.

OpenWrt con openssh-server al posto di Dropbear

Su OpenWrt la configurazione SSH standard passa da Dropbear, con il file /etc/config/dropbear. Il rischio nasce se qualcuno ha installato openssh-server. OpenWrt è tra i sistemi musl citati da OX. Inoltre il Makefile del pacchetto compila con --without-pam, a meno che tu non scelga la variante with-pam.

Nel feed dei pacchetti, il Makefile del ramo master e quello del ramo openwrt-25.12 sono passati a 10.6_p2 il 5 novembre 2026. Il ramo openwrt-24.10 è ancora a 9.9_p2-2, senza correzione. Attenzione: questi sono i sorgenti. Non ho potuto verificare che i pacchetti compilati siano già sui server di download. Non ho trovato nemmeno un advisory di OpenWrt per questo CVE. Controlla cosa è installato sul router con il gestore pacchetti della tua release, opkg oppure apk:

opkg list-installed | grep openssh
apk list --installed | grep openssh

Puoi trovare openssh-server su 24.10 o su un ramo più vecchio (i rami più vecchi non li ho controllati). In quel caso imposta PasswordAuthentication no in /etc/ssh/sshd_config e riavvia sshd, oppure torna a Dropbear.

Come aggiornare Alpine: apk upgrade e poi il riavvio di sshd

Secondo l'advisory di Alpine, è colpita ogni release controllata, dalla 3.21 alla 3.24 più edge. Lo stesso vale per le release non più supportate, dalla 3.16 alla 3.20. Le versioni corrette del pacchetto openssh sono:

  • edge: 10.6_p2-r0
  • 3.24: 10.3_p1-r2
  • 3.23: 10.2_p1-r1
  • 3.22: 10.0_p1-r11
  • 3.21: 9.9_p2-r1
  • 3.20: 9.7_p1-r6
  • 3.19: 9.6_p1-r3
  • 3.18: 9.3_p2-r4
  • 3.17: 9.1_p1-r7
  • 3.16: 9.0_p1-r6

Su una macchina Alpine, come root:

apk upgrade -U -a
rc-service sshd restart
apk policy openssh

Il riavvio non è facoltativo. L'advisory lo dice così: "Upgrading the package does not restart a running sshd, so you must do it after." Il pacchetto nuovo sostituisce i file su disco. Il processo sshd già in esecuzione però resta quello vecchio finché non lo riavvii. apk policy openssh deve mostrare come installata la versione dell'elenco per il tuo ramo, o una successiva.

In un container non usi rc-service. La strada pulita è un'immagine ricostruita, come visto per linuxserver. Se l'immagine nuova non c'è ancora, puoi eseguire docker exec <nome> apk upgrade -U -a e poi docker restart <nome>. La correzione resta nel container finché non lo ricrei da un'immagine vecchia. Quindi ripeti il controllo dopo ogni docker compose up -d.

Il tampone: PasswordAuthentication no con un drop-in

Prima di spegnere le password, verifica da un secondo terminale che l'accesso con chiave funzioni. Tieni aperta la sessione attuale. Se non hai ancora una chiave, la guida alle basi della gestione delle chiavi SSH spiega come crearla e installarla. Poi, come root, esegui i comandi dell'advisory di Alpine:

mkdir -p /etc/ssh/sshd_config.d
echo 'PasswordAuthentication no' > /etc/ssh/sshd_config.d/00-no-passwords.conf
sshd -t && rc-service sshd restart
sshd -T | grep -i passwordauthentication

sshd -t controlla solo la sintassi. Il riavvio quindi parte solo se la configurazione è valida. L'ultimo comando deve mostrare no. L'advisory precisa che la configurazione di default di Alpine legge /etc/ssh/sshd_config.d dalla 3.19 in poi, e che le release più vecchie ignorano la cartella. Su quelle release, o se sshd -T mostra ancora yes, l'advisory modifica il file principale:

sed -i.bak -e '1i PasswordAuthentication no' \
  -e 's/^\(\s*PasswordAuthentication[^y#]*\)yes/\1no/i' \
  /etc/ssh/sshd_config

Il comando aggiunge la riga in cima al file e cambia in no ogni riga attiva con yes. Salva anche una copia in sshd_config.bak. Poi ripeti sshd -t && rc-service sshd restart e il controllo con sshd -T.

Controllare i log: cerca "Accepted password for"

Il segnale da cercare è un accesso con password riuscito su un account che non dovrebbe averne una: root, alpine o un utente che usa solo chiavi. Su Alpine l'advisory indica il file /var/log/messages:

grep 'Accepted password for' /var/log/messages

Nei container sshd scrive spesso sullo standard error. linuxserver/openssh-server, per esempio, lo avvia con -e. In quel caso i log sono in docker logs:

docker logs <nome> 2>&1 | grep 'Accepted password for'
docker logs --since 2026-11-01T00:00:00Z <nome> 2>&1 | grep 'Accepted password for'

--since accetta una data RFC 3339, un timestamp Unix o una durata come 48h. docker logs vede solo i log che il driver ha conservato. Un container ricreato da poco quindi può non avere la storia che ti serve. Su Gitea e Forgejo, con le password spente, devi indagare su qualunque riga di questo tipo.

Se trovi un accesso che non sai spiegare

L'advisory di Alpine è chiaro: "If you cannot rule out a login, treat the machine as compromised." Chiede anche di controllare l'uso di doas, gli utenti creati di recente, i file nuovi e le chiavi autorizzate. Il primo passo è tagliare la rete dal pannello del provider, non dall'interno della macchina. La guida su cosa fare se la tua VPS è stata compromessa spiega l'ordine dei passi, dall'isolamento alla ricostruzione.

Per un container, guarda subito cosa poteva toccare:

docker inspect --format '{{.HostConfig.Privileged}} {{json .Mounts}}' <nome>

Il container può essere privilegiato, oppure avere /var/run/docker.sock tra i mount. In questi casi una shell nel container può dare il controllo dell'host. Allora considera compromessa tutta la VPS, non solo il container.

Lo sfruttamento è confermato? Le fonti non concordano

Help Net Security riporta che Previdian ha ottenuto un accesso root su un proprio honeypot con Alpine e OpenSSH reali. Secondo Previdian, il primo tentativo è arrivato "less than nine hours after CVE publication". Lo stesso articolo dice che CISA non ha inserito il CVE nel suo catalogo KEV (l'elenco delle vulnerabilità sfruttate in modo noto). Altre testate hanno scritto che non ci sono attacchi confermati, ma non ho potuto verificare quegli articoli. Non serve risolvere la disputa per decidere cosa fare. Se un tuo sshd soddisfa tutte le condizioni, correggilo subito.

Non eseguire PoC e script "checker" presi da GitHub

Il record CVE rimanda anche a pagine con exploit e a repository di riproduzione. Non li linko e non li consiglio. La scheda di OX Security dice: "Do not clone or run unverified exploit or scanner scripts from public repos or social media." Uno script che esegui come root sul tuo server ha gli stessi poteri di un attaccante. I passi da 1 a 4 di questa guida rispondono alla stessa domanda con comandi che puoi leggere riga per riga.

Quando l'emergenza è chiusa, la regola che ti protegge anche dal prossimo bug di questo tipo è l'accesso solo con chiave. La guida all'hardening di SSH su una VPS mostra come renderla la configurazione normale di ogni server.

FAQ

La mia VPS Ubuntu o Debian è vulnerabile a CVE-2026-123158?

Il demone SSH dell'host non lo è, se usa glibc. Damien Miller scrive che la vulnerabilità non esiste sui sistemi Linux con glibc, e Ubuntu e Debian usano glibc. Verifica con ldd /usr/sbin/sshd | grep -E 'libc|musl'. Poi controlla i container e le altre macchine. Un container Alpine con sshd e password attive può essere vulnerabile anche su un host Ubuntu.

Un sshd compilato con PAM è al sicuro?

Non per forza. Nel codice di OpenSSH la verifica della password passa da PAM solo se UsePAM è attivo, e il default di UsePAM è no. Un sshd compilato con PAM ma avviato con UsePAM no usa il confronto diretto con /etc/shadow, che è il percorso vulnerabile. Controlla il valore con sshd -T | grep -i usepam.

Basta eseguire apk upgrade su Alpine?

No. apk upgrade -U -a installa il pacchetto corretto, ma il processo sshd già in esecuzione resta quello vecchio. Dopo l'aggiornamento esegui rc-service sshd restart. Poi verifica con apk policy openssh che la versione installata sia quella corretta per il tuo ramo secondo l'advisory di Alpine. In un container usa un'immagine ricostruita, oppure riavvia il container dopo l'aggiornamento.

Come capisco se qualcuno è entrato sfruttando il bypass?

Cerca Accepted password for in /var/log/messages su Alpine. Per i container che scrivono i log sullo standard error, cerca la stessa stringa in docker logs. Una riga su un account che non dovrebbe avere una password è un segnale grave, per esempio root, alpine o un utente che usa solo chiavi. Se non puoi escludere un accesso, Alpine consiglia di considerare la macchina compromessa. Isolala dal pannello del provider e ricostruiscila.

Questo articolo ti è stato utile?