Neutron, nuestro motor de IA, obtuvo un 97.1% en el benchmark CyberGym de UC Berkeley. Más información

Análisis a fondo

Análisis a fondo

Omisión de contraseña de OpenSSH en musl, corregida en 10.6p2 (CVE-2026-123158)

OpenSSH sobre musl sin PAM acepta una contraseña de 257 bytes para cuentas bloqueadas; corregido en 10.6p2 y en los paquetes de Alpine.

Resumen

  • El sshd de OpenSSH compilado con musl sin PAM acepta cualquier contraseña de más de 256 bytes para las cuentas cuyo campo shadow es *. El proyecto OpenSSH publicó CVE-2026-123158 y la versión 10.6p2 el 5 de noviembre de 2026.
  • Los servidores Alpine Linux que ejecutan sshd con autenticación por contraseña están expuestos si otra cuenta local tiene un hash $5$ o $6$. Las imágenes Alpine oficiales son un despliegue amplio; los contenedores sin sshd no se ven afectados.
  • MITRE asigna CVSS v3.1 8.1 High (CWE-394). Hay un laboratorio público y dos repositorios de prueba de concepto; no se ha informado de explotación, y la CVE no está en KEV al 6 de noviembre de 2026.
  • Actualice a OpenSSH 10.6p2, o al paquete de Alpine corregido para su rama, y reinicie sshd. Si no puede, configure PasswordAuthentication no; consulte «¿Cómo lo corrijo?».

¿Estoy afectado?

Está expuesto solo cuando se cumplen todas estas condiciones:

  • El host ejecuta OpenSSH anterior a 10.6p2, o un paquete openssh de Alpine inferior a la versión corregida de la tabla.
  • sshd está compilado y se ejecuta con musl y sin PAM. El paquete openssh-server de Alpine está compilado sin PAM. openssh-server-pam con UsePAM yes no se ve afectado; el mismo paquete con UsePAM no sí.
  • PasswordAuthentication yes está activo. Es el valor predeterminado de OpenSSH.
  • Al menos una cuenta accesible por contraseña tiene * como campo de contraseña shadow. En Alpine esto incluye root, que se entrega como root:* en la imagen base desde aproximadamente Alpine 3.16, los usuarios de solo clave creados con adduser sin contraseña, los usuarios de un servicio de directorio sin entrada shadow y el usuario alpine de las imágenes oficiales tiny-cloud.
  • Al menos una cuenta local tiene un hash crypt $5$ o $6$, para que sshd pueda tomar prestada una sal. Una máquina sin ningún hash SHA-256 ni SHA-512 no llega a la falla.

Para root, el valor predeterminado PermitRootLogin prohibit-password sigue bloqueando los inicios de sesión por contraseña. Root se vuelve accesible cuando se configura PermitRootLogin yes, o cuando otra cuenta * que puede iniciar sesión tiene una vía a root, como doas sin contraseña en las imágenes tiny-cloud.

Los contenedores que no ejecutan sshd no se ven afectados. Las cuentas bloqueadas con ! no pueden iniciar sesión ni por contraseña ni por clave, así que no son un objetivo. Los sistemas con PasswordAuthentication no, los que usan openssh-server-pam con UsePAM yes y los que no tienen ningún hash $ también quedan fuera de esta falla.

Rama Versiones afectadas Versión corregida
OpenSSH upstream (musl, PAM desactivado) 10.6p1 y anteriores 10.6p2 (publicada el 5 de noviembre de 2026)
Alpine edge anterior a 10.6_p2-r0 10.6_p2-r0
Alpine 3.24 anterior a 10.3_p1-r2 10.3_p1-r2 (versión puntual 3.24.4)
Alpine 3.23 anterior a 10.2_p1-r1 10.2_p1-r1 (versión puntual 3.23.8)
Alpine 3.22 anterior a 10.0_p1-r11 10.0_p1-r11 (versión puntual 3.22.8)
Alpine 3.21 anterior a 9.9_p2-r1 9.9_p2-r1 (versión puntual 3.21.10)
Alpine 3.20 anterior a 9.7_p1-r6 9.7_p1-r6
Alpine 3.19 anterior a 9.6_p1-r3 9.6_p1-r3
Alpine 3.18 anterior a 9.3_p2-r4 9.3_p2-r4
Alpine 3.17 anterior a 9.1_p1-r7 9.1_p1-r7
Alpine 3.16 anterior a 9.0_p1-r6 9.0_p1-r6

