# CVE-2026-123158 (OpenSSH / musl password authentication bypass) detection and verification script
Regarding **CVE-2026-123158** (referred to as “sshole” by the community)—a password authentication bypass vulnerability in **OpenSSH + musl**: on musl systems lacking PAM, accounts with a password field set to `*` (i.e., no password set) can be logged in using passwords of any length between 257 and 1024 bytes. > **ID**: CVE-2026-123158 (CVSS 3.1 **8.1**); OpenSSH has patched this issue in **10.6p2** (released on 2026-11-05) as a “critical security bypass”. This repository provides a Python script for:
1. **Local inspection**: checking whether a machine falls into the affected category based on four predefined conditions (read from `/etc/shadow`);
2. **Remote verification**: performing a long password SSH authentication attempt on a specified account to determine if it can be bypassed (requires `paramiko`). ---
In one sentence: **Two “never-match” sentinel values collided during a single string comparison forty years later.**
| **1980** | Unix used `*` to indicate “no password set, never logged in” for the `daemon` account (as per Bill Joy’s 3BSD documentation); stored in `/etc/passwd`. |
| 1997 | POSIX mandates that `crypt()` returns **NULL** upon failure; however, many programs fail to check for this value. |
| **2012-08-09** | musl changed its behavior so that failures return **`*`** instead of triggering a crash—this matches the sentinel value from 1980. |
| **2012-08-29** | musl added a 256-byte limit to `sha512crypt` (to prevent DoS attacks); excessively long passwords → `crypt()` failure → return of **`*`**. |
| **2016-07** | OpenSSH fixed CVE-2016-6210 (username enumeration), allowing sshd to use the salt of another account for hashing when no real hash exists. |
| 2019 → 2022 | Alpine’s root value changed from `!` to `*` (`!`). | ` Login attempts will be blocked together with SSH key authentication) |
| **2026-11** | Two sentinels meet: the returned `"*"` is compared to the stored `"*"` and they match → **authentication successful** |
① An attacker tries to log into account X using an excessively long password (257–1024 bytes); the shadow field of X is `*`
② sshd (without PAM) calls sys_auth_passwd(); the retrieved pw_password is `*`
③ `"*"` cannot be used as a salt → sshd borrows the `$5$/$6$` salt from another account (a change introduced in 2016)
⑤ For passwords longer than 256 bytes → rejection → returns `"*"`
⑥ sshd compares: crypt returns `"*"` equals the stored field `"*"` → same → login succeeds
The fix (10.6p2, commit `90f9c2f7`) simply adds one line before the comparison:
strlen(pw_password) >= MIN_PASSWORD_HASH_LEN && /* MIN_PASSWORD_HASH_LEN = 4 */
strcmp(encrypted_password, pw_password) == 0;
When the stored field contains only 1 byte (`*`), it is immediately deemed non-matching. ---
1. Running **`openssh-server`** (without PAM during compilation), or `openssh-server-pam` with `UsePAM no`;
2. `PasswordAuthentication yes` (default in OpenSSH);
3. There exists an account whose password field is **`*`** on the machine;
4. At least one other account must contain a hash of type **`$5$`** or `$6$`. **Unaffected**: non-Linux systems, glibc-based systems, sshd running PAM, accounts locked via `!`, machines without `$`-style hashes, and containers that do not run sshd.
On Alpine, the `*` account includes: a passwordless key user named `adduser`, users created via `usermod -p '*'`, directory service users handled by musl-nscd, and the **root** account, which has been published as `root:*` since version 3.16 (protected by the default `PermitRootLogin prohibit-password` setting; this protection is disabled unless set to `yes`; however, if the account can be elevated via `doas`, the protection is lifted). ---
# 1) Local test (requires root to read /etc/shadow)
# 2) Remote verification (requires paramiko)
python3 ssh_star_bypass.py -t 10.0.0.5 -u alpine
python3 ssh_star_bypass.py -t 10.0.0.5 -u alpine -l 512 -p 22
Parameters: `-t/--target`, `-p/--port`, `-u/--user`, `-l/--length` (257–1024, default 300), `--timeout`. In remote mode, a **check** is performed first using a 16-byte random password (expected to be rejected); then a longer password is sent. This helps distinguish between cases where a vulnerability exists versus where the account already has a password. Exit codes: `0` = present/exploitable; `1` = no match; `2` = cannot determine (missing dependencies, permissions, or network access). ---
- Only **musl builds without PAM** trigger this; running on systems with glibc/PAM fails (this is one of the criteria used for detection). - A long password will cause `crypt()` to fail directly; therefore, there is no need to know the correct password or perform brute-force attacks.
- It can be successfully authenticated with a single connection and one attempt (not a probability hole).
- This script performs only **one** authentication attempt; it does not iterate through accounts or perform brute-force attacks, making it suitable for minimizing verification in authorized environments.
apk upgrade -U -a # Available for Alpine 3.21.10, 3.22.8, 3.23.8, and 3.24.4; includes fixes
rc-service sshd restart # Upgrading does not restart running sshd; must manually restart it
For self-compilation: upgrade to **OpenSSH 10.6p2**. If upgrading is not possible: set `PasswordAuthentication no` and restart sshd (first ensure the key works and that the current session remains active).
# Check if any “accounts without a password” have been logged in via password login
grep -E "Accepted password for" /var/log/messages
Pay special attention to `alpine`, `root`, and accounts dedicated to key management; also review records of `doas` usage, recently created users, and any changes to files or `authorized_keys`. Official recommendation: “If it’s impossible to rule out unauthorized logins, treat this as a compromised system.”
This tool is intended solely for security research and defensive testing within **your own systems or under explicit authorization**. Do not use it on unauthenticated systems; it generates actual authentication failure/success logs on target systems. Mechanism details are based on public analyses by researcher Özgün Kültekin (Wakefield), Alpine security advisories, and OpenSSH 10.6p2 release notes.
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
