The Alpine sshd Bypass Is Not a Version Check. It Is Four Conditions and a Restart.

2026-11-11来源 dev.toEnglish
The Alpine sshd Bypass Is Not a Version Check. It Is Four Conditions and a Restart.

Alpine sshd bypass exposure diagram: four conditions that must all hold on one host, with a version number shown as not deciding exposure

The Alpine sshd bypass is not a version problem. On November 5, Alpine Linux published an advisory for a password authentication bypass in its openssh-server package, and the OpenSSH project shipped 10.6p2 the same day with what its release notes call a "critical security workaround" for Linux systems that use musl libc with PAM disabled. Exposure depends on four conditions holding at once, and one of them is the state of the accounts on the machine, not a setting anyone chose. The fix has the same shape: a package on disk, a daemon in memory and an image in a registry are different states.

This post is scoped to what Alpine's advisory, the OpenSSH 10.6p2 release notes and the CVE record establish for Alpine hosts. The flaw is tracked as CVE-2026-123158, and CISA added it to its Known Exploited Vulnerabilities catalog on November 9. It does not claim the bypass reaches OpenSSH on other distributions, and it does not assess whether any particular host has been accessed.

The Alpine sshd bypass needs four conditions at once

The exposure lives in running state, which no template declares. Alpine lists four conditions. All four must be true on the same machine.

# Condition Where it comes from What removes it
1 PAM is not in the password path The openssh-server package is built without PAM; openssh-server-pam with UsePAM no is the same state openssh-server-pam with UsePAM yes
2 Password authentication is on PasswordAuthentication yes, the default PasswordAuthentication no
3 An account's password field is exactly * root (shipped as root:* since about 3.16), key-only users, musl-nscd directory users, the alpine user in official tiny-cloud images No such account, or accounts locked with !, which Alpine says are not affected
4 At least one local account has a $5$ or $6$ hash Any account with a real password No account on the machine with a hash

Root is protected by the default PermitRootLogin prohibit-password, but that does not help when another account that can log in can become root (Alpine illustrates this with doas). Containers that do not run sshd are outside the exposure. Alpine reports that every release it checked contains the affected code path: 3.21 through 3.24 and edge, plus the older 3.16 to 3.20 releases that no longer receive regular updates. That describes the packages, not every installation. The four conditions above must also hold on a given host.

Condition 3 is the key-only account, which is good practice. Condition 4 is a console password set once, or a user added through user-data. Neither is a mistake. The exposure appears when the second is added to the first, on a host whose configuration has not changed.

A read-only screen for conditions 2 to 4. The first two commands are this post's method, not Alpine's instruction; the third is from the advisory:

awk -F: '$2=="*" {print $1}' /etc/shadow awk -F: '$2 ~ /^\$(5|6)\$/ {print $1}' /etc/shadow sshd -T | grep -i passwordauthentication 

These are screening commands, not an exposure determination, and they have been run only against a mock shadow file, not validated on an Alpine host. The first cannot see musl-nscd accounts, so an empty result does not establish that condition 3 is false. An empty second result does mean condition 4 fails locally. The third does not evaluate Match blocks and reports configuration as sshd would load it now, not what a running daemon loaded. Condition 1 comes from the installed package and the UsePAM setting.

Alpine sshd bypass mechanism: the stored password field for a password-less account and the error value returned by crypt() are the same string, so the comparison matches

How an account with no password becomes a login

Without PAM, sshd hashes the attempt with crypt() and compares the result with the stored field. For a stored field too short to contain a salt, sshd borrows another account's salt, which is why condition 4 exists. musl's crypt() returns * as an error indicator for a key longer than 256 bytes, and * is also exactly what the stored field holds for an account with no password. The comparison sees two equal strings and the login succeeds.

Two unrelated meanings of * collide: "no password is set" in the shadow file, and "this call failed" in a return value. One account with a real password is enough, in Alpine's words, to make every account without one reachable. A field of ! is not equal to the error marker, so the comparison fails; Alpine states the unaffected result directly.

For a key-only account, the consequence is a login without a key. Where the account can reach root through doas, as the alpine user in the tiny-cloud images can, that is a root shell. Alpine offers a second route for hosts that cannot update yet: set PasswordAuthentication no in the effective configuration and restart sshd. Alpine says either route closes the exposure, and both end with a restart. An edit without a restart leaves the running daemon unchanged, and a restart without the edit leaves the exposure in place.

Package, daemon and image are not one fix

The first half of the fix is apk upgrade -U -a. The second is rc-service sshd restart, because upgrading the package does not replace the running daemon. A host that took the update and never restarted sshd runs the old code with the new package on disk.

Alpine sshd bypass fix paths: running host needs package upgrade and daemon restart, deployment path needs a rebuilt image, and a rebuilt image does not reach running instances