Alpine publicó paquetes corregidos para cada rama desde 3.16 hasta 3.24 y para edge, incluidas las versiones que ya no reciben actualizaciones periódicas. Las cuatro versiones puntuales actuales que incluyen la corrección son 3.21.10, 3.22.8, 3.23.8 y 3.24.4.

Compruebe qué ejecuta el host:

# Alpine: the installed OpenSSH packages and versions
apk info -v openssh-server openssh-server-pam 2>/dev/null

# Whether this sshd uses PAM (no means the vulnerable build on Alpine)
sshd -T 2>/dev/null | grep -i '^usepam'

# Whether password authentication is on
sshd -T 2>/dev/null | grep -i '^passwordauthentication'

# Accounts whose shadow password field is exactly *
sudo awk -F: '$2=="*"{print $1}' /etc/shadow

# Whether any local account has a SHA-256 or SHA-512 hash
sudo awk -F: '$2 ~ /^\$[56]\$/{print $1}' /etc/shadow

sshd -T requiere root y una configuración legible. En un sistema musl que no sea Alpine, como OpenWrt, Buildroot o Yocto, use el gestor de paquetes de la distribución y ssh -V para identificar la versión de OpenSSH, y compárela con el paquete corregido del proveedor.

¿Cómo lo corrijo?

  1. En Alpine, actualice el paquete y reinicie sshd: apk upgrade -U -a rc-service sshd restart La actualización no reinicia un sshd en ejecución, así que el reinicio es necesario. Reconstruya cualquier imagen de contenedor desde la base o el paquete corregido y vuelva a crear el contenedor; descargar una imagen nueva sin recrear el contenedor deja el proceso antiguo en marcha.
  2. En otro sistema musl, actualice OpenSSH a 10.6p2 o al paquete corregido de la distribución. Las notas de la versión 10.6p2 de OpenSSH nombran la corrección, y el aviso de Alpine enumera el paquete corregido para cada rama.
  3. Si no puede actualizar, desactive la autenticación por contraseña. Mantenga abierta su sesión actual y pruebe un inicio de sesión por clave antes de cerrarla. En Alpine 3.19 y posteriores, el archivo drop-in es suficiente: 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 En versiones anteriores de Alpine, que ignoran el directorio drop-in, edite /etc/ssh/sshd_config directamente y reinicie sshd. Configurar PasswordAuthentication no también elimina los inicios de sesión por contraseña de las cuentas con un hash real; ese es el coste previsto.
  4. Si necesita autenticación por contraseña y no puede actualizar, elimine los objetivos * o hágalos inalcanzables. Bloquee las cuentas sin uso con un campo ! en lugar de *, o elimínelas. El campo ! también bloquea los inicios de sesión con clave; mantenga abierta una sesión administrativa alternativa. Esto cierra la ruta de esa cuenta concreta pero no corrige la comparación; actualice en cuanto pueda.

¿Cuál es la causa raíz?

La falla es una comparación entre dos cadenas que valen *, una porque la cuenta no tiene contraseña y otra porque crypt() de musl usa * como valor de error. En auth-passwd.c, sshd sin PAM toma el campo de contraseña almacenado y aplica el hash a la contraseña suministrada con él:

char *pw_password = authctxt->valid ? shadow_pw(pw) : pw->pw_passwd;

if (pw_password == NULL)
    return 0;

/* Check for users with no password. */
if (strcmp(pw_password, "") == 0 && strcmp(password, "") == 0)
    return (1);

/*
 * Encrypt the candidate password using the proper salt, or pass a
 * NULL and let xcrypt pick one.
 */
if (authctxt->valid && pw_password[0] && pw_password[1])
    salt = pw_password;
encrypted_password = xcrypt(password, salt);

/*
 * Authentication is accepted if the encrypted passwords
 * are identical.
 */
return encrypted_password != NULL &&
    strcmp(encrypted_password, pw_password) == 0;

Para una cuenta cuyo campo es *, pw_password[1] es el NUL final, así que salt queda en NULL. xcrypt() llama entonces a pick_salt(), que recorre la base de cuentas hasta encontrar un campo $5$ o $6$ y devuelve el tipo y la sal de ese hash:

