← Back to BLACKWIRE GHOST BUREAU UNIX SECURITY Terminal showing /etc/shadow with asterisk entries on a Linux server

The asterisk in /etc/shadow, once a harmless lockout marker, now flags a potential authentication bypass on musl‑based systems.

SSH AUTHENTICATION BUG IN MUSL LIBC ALLOWS UNAUTHORIZED LOGINS

*A decades‑old asterisk in /etc/shadow can bypass SSH authentication on musl‑based systems. The flaw survived ten years of scrutiny, resurfacing in Alpine and similar images.*

By GHOST Bureau - BLACKWIRE  |  November 5, 2026, 21:00 CET  |  SSH, musl, authentication bug, /etc/shadow, Alpine Linux

From the dawn of Unix in 1980, password hashes lived in the world‑readable /etc/passwd file. Bill Joy marked the daemon account with a lone asterisk (*)—a placeholder that matches no password. Decades later the hash migrated to /etc/shadow, adopting the $6$sha512 format, but the * sentinel persisted as a lockout flag. Modern libc implementations rely on the crypt() function to turn a password into a hash. When crypt() cannot compute a hash, the POSIX spec says it must return NULL. Many authentication tools failed to test for NULL and crashed. In 2012 musl libc rewrote crypt(): it stopped returning NULL and instead emitted a single character, typically “*”, whenever the operation failed. The change was meant to avoid crashes, but it also gave sshd a value that could be mistaken for a legitimate hash under certain configurations.

Why the asterisk matters in Unix password files

In 1980 Unix stored password hashes in /etc/passwd, a file readable by every user. Bill Joy inserted a * for the daemon account, a value that is not a hash and therefore cannot match any password. The convention survived the 1990s move to /etc/shadow, where hashes appear as $6$abc$… with $6$ indicating SHA‑512. A field that contains only * still signals “no password”. Any password typed against such an entry is rejected because there is nothing to match. The asterisk remains in modern Unix systems as a lockout marker, but its presence also creates a corner case for libraries that mishandle failed hash calculations.

Did musl’s 2012 change introduce a silent authentication flaw?

Musl’s 2012 commit (b3c4cc12) altered crypt() to stop returning NULL. Instead, on failure it returns a single character that can never match a real password—typically “*”. The same year musl added SHA‑512 crypt, but capped input at 256 bytes (commit 88bf5a8a). Passwords longer than 256 bytes cause crypt() to fail and emit “*”. Because sshd and other programs compare the returned value directly with the stored field, a “*” from a failed crypt can be interpreted as a legitimate lockout marker, allowing the authentication routine to proceed without proper verification. The change eliminated crashes but opened a path for unauthorized logins on systems that rely on musl’s crypt implementation.

A single ‘*’ in /etc/shadow can turn a hardened server into an open door.

Why the bug only appears on musl without PAM

OpenSSH’s test suite runs on OpenBSD, glibc and musl, but the musl branch is rarely exercised. Most Linux distributions route SSH authentication through PAM, which never calls crypt() for a * entry. Consequently the musl bug stayed hidden. Debian developers once asked glibc to copy musl’s “*” behavior; six years later Debian switched to libxcrypt, which returns “*0” and closed the issue. A December 2024 musl mailing‑list thread reported busybox passwd breaking on a 257‑character password, again returning “*”. The only environments that combine musl, no PAM, and SSH password logins are Alpine, OpenWrt, Buildroot and Yocto images—exactly the platforms where the flaw has been observed.

How to verify and mitigate the vulnerability

Operators should first identify the libc in use. On Alpine or other musl‑based images, inspect /etc/shadow for a solitary * in a user’s password field. Ensure UsePAM is enabled; disabling PAM (UsePAM no) leaves the crypt() path exposed. Test by attempting a login with an over‑long password (>256 bytes) or random data; a successful login indicates the bug. Upgrade musl to a version that restores proper NULL handling or patches the return value. Alternatively switch to glibc or libxcrypt, which return “*0” for failures. Alpine’s 2022 3.16 update locked root with * while preserving SSH keys, demonstrating a safe mitigation. Apply CVE‑2016‑6210 and CVE‑2019‑5021 patches, and monitor future OpenSSH commits that address pick_salt() logic.

Frequently asked questions

Why does a * in /etc/shadow allow any password on musl systems?

Musl’s 2012 crypt() change returns a single character, usually *, when hashing fails; sshd compares this directly with the stored field and may treat it as a valid entry, letting any password succeed.

Which Linux distributions are vulnerable to this musl * bug?

Distributions that use musl libc without PAM, such as Alpine, OpenWrt, Buildroot and Yocto, can be affected because they bypass the PAM path that would otherwise avoid the crypt() call.

What did Alpine do in 2022 to mitigate the issue?

Alpine 3.16 switched the root account’s password field to * in /etc/shadow, keeping the account locked while allowing SSH keys to function, thereby avoiding the faulty crypt() return value.

The musl asterisk bug survived a decade because the typical PAM stack masked its effect. As more containers and edge devices adopt musl without PAM, the risk expands. Administrators must audit their libc, enforce PAM, and apply the latest musl patches. Failure to do so leaves a silent backdoor that can be triggered with a malformed password—exactly the scenario the original * was meant to prevent.

Sources: Hacker News post "sshole" (https://sshhole.com)