# CVE-2026-123158 (*sshole): the OpenSSH password bypass on Alpine

> How a password over 256 bytes logs in to Alpine accounts whose password is disabled, which images and pods are exposed, and how to patch or mitigate.

Published: 2026-11-08
Author: k8wiz Team
URL: https://k8wiz.com/articles/cve-2026-123158-sshole-openssh-password-bypass-alpine

On November 5, 2026, OpenSSH released 10.6p2 and Alpine Linux shipped patched `openssh` packages for every branch the same day. The bug they fix, CVE-2026-123158 and nicknamed "\*sshole", lets someone log in over SSH with a password to an account whose password login is supposed to be disabled. Security vendors rate it CVSS 8.1 (High).

It only affects `sshd` on Linux systems built on musl libc without PAM, which in practice means Alpine. Alpine is also the base of a large share of container images, so it's worth knowing exactly when a pod is exposed. Most aren't, because most containers don't run `sshd`. The ones that do deserve a careful look.

## How the bypass works

The bug sits where OpenSSH's password check meets musl's `crypt()`. You need three facts to see it.

First, Alpine marks accounts that may log in with an SSH key but never with a password by putting a single `*` in the password field of `/etc/shadow`. Alpine's root filesystem build does this for root, with the comment "disable password login but allow login with ssh keys for root". This rootfs is what the official `alpine` container image is made from.

Second, when `sshd` checks a password, it hashes the password with the salt of the stored hash and compares the result with the stored value. A stored value of `*` is too short to hold a salt, so OpenSSH falls back to the salt of the first account on the system that has a real hash, such as a `$6$` (SHA-512) hash.

Third, musl's SHA-256 and SHA-512 `crypt()` refuse keys longer than 256 bytes. On any failure they don't return an error. They return the string `*`.

Put together: an attacker sends a password longer than 256 bytes for an account whose stored value is `*`. The hash fails, `crypt()` returns `*`, OpenSSH compares `*` with the stored `*`, and the login succeeds. OpenSSH caps passwords at 1,024 bytes, so anything between 257 and 1,024 bytes works.

The fix in 10.6p2 refuses to match any stored value shorter than four characters. This is the comparison in `auth-passwd.c` after the fix:

```c
ret = encrypted_password != NULL &&
    strlen(pw_password) >= MIN_PASSWORD_HASH_LEN &&
    strcmp(encrypted_password, pw_password) == 0;
```

`MIN_PASSWORD_HASH_LEN` is 4. A real crypt hash is far longer, so nothing legitimate is affected.

## Who is exposed

All of these must be true for an account to be at risk:

- The system uses musl libc and `sshd` runs without PAM. OpenSSH's default is `UsePAM no`, and Alpine's package doesn't use PAM.
- `sshd` accepts passwords. `PasswordAuthentication` defaults to `yes`.
- The target account's password field is exactly `*`. Accounts locked with `!` are not affected, because musl never returns `!`.
- At least one account on the system has a real SHA-256, SHA-512 or MD5 password hash. Without one, OpenSSH falls back to an old DES salt, which never fails this way.

Root is a special case. Out of the box `PermitRootLogin` is `prohibit-password`, which refuses password logins for root whatever the hash says. Root is only exposed where someone set `PermitRootLogin yes`, which is common in quick-and-dirty images. Any other account with `*` is exposed with the defaults.

Kevin Beaumont reported on Mastodon soon after the release that attackers were already trying long passwords against SSH servers with the usernames `root` and `alpine`. As of November 6, the CVE was not in CISA's Known Exploited Vulnerabilities catalog.

## Why Kubernetes clusters should check

Most Alpine-based pods run a single application process and no `sshd`, and they are not affected. The pods to look at are the ones that run an SSH server: jump hosts and bastion pods, SFTP and Git-over-SSH services, CI build agents that accept SSH, and debugging images someone left running. Nodes running Alpine itself, such as small k3s installs, matter too.

A pod is exposed to the outside when a Service publishes its SSH port, often with type `LoadBalancer` or `NodePort`. Inside the cluster, any compromised pod that can reach it is enough.

## Find the pods to check

Start with Services that publish port 22:

```bash
kubectl get svc -A -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"\t"}{.spec.type}{"\t"}{.spec.ports[*].port}{"\n"}{end}' | awk -F'\t' '$3 ~ /(^| )22( |$)/'
```

Then list which images run in the cluster, to spot Alpine-based ones that ship an SSH server:

```bash
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{range .spec.containers[*]}{.image}{" "}{end}{"\n"}{end}'
```

The image name alone doesn't tell you the base image. For each candidate, look inside the container. Replace `NS` and `POD` with the pod you're checking:

```bash
kubectl exec -n NS POD -- cat /etc/alpine-release
```

```bash
kubectl exec -n NS POD -- apk info -v openssh-server
```

The first command fails on anything that isn't Alpine. The second prints the installed package and version, or nothing if there's no SSH server.

## Check whether an account can be reached

If the pod runs Alpine and `openssh-server`, see how `sshd` is configured. `sshd -T` prints the configuration it actually uses, after includes and defaults:

```bash
kubectl exec -n NS POD -- sh -c 'sshd -T | grep -Ei "^(passwordauthentication|permitrootlogin|usepam) "'
```