if (salt == NULL)
    salt = pick_salt();
...
crypted = crypt(password, salt);

La implementación SHA-512 de musl rechaza una clave de más de 256 bytes. El límite es una protección contra la denegación de servicio, porque el cifrado SHA-512 se ralentiza a medida que crece la clave:

#define KEY_MAX 256
...
/* reject large keys */
for (i = 0; i <= KEY_MAX && key[i]; i++);
if (i > KEY_MAX)
    return 0;

Cuando la función interna devuelve 0, __crypt_sha512() de musl devuelve la cadena *:

p = sha512crypt(key, setting, output);
...
if (!p || q != testbuf || memcmp(testbuf, testhash, sizeof testhash))
    return "*";
return p;

sshd compara el * devuelto con el * almacenado y acepta el inicio de sesión. OpenSSH limita la contraseña suministrada a 1024 bytes, así que el rango que funciona va de 257 a 1024 bytes. El registro CVE lo describe como 256 a 1024; la clave de 256 bytes todavía es aceptada por musl y produce un hash real, así que 257 es el valor más corto que provoca el retorno de error.

La falla se volvió alcanzable en 2016, cuando OpenSSH añadió pick_salt() como corrección del canal temporal CVE-2016-6210. Antes de ese cambio, una cuenta * se hasheaba con la sal DES fija xx, que no devolvía *.

¿Cómo se explota?

  1. El atacante encuentra un servicio SSH en un sistema musl con autenticación por contraseña activa.
  2. El objetivo tiene una cuenta * accesible por contraseña: root con PermitRootLogin yes, un usuario de despliegue de solo clave, un usuario de directorio o el usuario alpine de una imagen tiny-cloud.
  3. Otra cuenta local tiene un hash $5$ o $6$, así que sshd puede tomar prestada su sal.
  4. El atacante envía una contraseña de 257 a 1024 bytes para la cuenta *. Los caracteres no importan.
  5. sshd aplica el hash a la contraseña larga con la sal prestada. musl devuelve *; la comparación con el * almacenado tiene éxito, y el atacante obtiene un shell como esa cuenta.

El laboratorio público de sshhole.com permite probar la comparación en un navegador, y el registro CVE enumera dos repositorios de prueba de concepto. No se ha informado públicamente de explotación en entornos reales al 6 de noviembre de 2026.

¿Cómo se desarrolla el ataque?

Una contraseña larga para una cuenta sin contraseña llega al retorno de error * de musl, y sshd la acepta porque el campo almacenado también es *.

flowchart LR A["El atacante envía una contraseña de 257 bytes o más"] --> B["sshd toma prestada una sal $6$ de otra cuenta"] B --> C["musl rechaza la clave larga y devuelve *"] C --> D["sshd compara * con el * almacenado"] D --> E["Inicio de sesión aceptado como la cuenta bloqueada"]

¿Qué puede hacer un atacante?

Un atacante puede iniciar sesión como cualquier cuenta * a la que llegue la autenticación por contraseña, con los privilegios de esa cuenta. El impacto depende de la cuenta:

  • Root en Alpine, cuando PermitRootLogin yes está configurado: control total del host.
  • El usuario alpine de una imagen tiny-cloud, que tiene doas sin contraseña: root en la instancia.
  • Un usuario de despliegue de solo clave: el atacante obtiene el shell que habría tenido ese usuario, incluidas sus claves, credenciales de nube y acceso a la aplicación.
  • Un usuario de servicio de directorio sin entrada shadow: el atacante suplanta a ese usuario.

Se aplican todas las condiciones de «¿Estoy afectado?»: musl, PAM desactivado, autenticación por contraseña activa, una cuenta *, un hash $5$ o $6$ en otra cuenta y accesibilidad de red. La falla no revela ni descifra un hash de contraseña real, y no afecta a las cuentas bloqueadas con !. No es una falla de preautenticación en el sentido de saltarse el protocolo SSH; el atacante completa un inicio de sesión por contraseña normal, con una contraseña que el servidor debería haber rechazado.

¿Por qué funciona la corrección?

La corrección añade una longitud mínima para un hash almacenado y se niega a comparar un campo de un solo carácter:

