Un fallo escondido durante catorce años en musl libc, la biblioteca C minimalista que sostiene Alpine Linux y una enorme cantidad de imágenes de Docker, permite que un atacante entre a una cuenta SSH marcada como bloqueada sin adivinar ninguna contraseña. Basta con enviar una cadena de más de 256 bytes contra una cuenta cuyo campo de contraseña en /etc/shadow sea un simple asterisco y con que el servidor use musl sin PAM. El hallazgo, publicado en sshhole.com, importa de forma directa para México y América Latina porque la mayoría de los contenedores que corren en la nube de la región se construyen sobre imágenes base de Alpine, justamente las configuraciones más expuestas.
Qué es musl y por qué está en todas partes
Musl es la biblioteca C estándar (libc) que usa Alpine Linux y muchas imágenes de Docker. Su objetivo es producir binarios pequeños y estáticos, lo que la volvió popular en entornos de contenedores donde cada megabyte cuenta. Implementa las funciones POSIX que un programa en C necesita, entre ellas crypt(), la que sshd llama para comparar la contraseña escrita por el usuario con el hash guardado en el sistema.
El problema nace de una decisión de diseño razonable en su momento. La especificación Single UNIX de 1997 estableció que, si crypt() no puede hacer su trabajo, debe devolver NULL. Muchos programas nunca revisaron ese caso y terminaban comparando contra NULL, lo que los hacía crashear. Musl resolvió eso en agosto de 2012 con el commit b3c4cc12: en vez de NULL, crypt() devuelve un carácter que ninguna contraseña real puede producir. Ese carácter es el asterisco, el mismo símbolo que desde 1980 marca una cuenta bloqueada en Unix.
Veinte días más tarde, el commit 88bf5a8a sumó sha512crypt a musl y le puso un límite de 256 bytes (constante KEY_MAX), porque sha512crypt se vuelve más lento cuanto más larga es la entrada y una contraseña gigante podría ocupar el servidor por un rato largo. Pasado ese límite, crypt() falla y, por el cambio de veinte días antes, devuelve asterisco.
La prueba: seis builds y casi 17.600 intentos
El autor del reporte compiló sshd de seis formas distintas (con glibc o musl, con o sin PAM, con o sin /etc/shadow) y le lanzó cerca de 17.600 intentos de login con hashes raros, cuentas rotas y contraseñas demasiado largas. Según sshhole.com, usó enjambres de agentes automatizados para generar y correr esos intentos en paralelo. Cinco de las seis combinaciones coincidieron en cada resultado.
El patrón era siempre el mismo: una cuenta con asterisco en el campo de contraseña de /etc/shadow dejaba entrar a cualquiera que enviara una contraseña de más de 256 bytes, pero solo en la build de musl sin PAM. La tabla siguiente resume qué combinación de libc y PAM resultó vulnerable.
- glibc con PAM: rechaza el login; PAM nunca llama a
crypt()para un campo con asterisco. - glibc sin PAM: rechaza el login;
crypt()de glibc no devuelve asterisco al fallar. - musl con PAM: rechaza el login, por el mismo motivo que glibc con PAM.
- musl sin PAM: acepta el login en cuentas bloqueadas con asterisco.
El mecanismo: una colisión de representación
No se trata de una falla criptográfica sino de una colisión de representación. sshd, cuando no usa PAM, llama directamente a crypt() con la contraseña y el campo de shadow, y compara el resultado contra ese mismo campo tratando ambos como simples cadenas de texto. Si el campo guardado ya es asterisco y crypt() también devuelve asterisco porque la entrada es demasiado larga, la comparación de strings da verdadero, aunque ninguno de los dos lados sea un hash real.
La ruta con PAM nunca pasa por ahí. PAM intercepta la autenticación antes de llegar a crypt(), y cuando el campo de la cuenta ya es asterisco ni siquiera intenta calcular un hash; por eso la colisión solo aparece en builds sin PAM, que son comunes en contenedores Alpine mínimos.
El asterisco, un candado de 46 años
La marca de cuenta bloqueada nació en 1980, cuando Bill Joy la agregó en 3BSD para la cuenta daemon, con un detalle curioso: el nombre real que anotó para ese usuario fue “the devil himself”. Un asterisco no es un hash válido, así que ninguna contraseña puede coincidir con él, y por eso durante 46 años funcionó como un candado seguro. En los noventa, el hash se mudó a /etc/shadow con el formato $6$sal$hash (sha512crypt).
Los antecedentes que rozaron el bug
Hubo quienes estuvieron cerca de notarlo. En 2013, un desarrollador de Debian le pidió a glibc copiar el asterisco de musl en la lista libc-alpha; seis años después, Debian migró a libxcrypt, que usa *0 (un carácter distinto), y cerró el pedido ahí. En diciembre de 2024, un hilo de la lista de musl discutió un bug de busybox passwd con contraseñas de 257 caracteres, el mismo límite y el mismo asterisco, pero nadie mencionó a sshd.
También hay dos CVE relacionados con el mismo símbolo. En 2016 llegó el CVE-2016-6210: preguntarle a sshd por un usuario que no existe respondía más rápido que por uno real, porque no había ningún hash que calcular, y cronometrando esa diferencia un atacante podía enumerar cuentas válidas. El parche de OpenSSH hizo que, ante un usuario inválido, sshd tome la sal de otra cuenta cualquiera y cifre la contraseña igual, solo para gastar el mismo tiempo; el mensaje del commit decía “for invalid users”, pero terminó aplicándose también a cuentas reales con asterisco en el campo. En 2019, Alpine enfrentó el CVE-2019-5021: sus imágenes oficiales de Docker no traían contraseña de root. La primera corrección bloqueó la cuenta con el signo de exclamación, pero sin PAM ese símbolo también tumbaba el login por llave SSH, que es como se accede a casi todas las imágenes en la nube. En 2022, Alpine 3.16 volvió a usar asterisco para bloquear root (commit fbe926af), lo que restauró el acceso por llave.
Qué tan grave es y a quién afecta
El alcance es acotado: solo afecta a cuentas marcadas como bloqueadas y a builds de sshd sin PAM, no a los inicios de sesión normales con contraseña real. Aun así, Alpine es la base de una enorme cantidad de imágenes Docker, y muchas de ellas corren sin PAM por diseño, para mantenerse livianas. En la práctica, el riesgo depende de qué cuentas tengan ese asterisco y de si sshd las deja intentar login.
Muchas cuentas de servicio usan un shell /sbin/nologin que corta el acceso igual, y root suele tener PermitRootLogin en no. El caso más sensible es un contenedor Alpine con PAM deshabilitado donde alguien reactivó el login por contraseña para una cuenta pensada para quedar bloqueada. El bug no requiere adivinar ninguna contraseña real: alcanza con mandar cualquier cadena de más de 256 bytes contra una cuenta con asterisco en /etc/shadow.
Sin parche oficial, por ahora
La vulnerabilidad sigue documentada pero sin parche oficial confirmado: ni musl ni OpenSSH publicaron todavía un commit que la cierre. La revisión de seguridad de OpenSSH fechada el 30 de octubre de 2026 corrigió una función vecina (pick_salt()) sin tocar el bug real, que según sshhole.com está apenas cuatro líneas más abajo en el mismo archivo. Tampoco hay, por ahora, un identificador CVE asignado a este hallazgo puntual.
Los administradores de sistemas Alpine pueden mitigarlo revisando si PAM está activo, confirmando que las cuentas de servicio usen un shell sin login y verificando el estado de PermitRootLogin en su configuración de sshd. Vale la pena revisar el campo de contraseña de las cuentas de servicio en /etc/shadow y confirmar si el sshd corre con PAM antes de confiar en que un asterisco protege.

