regreSSHion — OpenSSH (CVE-2024-6387)
TL;DR: A race in
sshd’sSIGALRMhandler calls async-signal-unsafe glibc functions, letting a remote attacker corrupt heap state and execute code as root with no credentials.
What it is
Qualys disclosed this in July 2024. It is a regression of CVE-2006-5051: a 2020 refactor reintroduced async-signal-unsafe calls inside sshd’s SIGALRM handler, which fires when a client fails to authenticate within LoginGraceTime (default 120s). The handler calls sigdie(), which lands in glibc’s syslog() path, which is not safe to invoke from a signal handler — it can hold malloc arena locks, fiddle with the FILE buffer for /dev/log, and call localtime_r. By precisely timing the connection to be interrupted while the main sshd thread is mid-malloc, an attacker corrupts the heap of a privileged child process and walks it to RCE as root.
Preconditions / where it applies
- OpenSSH 8.5p1 through 9.7p1 with the 2020 regression (commit 752250c) on glibc-based Linux
LoginGraceTimenon-zero (the default) so the alarm fires- Network reachability to TCP/22 (or whatever port sshd binds)
- Originally demonstrated against 32-bit glibc; the 64-bit case is exploitable but takes much longer (Qualys: ~6–8h on 32-bit, weeks on 64-bit)
- Not affected: OpenBSD (different sig handling), musl-libc systems, OpenSSH ≤ 4.4p1 or ≥ 9.8p1
Technique
The attacker opens many parallel SSH connections and lets LoginGraceTime expire on each, hoping one fires SIGALRM mid-free():
1
2
3
4
5
6
7
8
9
10
11
12
13
# Conceptual — real exploit pre-computes glibc offsets and sprays FILE structures
import socket, threading, time
def race(host, delay):
s = socket.create_connection((host, 22))
s.sendall(b"SSH-2.0-regreSSHion\r\n")
# Send a partial KEX init sized to nudge sshd into malloc(), then stall
s.sendall(KEX_PRIMER + b"A" * SPRAY_LEN)
time.sleep(delay) # tuned so alarm fires inside malloc()
s.close()
for _ in range(10000):
threading.Thread(target=race, args=(target, GRACE_TIME - 0.0007)).start()
When the race wins, syslog() reuses a half-freed _IO_FILE structure whose vtable the attacker has primed via the KEX spray. vtable->_xsputn redirects to a small ROP chain into glibc’s gadget set, ending in execve("/bin/sh", ...) as UID 0. Qualys’s lab numbers: ~10k connections per win on 32-bit Debian 12.
Detection and defence
- Upgrade OpenSSH to 9.8p1 (released 2024-07-01) or apply distro patches (USN-6859, DSA-5724, RHSA-2024:4312)
- Stopgap:
LoginGraceTime 0insshd_configdisables the alarm and blocks the race at the cost of letting slow clients sit forever — pair with connection limits MaxStartups,PerSourceMaxStartups, fail2ban-style throttles raise the time-to-exploit dramatically- Detect: spikes of half-open SSH sessions from a single source, sshd children segfaulting under load (
coredumpctl list sshd), or auth logs full ofTimeout before authentication - Network layer: rate-limit TCP/22 SYNs per source; expose SSH only via VPN or bastion
- glibc heap mitigations (tcache double-free check, safe-linking) raise difficulty but do not prevent exploitation
References
- CVE-2024-6387 — NVD — official record
- Qualys regreSSHion advisory — discoverer write-up with timing analysis
- OpenSSH 9.8 release notes — vendor fix and rationale
See also: linux-enumeration, kernel-exploits-linux, linux-privesc-vectors.