Skip to content
sshhole

sshhole

Sploitus • November 5, 2026

[![Python 3.8+](

[![License: MIT](

> **DISCLAIMER & LIABILITY NOTICE:** This tool and proof-of-concept are strictly for educational research and authorized security assessments. The user assumes all risk and legal responsibility for its use or misuse. The authors disclaim all liability for damages or unauthorized operations.

The **\*sshole** vulnerability allows unauthenticated remote attackers to bypass password authentication on OpenSSH servers compiled against **musl libc** (such as Alpine Linux, OpenWrt, Buildroot, and minimal containers) under non-PAM configurations.

In musl libc, the SHA-256 (`$5$`) and SHA-512 (`$6$`) implementations of `crypt()` enforce a 256-byte input cap. Passwords exceeding 256 bytes cause `crypt()` to return a literal asterisk (`"*"`), rather than `NULL`.

2. **OpenSSH Salt Borrowing (`pick_salt`):**

When OpenSSH runs without PAM (`UsePAM no`), it relies directly on `crypt()`. For accounts without a set password (`/etc/shadow` entry is `*`, typical of locked, service, or SSH-key-only accounts), OpenSSH borrows a valid `$5$` or `$6$` salt from another system account to prevent timing-based user enumeration (CVE-2016-6210).

encrypted_password = xcrypt(password, salt);

return encrypted_password != NULL && strcmp(encrypted_password, pw_password) == 0;

When an attacker submits a password longer than 256 characters (e.g. 300 bytes of `'A'`), musl `crypt()` returns `*`. Because the target's shadow record `pw_password` is also `*`, the comparison succeeds (`strcmp("*", "*") == 0`), granting immediate authenticated access.

The OpenSSH preauth monitor initializes the salt cache during its first authentication cycle. An initial probe attempt within the same connection primes `pick_salt()`, allowing the subsequent overflow payload to match.

All conditions must be present simultaneously:

- **musl C library** (Alpine Linux, OpenWrt, Void musl, etc.).

- **OpenSSH compiled/configured without PAM** (`UsePAM no`).

- **Password authentication enabled** (`PasswordAuthentication yes`).

- **Target user has `*` in `/etc/shadow`** (e.g. `alpine`, `ctf`, `deploy`, or `root` if `PermitRootLogin yes`).

- **At least one other account exists** with a valid `$5$` or `$6$` hash in `/etc/shadow`.

## Operational Note: OpenSSH 9.8+ `PerSourcePenalties`

OpenSSH 9.8+ includes `PerSourcePenalties` active by default. Rapid consecutive failed authentications from the same source IP trigger an automatic penalty window (15–30 seconds), causing sshd to drop new connections immediately:

srclimit_penalise: activating ipv4 penalty of 16.502 seconds for penalty: failed authentication

drop connection from [IP] on [PORT] penalty: failed authentication

> In CTF and red-team engagements, prioritize targeting known usernames directly (`-u ctf` or `-u alpine`) rather than rapid unthrottled spraying, to avoid triggering source IP penalties.

Run the vulnerable target container locally:

docker run -d --name sshhole-lab -p 2222:22 ghcr.io/leonjza/sshhole-lab:latest

python3 poc.py --host 127.0.0.1 -p 2222 -u alpine -c "id; uname -a"

This project is licensed under the [MIT License](LICENSE).

Extracted Entities

CVEs (1)

Domains (1)

Vulnerabilities (1)