Skip to content
Exploit for CVE-2026

Exploit for CVE-2026

Sploitus • November 6, 2026

# 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.

Extracted Entities