Then list the accounts whose password field is exactly `*`, and the accounts with a real hash. This reads `/etc/shadow`, so the container must run as root:

```bash
kubectl exec -n NS POD -- awk -F: '$2 == "*" { print "star:", $1 } $2 ~ /^\$/ { print "hash:", $1 }' /etc/shadow
```

The pod is vulnerable if password authentication is on, there is at least one `hash:` line, and there is a `star:` account that can log in. Root counts only when `permitrootlogin` is `yes`.

## Patch: the fixed Alpine packages

Alpine backported the fix to every branch instead of moving them to 10.6p2. These are the first fixed versions, from the `secfixes` records in Alpine's package definitions:

| Alpine branch | Fixed `openssh` version |
| --- | --- |
| edge | 10.6_p2-r0 |
| 3.23 | 10.2_p1-r1 |
| 3.22 | 10.0_p1-r11 |
| 3.21 | 9.9_p2-r1 |
| 3.20 | 9.7_p1-r6 |
| 3.19 | 9.6_p1-r3 |
| 3.18 | 9.3_p2-r4 |
| 3.17 | 9.1_p1-r7 |
| 3.16 | 9.0_p1-r6 |

On a running Alpine host or VM, upgrade the package and restart `sshd`:

```bash
apk upgrade --no-cache openssh-server && rc-service sshd restart
```

Containers should not be patched in place. Rebuild the image so the fix survives the next restart, and make sure the build doesn't reuse an old base image or cached layers. Add an upgrade step to the Dockerfile:

```dockerfile
FROM alpine:3.22
RUN apk upgrade --no-cache && apk add --no-cache openssh-server
```

Build with `--pull` and `--no-cache` so you get the current base image and fresh packages:

```bash
docker build --pull --no-cache -t registry.example.com/bastion:2026-11-08 .
```

Push the new tag, point the workload at it and watch the rollout:

```bash
kubectl set image deployment/bastion -n ops sshd=registry.example.com/bastion:2026-11-08
```

```bash
kubectl rollout status deployment/bastion -n ops
```

Remember images built before November 5 that live elsewhere: copies in internal registries, cached CI layers and mirrored package repositories all still hold the old package until they are rebuilt or refreshed.

## Mitigate when you can't patch yet

The bug only matters when `sshd` accepts passwords. If your users log in with keys, which they should, turn passwords off. Alpine's `sshd_config` reads drop-in files from `/etc/ssh/sshd_config.d/`, so you can add one without editing the main file:

```bash
printf 'PasswordAuthentication no\n' > /etc/ssh/sshd_config.d/10-no-passwords.conf
```

Check the configuration and reload `sshd`:

```bash
sshd -t && rc-service sshd reload
```

In a container, bake the same file into the image or mount it from a ConfigMap, then restart the pod. `sshd -T` should now show `passwordauthentication no`.

One tempting shortcut is to replace `*` with `!` in `/etc/shadow`. Don't do it for accounts that log in with keys. On Linux without PAM, OpenSSH treats a password field starting with `!` as a locked account and refuses every login, keys included. That is exactly why Alpine uses `*` for root.

## Why your scanner may disagree

Because Alpine backported the fix, a patched Alpine 3.22 system still reports OpenSSH 10.0. A scanner that compares the upstream version with 10.6p2 will flag it as vulnerable. Scanners that read Alpine's security database match on the Alpine package version (`10.0_p1-r11` and later) and get it right. If a report and the table above disagree, check the installed package with `apk info -v openssh-server`.

The reverse matters too. A container that a scanner checked weeks ago and marked clean was clean against the data it had then. Re-scan images after November 5, and rebuild anything built before it.

## Sources

- OpenSSH portable, [`auth-passwd.c`](https://github.com/openssh/openssh-portable/blob/master/auth-passwd.c) (the fixed comparison and `MAX_PASSWORD_LEN`) and the [`sshd_config(5)` defaults](https://github.com/openssh/openssh-portable/blob/master/sshd_config.5)
- musl, [`crypt_sha512.c`](https://git.musl-libc.org/cgit/musl/tree/src/crypt/crypt_sha512.c) (the 256-byte key limit and the `*` failure value)
- Alpine aports, the [`openssh` package definition](https://gitlab.alpinelinux.org/alpine/aports/-/blob/master/main/openssh/APKBUILD) on each branch (fixed versions) and [`scripts/genrootfs.sh`](https://gitlab.alpinelinux.org/alpine/aports/-/blob/master/scripts/genrootfs.sh) (root set to `*`)
- StepSecurity, [CVE-2026-123158 (\*sshole)](https://www.stepsecurity.io/blog/cve-2026-123158-sshhole-openssh-musl-password-bypass), and BleepingComputer, [OpenSSH flaw lets attackers log into password-disabled Linux accounts](https://www.bleepingcomputer.com/news/security/openssh-flaw-lets-attackers-log-into-password-disabled-linux-accounts/) (CVSS score, release date, exploitation status)
- Kevin Beaumont on [Mastodon](https://cyberplace.social/@GossiTheDog/117563742240898800) (reports of attempts against `root` and `alpine`)
