El 5 de noviembre el equipo de OpenSSH publicó 10.6p2, calificado como crítico, para un bypass de login que no necesita código de exploit, ni corrupción de memoria, ni payload ingenioso. Envías una contraseña de más de 256 caracteres a una cuenta que no tiene contraseña. Entras.
La falla no vive en un rincón olvidado de internet. Vive en Alpine Linux, y por tanto en una porción muy grande de las imágenes de contenedor que tu equipo descarga cada semana. Si tienes un usuario de despliegue solo con clave en un host Alpine con la autenticación por contraseña dejada por defecto, esa cuenta ya no es solo de clave.
Aquí está la parte que debería captar tu atención: las cuentas en riesgo son las que endureciste.
Qué ocurre realmente en la red
Sin PAM, sshd verifica las contraseñas por sí mismo. Toma el intento, lo hashea con crypt() y compara el resultado con el campo almacenado en /etc/shadow. Una cuenta normal guarda algo como $6$abc$k2P9...: un marcador de algoritmo, una sal, un hash.
Una cuenta sin contraseña guarda un solo *. Ese marcador no es un hash y ninguna contraseña puede coincidir con él. Ha sido así desde 1980, cuando Bill Joy puso un * en el campo de contraseña de la cuenta daemon en 3BSD. Quince palabras de intención, aún respetadas por cada Unix del planeta.
Pero un * es demasiado corto para albergar una sal. Así que sshd toma prestada una: toma la sal de otra cuenta local que tenga un hash real, hashea tu intento de todas formas y compara. Ese préstamo llegó en julio de 2016 como la corrección de CVE-2016-6210, un oráculo de temporización que permitía a los atacantes enumerar nombres de usuario válidos. Desperdiciar la misma cantidad de tiempo en un usuario inexistente era todo el punto.
Ahora añade 2012. musl, la biblioteca C sobre la que está construido Alpine, cambió crypt() para que una llamada fallida devuelva un solo * en lugar de NULL, porque demasiados programas nunca verificaban NULL y simplemente se caían. Veinte días después musl topó el hasheo sha256 y sha512 a 256 bytes para detener ataques de denegación de servicio que alimentan al hasheador con entradas enormes.
Pon esas decisiones en un solo proceso y la lógica se invierte:
- El campo almacenado es
*. - Tu contraseña es de 257 bytes o más, así que
crypt()la rechaza y devuelve*. - sshd compara
*con*. - Coincide.
La ruta de error es la puerta. No un bug en el sentido de una escritura de memoria extraviada, sino una cadena de cuatro decisiones individualmente razonables, tomadas con décadas de diferencia, que ahora coinciden en la respuesta incorrecta.
Quién está expuesto de verdad
El aviso de Alpine lista cuatro condiciones que deben cumplirse todas:
- El servidor ejecuta
openssh-server, que se construye sin PAM, o ejecutaopenssh-server-pamconUsePAM no. PasswordAuthenticationesyes, que es el valor por defecto de OpenSSH.- Una cuenta tiene
*en su campo de contraseña, lo que significa que no tiene contraseña establecida. - Al menos otra cuenta local tiene un hash
$5$o$6$.
Esa última condición es la silenciosa. Una sola cuenta en cualquier parte de la máquina con un hash de contraseña moderno basta para exponer a todas las cuentas sin una. En Alpine, las cuentas sin contraseña incluyen usuarios solo con clave creados con adduser, usuarios de directorio que llegan a través de musl-nscd, y root, que se ha distribuido como root:* desde Alpine 3.16.
Root sigue protegido por PermitRootLogin prohibit-password a menos que lo hayas cambiado. Ese escudo significa menos de lo que parece si una cuenta expuesta puede escalar: las imágenes tiny-cloud de 3.18.11 a 3.24.3 dan al usuario alpine un doas sin contraseña. Esas imágenes son seguras en el primer arranque y se abren en cuanto existe cualquier hash $6$ en la máquina, lo cual ocurre la primera vez que un administrador establece una contraseña de consola.
El atacante necesita acceso de red, un nombre de cuenta y una contraseña de entre 257 y 1.024 bytes. El aviso dice que tiene éxito en cada intento, sobre una sola conexión. No se habían reportado ataques que lo usaran cuando salió la corrección.
No afectados: sistemas no Linux, Linux basado en glibc, cualquier cosa que verifique contraseñas a través de PAM, sistemas con PasswordAuthentication no, cuentas bloqueadas con ! en lugar de *, contenedores que no ejecutan sshd, y máquinas donde ninguna cuenta lleva un hash de estilo $. OpenWrt está en gran parte bien porque por defecto usa Dropbear. Buildroot y Yocto merecen una mirada.
Qué hacer hoy
El investigador que lo encontró, Özgün Kültekin de Wakefield, publicó un análisis completo en sshhole.com, y el apodo que quedó no es imprimible en un deck para clientes. La remediación es aburrida y rápida:
- Actualiza. Ejecuta
apk upgrade -U -a, luegorc-service sshd restart. Actualizar el paquete no reinicia el daemon en ejecución, así que el reinicio no es opcional. Versiones con corrección: 3.21.10, 3.22.8, 3.23.8 y 3.24.4. Las ramas hasta 3.16 están fuera de soporte pero también recibieron paquetes. - Si no puedes parchear de inmediato, desactiva los inicios de sesión por contraseña. Confirma que tu login por clave funciona en una segunda sesión antes de cerrar la primera. Pon
PasswordAuthentication no, ejecutasshd -t, reinicia sshd. Verifica consshd -T | grep -i passwordauthentication. - Caza. Busca en
/var/log/messagesAccepted password forcontra una cuenta sin contraseña:alpine,root, un usuario de despliegue solo con clave. Revisa el uso dedoas, usuarios creados recientemente, claves autorizadas modificadas y cambios de archivos. - Si no puedes descartar un inicio de sesión, trata la máquina como comprometida. El aviso es así de directo, y tiene razón.
Si construyes OpenSSH tú mismo, 10.6p2 contiene el cambio: sshd ahora rechaza una coincidencia cuando el campo almacenado tiene menos de cuatro caracteres. OpenSSH llama a eso una solución temporal y señala a musl como la causa raíz.
Por qué la corrección llegó en un día
No se había asignado ningún identificador CVE cuando salió el aviso. Damien Miller fue explícito sobre por qué su equipo se movió a esta velocidad: los embargos largos son peligrosos ahora, y ayudan principalmente a los atacantes, porque el proyecto ha visto recientemente a agentes de IA redescubrir los mismos bugs, a veces en cuestión de horas.
Lee esa frase dos veces, porque es la verdadera historia. La vulnerabilidad es una decisión de diseño de 2016 chocando contra un cambio de biblioteca de 2012. El tiempo de respuesta lo marcan las máquinas que leen código toda la noche y pueden re-derivar un hallazgo desde un rastro parcialmente público en un día laborable. Las ventanas de embargo solían medirse contra la atención humana. Ahora se miden contra el throughput de inferencia.
Qué significa esto para tu equipo
Tres cosas, y solo la primera es sobre OpenSSH.
Audita la imagen base, no solo la aplicación. La mayoría de los equipos que conocemos pueden nombrar su versión de framework y no pueden nombrar su libc. La ubicuidad de Alpine en contenedores significa que los valores por defecto de musl son tus valores por defecto, y musl tomó una decisión defendible en 2012 que se convirtió en un bypass de autenticación en 2026.
Escribe tus suposiciones para que puedan ser falsadas. "Las cuentas solo con clave no pueden tener login con contraseña" era verdad, ampliamente creída y copiada en cientos de runbooks internos. Dejó de ser verdad sin que nadie editara los runbooks.
Asume que tu cuenta más silenciosa es la que importa. La cuenta sin contraseña, sin historial de shell, sin dueño en el organigrama. En esta falla fue el objetivo, y un hash $6$ suelto en cualquier parte de la máquina la convirtió en la puerta principal.
Parchea, reinicia, verifica. Luego haz la pregunta que realmente paga: ¿cuál de nuestras suposiciones no se ha vuelto a probar desde que las máquinas se volvieron más rápidas que nosotros?