Path What closes the exposure What does not How you know
Running host Fixed package installed and sshd restarted The package upgrade alone The listener started after the package was upgraded (this post's method)
Running host, interim PasswordAuthentication no on every such line, Match blocks included, and sshd restarted, after confirming key login works Editing the file without a restart sshd -T run after the restart, as Alpine's steps end
Deployment path Images rebuilt from 3.21.10, 3.22.8, 3.23.8 and 3.24.4 or later A rebuilt image says nothing about instances already running Check each running instance

On the host, compare the sshd process start time with the time the package was upgraded; the listener has to be the younger of the two. sshd -T is evidence about the file, not about what a running daemon loaded.

On the deployment path, images from earlier point releases do not include the fixed package, and for every running instance the answer is to update openssh or disable password authentication. Alpine has published fixed packages back to 3.16, including 3.21, which reached the end of its support period on November 1. Stable branches carry the fix as patches to their existing OpenSSH versions, so the fixed package version differs from upstream 10.6p2: edge ships 10.6_p2-r0 and 3.24 ships 10.3_p1-r2, per the advisory's fixed-version table. Check a host against its own branch's version. For the interim route, use Alpine's published commands as written, confirm key login works first, and keep the current session open until a new login has been tested.

The same exists-versus-running separation shows up where a provider lifecycle setting makes the final move, in Managed Compute Patching Is Not Managed Remediation. Here the final action is a command on a host you operate, and the package manager does not schedule it.

Tiny-cloud images are safe at first boot and exposed after the first password

The official tiny-cloud images (qcow2, VHD, AMI) from 3.18.11 through 3.24.3 ship an alpine user with a * field and passwordless doas. Alpine describes them as safe on first boot. They become exploitable once a $6$ hash exists, for example when an administrator sets a console password or user-data adds a user with hashed_passwd, provided the other conditions hold. The image is not vulnerable as shipped, and the state change that makes it vulnerable is an ordinary provisioning action. Fixed images come from 3.21.10, 3.22.8, 3.23.8 and 3.24.4 onward. An instance launched from an earlier image keeps its sshd until someone updates it or turns password authentication off.

When a login cannot be ruled out, the verdict stops at exposure

Alpine's guidance: look in /var/log/messages for sshd lines reading Accepted password for an account that has no password, review doas use and recently created users, files and authorized keys, and treat the machine as compromised if you cannot rule out a login.

This post stops at the exposure verdict: not exposed because a condition fails, exposed and fixed because both fix states are verified, or exposed with a login history you can or cannot read. The architectural question is narrower: does the evidence to rule a login out still exist? A rotated or unshipped log cannot show an absence. When the records were never kept, the decision narrows to what you can rebuild and attest, which When You Cannot Prove Integrity, Rebuilding Becomes the Recovery Decision works through on a different Alpine matter. A shell as a key-only account with doas also leaves things behind, and The Server Was Fixed. Persistent Access Wasn't. covers why a rebuild does not remove access written somewhere it never reached.

What the sources leave open

Who owns the root cause is disputed. The OpenSSH release notes say the root cause is outside sshd while describing the change as one that avoids it in sshd. Rich Felker, writing to the musl mailing list on November 5, states his position that it is not a bug in musl but erroneous use by the caller, and that the mechanism is likely to change in the upcoming release cycle. The CVE record lists OpenSSH as the affected product, which names where the fix shipped and does not settle that dispute. This post takes no side. The exposure test and the fix paths are identical either way.

Scope beyond Alpine is not assessed. The release notes scope the workaround to Linux systems using musl without PAM. How that maps onto other musl-based distributions is outside what this post establishes.

An upstream affected-version lower bound is not established. Alpine reports the releases it checked; neither the release notes nor the CVE record, which says only "before 10.6p2", state where the exposure begins.

Exploitation is reported, not described. CISA lists this vulnerability in its catalog, which it populates on evidence of active exploitation. The listing does not say which hosts were affected, how many, or how the attacks were carried out, and this post makes no claim about whether any particular host has been accessed.

Alpine's advisory reflects the position on November 5. Re-read it before acting on a specific version.

Architect's Verdict

The Alpine sshd bypass is a state, not a version. Four conditions have to hold together, and the one that does the work is an account with no password, which is exactly what key-only hygiene produces. Alpine says accounts locked with ! are not affected, but a locked account cannot log in by key either, which is my analysis, not Alpine's. The fix is a state as well: a package on disk, a restarted daemon and a rebuilt image are three facts, and closing one does not close the others.

Version numbers, upgrade logs and image tags are signals that fire when an action ran. Exposure lives in two places they do not reach: the accounts that exist today, and the process listening today. An administrator who sets one console password can move a host across the line with no change to its configuration or version.

The operational answer is to ask the running host. Does a password-less account exist, does a hashed account exist, is password authentication on, and is the listener younger than the package.

A package manager reports what was installed. Only the running host can say what is listening.

Originally published at rack2cloud.com

原文:https://www.a8q9.cn/en/article/the-alpine-sshd-bypass-is-not-a-version-check-it-is-four-conditions-and-a-restar-bb834f.html

相关文章