regreSSHion: Time Travel is Real, and It Roots Your Box
Jan 5, 2026·5 min read
Executive Summary (TL;DR)
OpenSSH's `sshd` has a race condition in its `SIGALRM` handler. If a client doesn't authenticate within the `LoginGraceTime` (usually 120s), the handler fires. Unfortunately, the handler calls `syslog()`, which is not async-signal-safe. If this interrupts the heap manager (malloc/free) in the main thread, heap corruption occurs. Attackers can exploit this to gain unauthenticated root access, though it takes ~10,000 attempts (6-8 hours) on average.
A signal handler race condition in OpenSSH's sshd allows unauthenticated remote code execution as root on glibc-based Linux systems. This is a regression of a vulnerability originally fixed in 2006, proving that history doesn't just repeat itself—it recompiles.
The Hook: The Zombie Bug Returns
In the world of software development, we like to think we move forward. We fix bugs, we write tests, we deploy. But CVE-2024-6387, dubbed 'regreSSHion', is a stark reminder that codebases have long memories.
This isn't a new vulnerability. It's a zombie. Specifically, it's a regression of CVE-2006-5051, a signal handler race condition that was patched nearly two decades ago. Somehow, in a refactor around October 2020 (OpenSSH 8.5p1), someone accidentally removed the safety rails.
The target? sshd, the ubiquitous daemon that guards the front door of nearly every Linux server on the planet. The impact? Unauthenticated Remote Code Execution (RCE) as root. If that doesn't make you check your patch levels, nothing will.
The Flaw: Async-Signal-Safety 101
To understand this bug, you need to understand the absolute chaos of UNIX signals. When a signal (like SIGALRM) fires, the OS pauses whatever the main thread is doing—literally anywhere in the execution flow—and jumps to the signal handler.
Because the main thread is frozen, the signal handler must be incredibly careful. It cannot call functions that might be holding locks or modifying global state that the main thread was also using. These safe functions are called Async-Signal-Safe.
Here is the cardinal sin: syslog() is NOT async-signal-safe. It uses malloc() and free() internally to format messages. If the main thread was in the middle of a malloc() (manipulating the heap linked lists) when the signal hit, and the signal handler also calls syslog() (which calls malloc()), you get re-entrancy into a non-reentrant function. The result? The heap metadata gets scrambled like eggs.
The Code: The Smoking Gun
Let's look at the crime scene in sshd.c. The logic is supposed to handle timeouts. If you take too long to type your password (LoginGraceTime), sshd kills the connection.
The vulnerable chain looks like this:
SIGALRMfires.grace_alarm_handler()is called.- It calls
sigdie(). sigdie()callssyslog().
Here is the simplified pseudocode of the disaster:
// The Signal Handler
void grace_alarm_handler(int sig) {
// ... logic ...
sigdie("Timeout before authentication");
}
void sigdie(const char *fmt, ...) {
// DOOM: syslog is not safe here!
syslog(LOG_INFO, fmt, ...);
_exit(1);
}The fix is technically simple but conceptually strict: remove the logging from the signal handler. The patch moves the complex logic out of the interrupt context, ensuring we only call async-signal-safe functions (like _exit) directly.
The Exploit: Winning the Race
Exploiting this is not like throwing a payload at a buffer overflow. It's a probabilistic race condition. We are playing a timing game against the CPU scheduler.
To win, the attacker must force sshd to be inside a malloc() or free() call exactly when the SIGALRM timer expires. Since we know the LoginGraceTime is usually 120 seconds, we can attempt to manipulate the timing.
The attack strategy involves:
- Open a connection to
sshd. - Send specific public key packets to force the server to allocate memory (heap grooming/Feng Shui).
- Wait.
- Hope the signal fires while the heap is in an inconsistent state.
Qualys (who found this) noted that on a 32-bit system, this is easier because heap structures are predictable. On 64-bit AMD64 with ASLR, it's a nightmare. It takes roughly ~10,000 attempts to succeed. That sounds like a lot, but in a scriptable attack, that's just a few hours of noise. Once the race is won, the heap corruption allows overwriting a struct that contains function pointers, redirecting execution to a ROP chain.
The Impact: Root for Everyone
If an attacker pulls this off, they bypass authentication entirely. They are running code within the context of the unprivileged sshd worker, which is bad, but it gets worse.
Because of the way OpenSSH is architected, exploiting the unprivileged process is often a stepping stone to kernel exploitation or privilege escalation, but in this specific race, we are interfering with the root-owned parent process in some configurations or the pre-auth stage.
The saving grace? This effectively only affects glibc-based Linux systems. Musl libc and others are likely safe because their syslog implementation or heap management differs slightly, or they don't have the specific gadgetry required for the heap exploit to land easily. Windows and macOS are generally unaffected.
The Mitigation: Kill the Grace Period
The official fix is to upgrade to OpenSSH 9.8p1. The developers reorganized the code to ensure syslog is never called from the signal handler.
Immediate Workaround:
If you can't patch immediately (and let's be honest, patching sshd on 5,000 servers on a Friday is risky), you can mitigate this by setting LoginGraceTime to 0 in sshd_config.
# /etc/ssh/sshd_config
LoginGraceTime 0[!WARNING] Setting
LoginGraceTimeto 0 means connections will never time out. This exposes you to a Denial of Service (DoS) where an attacker opens thousands of connections and just sits there, exhausting your file descriptors. It's a "pick your poison" scenario until you patch.
Official Patches
Technical Appendix
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:HAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
OpenSSH OpenBSD Foundation | >= 8.5p1, < 9.8p1 | 9.8p1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-364 |
| Attack Vector | Network (AV:N) |
| Impact | Root RCE |
| CVSS v3.1 | 8.1 (High) |
| Exploit Status | PoC Available / Complex |
| Complexity | High (AC:H) |
MITRE ATT&CK Mapping
Signal Handler Race Condition causing memory corruption
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.