+#define MIN_PASSWORD_HASH_LEN  4
 #define MAX_PASSWORD_LEN   1024
@@
-   return encrypted_password != NULL &&
+   ret = encrypted_password != NULL &&
+       strlen(pw_password) >= MIN_PASSWORD_HASH_LEN &&
        strcmp(encrypted_password, pw_password) == 0;
+   freezero(pw_password, strlen(pw_password));
+   return ret;

Un campo * ocupa un byte, por debajo del nuevo mínimo de cuatro bytes, así que la comparación es falsa incluso cuando musl devuelve *. Un campo SHA-512 real es mucho más largo que cuatro bytes: $6$, una sal y el hash. El mismo cambio se aplica a las rutas de contraseña portable y UnixWare, y la corrección también libera la copia shadow. El proyecto OpenSSH lo llama una solución provisional porque la causa raíz es el valor de error de musl, pero cierra la ruta de sshd. Los paquetes estables de Alpine llevan la misma corrección.

¿Cómo compruebo si estuve comprometido?

No hay indicadores de compromiso publicados. Un inicio de sesión correcto deja una línea Accepted password para una cuenta que nunca debería aceptar contraseña, así que empiece por ahí:

# Alpine and other syslog-based systems
sudo grep -i "Accepted password" /var/log/messages

# Compare the account name with the * accounts on the host
sudo awk -F: '$2=="*"{print $1}' /etc/shadow

Un resultado es una pista, no una prueba: una cuenta con un hash real también puede producir una línea Accepted password. Compruebe la dirección de origen y la hora con sus propios inicios de sesión. Después revise el uso de doas y sudo, los usuarios creados recientemente, las nuevas entradas authorized_keys y los archivos o servicios a los que podía llegar la cuenta. En una imagen tiny-cloud, compruebe si el usuario alpine inició sesión desde una dirección que no reconoce.

Un intento fallido no deja un rastro especial, y uno correcto puede haberse borrado de los registros. Si no puede descartar un inicio de sesión, trate el host como comprometido, reconstruirlo desde una imagen de confianza y renueve todas las credenciales a las que podía llegar.

Cronología

  • 2012-08: musl cambia crypt() para devolver * en lugar de NULL cuando no puede calcular un hash.
  • 2012-08-29: musl limita las claves SHA-512 crypt a 256 bytes, lo que hace que la ruta de claves largas devuelva *.
  • 2016-07: OpenSSH añade pick_salt() para usuarios inválidos como corrección temporal de CVE-2016-6210, lo que hace alcanzable la comparación *.
  • 2026-10-30: una revisión de seguridad de OpenSSH corrige pick_salt(); la comparación vulnerable está cuatro líneas después.
  • 2026-11-05: se publican OpenSSH 10.6p2 y CVE-2026-123158, y Alpine publica su aviso y sus paquetes corregidos.
  • 2026-11-06: no se ha informado públicamente de explotación en entornos reales; la CVE no está en el catálogo KEV de CISA.

La falla vivió en musl y OpenSSH durante 14 años antes de la corrección de 2026.
Figura 1: del valor de error de musl en 2012 a la corrección de OpenSSH en 2026. Fuente: commits de musl b3c4cc12 y 88bf5a8a, notas de la versión 10.6p2 de OpenSSH y CVE-2026-123158, comprobados el 6 de noviembre de 2026

Preguntas frecuentes

¿Afecta esto a Debian, Ubuntu o Fedora?

No. Esos sistemas usan glibc y PAM, y la omisión necesita el retorno de error * de musl con PAM desactivado. La misma versión de OpenSSH sobre glibc no toma esta ruta.

Uso Alpine pero solo inicios de sesión por clave. ¿Estoy afectado?

Sí, si PasswordAuthentication sigue activo y existe una cuenta *. El atacante inicia sesión como la cuenta * con una contraseña larga; su método de inicio de sesión por clave no importa. Configure PasswordAuthentication no o actualice.

¿Root es explotable por defecto?

No por defecto. Alpine entrega root con un campo *, pero el valor predeterminado PermitRootLogin prohibit-password de OpenSSH bloquea los inicios de sesión por contraseña para root. Root se vuelve accesible cuando se configura PermitRootLogin yes, o cuando otra cuenta * puede escalar, como el usuario alpine con doas sin contraseña.

