Published
- 15 min read
The Alpine OpenSSH Fix Needs a Restart Receipt
Books by the author
Compare all 5As an Amazon Associate I earn from qualifying purchases. Buying through these links costs you nothing extra and helps pay for the blog.
A password between 257 and 1,024 bytes could open certain accounts on an affected Alpine Linux SSH server. An attacker who knew the username and could reach the SSH port could use the bypass only when password authentication was enabled, password checks ran without PAM, the target account stored *, and another local account had a $5$ or $6$ password hash.
OpenSSH released 10.6p2 on November 5, 2026. Its release notes call the change a “critical security workaround” for Linux systems using musl libc with PAM disabled. Alpine published fixed packages for supported branches and older branches back to 3.16. The immediate instruction is short: update OpenSSH and restart sshd.
That last word carries most of the operational lesson. A package file on disk can be fixed while the vulnerable daemon keeps accepting connections from memory. Alpine says this without ceremony: “Upgrading the package alone does not replace the running daemon.” A green package report proves that the fixed files were downloaded and installed while leaving the state of the exposed daemon unresolved.
The failure crossed three representations. The account database stored * to mean “this account has no password.” Musl’s crypt() returned the same * as an error marker, and OpenSSH accepted equality between the stored field and that result. That sequence turned a library error into an accepted login.
What shipped on November 5
OpenSSH 10.6p2 contains the upstream workaround. The project describes the affected environment narrowly: Linux, musl libc, and password checks performed with PAM disabled. Alpine’s standard openssh-server package fits that shape because it is built without PAM. OpenSSH enables password authentication by default unless an administrator changes the setting.
The bypass still needs four conditions at the same time. Password authentication must be enabled. The server must check passwords without PAM. The target account’s password field must contain *, a common state for an account intended to use keys rather than a password. At least one local account must carry a $5$ or $6$ password hash. Alpine’s advisory lists all four conditions and distinguishes them from configurations that are safe.
When all four conditions align, an attacker who knows the affected account name and can reach the SSH port can send a password between 257 and 1,024 bytes. Alpine reports that one connection is enough. A long input makes the password-hashing function return *; OpenSSH then compares that result with the target account’s stored * and accepts equality without proof of a password.
Many Alpine systems fall outside that path. A container with dormant sshd has no SSH exposure merely because its base image contains musl, while PasswordAuthentication no blocks this route on a running server. An openssh-server-pam installation with UsePAM yes follows a different password-checking path. An account locked with ! in /etc/shadow also fails the advisory’s required * condition. Use those distinctions to decide whether the package name identifies an exposed service.
Alpine shipped corrected package versions for branches 3.16 through 3.24 and edge. That includes branches old enough to sit outside regular support, which is useful for emergency containment but should not turn an unsupported operating system into a long-term plan. For current branches, Alpine names fixed point releases including 3.21.10, 3.22.8, 3.23.8, and 3.24.4. The exact package version differs by branch, so the distribution advisory is the right lookup table.
No CVE identifier had been assigned when Alpine published its November 5 advisory. Remediation can proceed from the documented code and configuration conditions while scanners and ticket workflows wait for the identifier they use.
How an error string became a password
The mechanism is compact enough to hold in your head. Without PAM, sshd checks a submitted password through crypt(), the C library function used to derive the stored form. It then compares the derived result with the password field associated with the account. Normal authentication expects a submitted secret to produce the same stored value as the real secret.
An account reserved for key authentication may have * in its password field. That value is too short to contain the salt needed by the normal hashing operation, so the checking path borrows a salt from another account. The affected arrangement requires at least one account whose password hash begins with $5$ or $6$, giving the code the kind of salt that reaches the failure.
Musl’s crypt() rejects an input longer than 256 bytes for those hash types and returns * as its error indicator. The target account also stores *. The caller sees two equal strings:
stored password field: *
crypt() error result: *
comparison result: equal
For the affected hash types, any input longer than 256 bytes triggers musl’s * error marker. OpenSSH accepts at most 1,024 bytes of password input, which bounds the reported attack range at 257 to 1,024 bytes.
This is a contract failure at a boundary. Musl returned a value intended to be unmatchable. That same * already represented a different state in the account database. OpenSSH then compared the library output as a candidate hash before separating an error from a successful result. A sentinel crossed layers and changed meaning on the way.
OpenSSH calls its change a workaround because it considers the root cause to sit outside sshd. That wording is technically useful and operationally secondary. The network service was the route through which the mismatch became a login, so the service now rejects the dangerous comparison. Stable Alpine packages carry the corresponding fix for their OpenSSH versions.
The useful engineering rule sits in the failed comparison. A caller must determine whether a dependency returned valid data before that value reaches an authorization check. Here, * could be both stored account data and musl’s failure result, so OpenSSH needed to preserve the distinction before comparing the strings. Tests for this boundary should force the library error and verify that the login is denied.
Which servers deserve the first call
Reachability sets the first priority. Start with running SSH services on Alpine or other relevant musl-based hosts that accept traffic from the internet, partner networks, or a broad internal segment. For each live route, identify the OpenSSH package or build path and read the effective PasswordAuthentication and UsePAM settings. Then inspect account state to distinguish * from ! and determine whether any local account carries the $5$ or $6$ hash needed by the failing path. Alpine images with a dormant package can wait.
Read the daemon’s resolved configuration rather than trusting a fragment in a configuration-management repository. Included files and Match blocks can override a line in the main sshd_config for a user, address, or group. Alpine’s mitigation uses sshd -T; its final output should contain passwordauthentication no. That result carries more weight than a text search for one reassuring line.
Cloud images need the same discipline. Alpine says its affected tiny-cloud images are safe on first boot, then describes a configuration change that can make them exploitable. The image contains an alpine user with a * password field and passwordless doas; adding a local $6$ hash can complete the conditions. A console password set during recovery may therefore change the security state of SSH without anyone editing sshd_config.
Key-only service accounts deserve attention because their stored * marker supplies one condition of the vulnerable equality. Exposure still requires password authentication, non-PAM checks, a reachable service, and the $5$ or $6$ hash elsewhere on the host. Verify that executed path for each account rather than inferring it from the key-only intention.
The safest prioritisation uses two axes. Reachability tells you how soon an attacker could try. Privilege tells you what a successful login buys. An internet-facing account with passwordless doas belongs at the front. A management host reachable only through a tightly controlled jump path still needs the fix, but its containment options and review window differ.
Independent reporting from The Hacker News confirms the same affected conditions, the long-password range, and Alpine’s update coverage. It also reports that no attacks using the flaw had been disclosed as of November 5. That dated disclosure status leaves the contents of your November 6 logs as a separate question.
Prove the running daemon changed
Alpine’s package upgrade leaves sshd running. The process already listening on port 22 can therefore continue to execute the old code from memory. Package scanners will correctly see the fixed artifact even while connections still reach the vulnerable daemon.
Completion requires three observed steps after installation. First, run sshd -t so the current configuration is known to parse before disruption. Restart the service and capture the command result together with a new process identity or start time. Finally, open a fresh SSH connection through the approved key path; that connection checks both the replacement daemon and the route operators actually use.
Keep those facts in one restart receipt: Alpine branch, installed package version, configuration-validation result, restart time and result, the new process identity or start time, and the fresh connection outcome. A failed restart can cause an outage or leave the old daemon serving, so an apk success by itself closes only the installation step.
When sshd runs in a container, rebuild from the fixed package and replace the running container. A newly built image does not alter existing tasks, and changing a stopped image tag leaves the current process intact. Containers without sshd should be documented and removed from the urgent service queue.
Immutable hosts require the same cutover evidence. Replace or patch instances that use the previous image according to the operating model, then test the route used by people and automation. The corrected image remains a candidate until those running instances have changed.
Patch without locking yourself out
An emergency SSH change is a poor time to discover that the only working key lives on a retired laptop. Alpine’s mitigation tells administrators to keep the current session open, confirm key authentication, and test a new login before closing the original connection. Follow that sequence. The old session is your recovery path while you change the service that provides remote access.
If you can update, use the fixed package for the host’s branch and restart sshd. Validate the configuration before the restart. Watch the command return. Then open a second connection through the same network path your operators use. A localhost test can prove the daemon responds while missing a firewall, jump host, or key-agent problem on the real route.
If the package update must wait, disabling password authentication closes this bypass. Set PasswordAuthentication no, account for included files and Match blocks, validate with sshd -t, restart the daemon, and inspect sshd -T. The effective output should show passwordauthentication no. Keep the original session until a new key-based login succeeds.
That mitigation blocks this password-authentication route after the daemon restarts while leaving the vulnerable package installed. A later configuration change could re-enable password logins and restore the exposure. Track the package update as unfinished work rather than treating the setting as permanent closure.
A useful runbook for each exposed host is short enough to execute under pressure:
- Preserve access. Keep the current SSH session open and confirm that the intended private key is available.
- Capture the before state. Record the Alpine release, installed OpenSSH package, effective authentication settings, and current
sshdprocess start time. - Install the branch-specific fix. Use Alpine’s advisory to identify the corrected package for that release.
- Validate before restart. Run
sshd -tso a syntax error does not turn a security change into a remote lockout. - Restart the daemon. Use the service manager and check that the command succeeds.
- Prove the after state. Confirm the new process start time, inspect effective settings with
sshd -T, and open a new key-based session through the normal route. - Investigate when exposure cannot be bounded. Review authentication and privilege activity before declaring the host clean.
Keep the runbook in this order to protect access while proving the change. Confirming key access first preserves the recovery path, and sshd -t catches a configuration failure before restart. After the restart, a fresh session through the normal route demonstrates that the replacement daemon and the real access path work. Record its result with the new process identity or start time so another engineer can verify that the host stopped running the affected path.
Decide whether this is a patch or an incident
The fixed package protects future authentication attempts. Past access requires a separate review. Alpine advises looking in /var/log/messages for sshd lines that say Accepted password for an account that has no password, including alpine, root, or a key-only user. Begin there, while recognizing that this search alone cannot rule out compromise.
For an exposed machine, compare the possible attack window with the log retention you actually have. Review successful password logins to accounts expected to use keys. Check doas activity where an affected account could gain more privilege. Examine newly created users, modified authorized keys, and files changed in places that the account could reach. The advisory’s position is appropriately firm: if you cannot rule out a login, treat the machine as compromised.
That decision should depend on evidence and consequence. A host that satisfied all four conditions, exposed port 22 to the internet, and retained two hours of logs has an unbounded exposure window even when the available search is empty. Treat it as compromised: rebuild from a known source, rotate credentials reachable from the host, and inspect adjacent systems according to the account’s privileges.
Evidence that a host never enabled password authentication closes this specific bypass finding for that host. Record how the effective setting was obtained and the history supporting it. That conclusion covers the documented password-authentication route rather than the host’s broader security state.
The middle case is common: configuration appears safe now, but nobody can prove when it changed. Use configuration history, image manifests, process start records, and central authentication logs to bound the interval. Favor containment when that interval remains open and the host held valuable credentials.
Avoid turning the investigation into a generic hunt for anything suspicious. Start from the mechanism. The attacker needs a reachable SSH service and a username for an account storing *. A successful attempt should appear as password authentication to an account that operators expected to accept keys. Privilege use and persistence checks follow from what that account could do.
The patch ticket must establish whether vulnerable behavior is still running. In parallel, the incident ticket must determine whether someone may have used the bypass before remediation. Keep both questions open until each has its own evidence.
What this changes in engineering practice
Connect every exposed daemon to the evidence needed to change it safely. The record should identify the host or workload and its network route, then link the supplying artifact to the effective configuration and process currently serving traffic. Add the restart time, health result, and responsible owner. Those fields can begin as a deployment record while a broader asset-management system develops; the immediate goal is to keep a package result tied to the service it is meant to repair.
Make restarts explicit in patch procedures. Some package upgrades reload or restart services; others deliberately avoid it to prevent surprise outages. Automation should know which behavior applies and report it. A successful installation followed by a failed service start is a failed change, even if the package manager exits zero.
Test the negative security property after deployment. In this case, sshd -T can show that password authentication is disabled, while a fresh key-based connection proves the intended route remains usable. For other services, the check might confirm that a revoked token fails, a closed endpoint returns the expected status, or a worker reports the new build identifier. Pick the behavior the patch was meant to alter.
Keep dependency failures distinguishable from valid authorization data all the way to the caller. The specific representation matters less than the enforced branch: callers must check failure status before comparing returned data. Force those error paths in tests and assert that access is denied.
Review composed defaults during threat modelling. Alpine’s package choice, OpenSSH’s password-authentication default, account markers, and musl’s error return each have local reasoning behind them. Their composition created the bypass. A deployment review that asks only whether each component uses its documented default will miss the system behavior.
The deployment-security chapter in The AI Native Engineer: Stay relevant as a Software Engineer connects shipping to observed production behavior. This incident supplies a precise production check because installed package state and running process state can diverge. Close the fix with a restart receipt. It must identify the replacement process and record a successful fresh connection.
Close with evidence from the service
Close each exposed service against a named host or workload and its Alpine release. Record the fixed OpenSSH package version, followed by the configuration-validation result and the restart result. Name the replacement daemon. Use its new process identity or start time, and attach the successful fresh connection through the approved route. Keep the original SSH session open until that connection succeeds. Together, those entries show what changed on disk and what now serves traffic; for an exposed service, the same record also needs a log-review window.
The bypass accepts a password from 257 to 1,024 bytes only when password authentication is enabled and password checks run without PAM. It also requires the target account to store * while another local account has a $5$ or $6$ hash. Cover the period when those four conditions may have aligned. The resulting evidence determines whether closure stops at remediation or continues as incident response.
Carry unresolved work into the fleet report as named exceptions. A host or workload remains open if the package could not update or configuration validation failed. It also remains open when sshd did not restart, the new process could not be identified, or a fresh connection failed. An unbounded review window belongs in the same report. Assign every exception an owner and a follow-up time.
For one practical security email each month, join the newsletter on this site.
Sources
- OpenSSH: Release Notes for OpenSSH 10.6p2, accessed 2026-11-06
- Alpine Linux: Security advisory, openssh-server password authentication bypass, accessed 2026-11-06
- The Hacker News: OpenSSH Issues Emergency Fix for Flaw Letting Long Passwords Bypass SSH Auth on Alpine Linux, accessed 2026-11-06