¿Hay una CVE que pueda seguir?

Sí. CVE-2026-123158 se publicó el 5 de noviembre de 2026 con CVSS v3.1 8.1 High y CWE-394. El aviso de OpenSSH y el de Alpine son las fuentes principales de remediación.

¿Se explotó en entornos reales?

No existe ningún informe público de explotación al 6 de noviembre de 2026, y la evaluación SSVC de CISA registra una prueba de concepto pública pero no explotación activa. La falla es lo bastante antigua como para que no se pueda descartar un inicio de sesión discreto con datos públicos; revise los registros.

¿Debo rotar las claves SSH?

Solo si encuentra pruebas de un inicio de sesión que no puede explicar. Si se accedió al host, trate todas las claves y credenciales a las que podía llegar como expuestas y rótelas después de reconstruirlo. Si los registros están limpios, actualizar y reiniciar sshd es suficiente.

Encuentre servicios SSH expuestos con Ostorlab

El descubrimiento de superficie de ataque de Ostorlab inventaría sus hosts expuestos a Internet y los servicios que exponen. Úselo para listar las máquinas que responden en el puerto TCP 22 y compruebe primero su versión de OpenSSH y su configuración de autenticación por contraseña.

Referencias

  • Notas de la versión 10.6p2 de OpenSSH, 5 de noviembre de 2026, la omisión de autenticación por contraseña y el crédito a Özgün Kültekin: https://www.openssh.org/releasenotes.html#10.6p2 (comprobado el 6 de noviembre de 2026)
  • Commit de OpenSSH 4353fdc43ef646c43a8dda7025cc1a898caf1482, «avoid password authn bypass», 5 de noviembre de 2026, la comprobación MIN_PASSWORD_HASH_LEN: https://github.com/openssh/openssh-portable/commit/4353fdc43ef646c43a8dda7025cc1a898caf1482 (comprobado el 6 de noviembre de 2026)
  • Registro CVE-2026-123158, MITRE, publicado el 5 de noviembre de 2026, CVSS v3.1 8.1, CWE-394 y las referencias de prueba de concepto: https://www.cve.org/CVERecord?id=CVE-2026-123158 (comprobado el 6 de noviembre de 2026)
  • Alpine Linux, «OpenSSH password authentication bypass», 5 de noviembre de 2026, las configuraciones afectadas, las versiones corregidas y la solución provisional: https://alpinelinux.org/posts/2026-11-05-openssh-password-authentication-bypass/ (comprobado el 6 de noviembre de 2026)
  • «sshole», el análisis de la investigadora y el laboratorio de navegador, la historia de musl y OpenSSH y las condiciones de explotación: https://sshhole.com/ (comprobado el 6 de noviembre de 2026)
  • Commit de musl b3c4cc12, «crypt: return "*" instead of NULL on failure», agosto de 2012: https://git.musl-libc.org/cgit/musl/commit/?id=b3c4cc12 (comprobado el 6 de noviembre de 2026)
  • Commit de musl 88bf5a8a, el límite de 256 bytes para las claves SHA-512, 29 de agosto de 2012: https://git.musl-libc.org/cgit/musl/commit/?id=88bf5a8a (comprobado el 6 de noviembre de 2026)
  • Archivo auth-passwd.c de OpenSSH en la etiqueta 10.6p1, la comparación vulnerable: https://github.com/openssh/openssh-portable/blob/V_10_6_P1/auth-passwd.c (comprobado el 6 de noviembre de 2026)
  • Archivo openbsd-compat/xcrypt.c de OpenSSH en la etiqueta 10.6p1, pick_salt() y xcrypt(): https://github.com/openssh/openssh-portable/blob/V_10_6_P1/openbsd-compat/xcrypt.c (comprobado el 6 de noviembre de 2026)
  • Docker Hub, la imagen oficial alpine, 12,476,923,091 descargas, la cifra de alcance: https://hub.docker.com/_/alpine (comprobado el 6 de noviembre de 2026)
  • Flujo JSON del catálogo Known Exploited Vulnerabilities de CISA, comprobado para CVE-2026-123158 el 6 de noviembre de 2026 (no incluida): https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (comprobado el 6 de noviembre de 2026)