CVE-2024-6387 in 60 seconds

OpenSSH’s sshd is the server process that accepts remote SSH connections. A client is allowed a limited time to authenticate. If that time expires, the operating system delivers a SIGALRM signal so sshd can terminate the unfinished session.

In Portable OpenSSH 8.5p1 through 9.7p1, that alarm handler called logging code. Logging looks harmless, but on glibc-based Linux systems syslog() can call complex memory-management functions such as malloc() and free(). A signal can interrupt the normal program at almost any instant — including while the same heap allocator is halfway through updating its internal state.

If the alarm arrived during that narrow window, the handler could enter the allocator a second time before the first operation finished. The immediate result might be a crash. With carefully shaped SSH messages and many repeated timing attempts, however, Qualys demonstrated that this inconsistent heap state could be turned into unauthenticated remote code execution as root on a 32-bit Linux/glibc target.

The attack path was:

remote client opens an SSH connection
  -> client deliberately does not complete authentication
  -> LoginGraceTime expires and SIGALRM interrupts sshd
  -> signal handler calls unsafe logging code
  -> syslog re-enters malloc/free while heap state may be incomplete
  -> repeated attempts try to win the timing race
  -> attacker-controlled heap corruption may reach code execution as root

No valid username, password, or SSH key was required to reach the vulnerable path. The difficulty was timing: the attacker had to make the signal arrive during a tiny unsafe window and repeat the attempt enough times to overcome ASLR and connection limits.

What are sshd, LoginGraceTime, and SIGALRM?

sshd is OpenSSH’s internet-facing server daemon. It starts processing a connection before the remote user has authenticated because it must negotiate the protocol and examine authentication messages. Some of this pre-authentication work occurs in a privileged process.

LoginGraceTime is the maximum time a client may spend authenticating. The usual upstream default was 120 seconds. When the deadline expires, sshd receives SIGALRM, an asynchronous operating- system signal.

“Asynchronous” is the important word. An ordinary function call happens at a point chosen by the program. A signal handler can begin while an unrelated operation is temporarily holding a lock or has only half-updated a data structure. POSIX therefore permits only a small set of async-signal-safe operations inside a handler. General logging is not one of them.

The vulnerability did not exist because SSH had a timeout. It existed because timeout handling performed work that was unsafe in an asynchronous and privileged execution context.

What happened?

The public record supports the following chronology:

  1. In 2006, OpenSSH fixed CVE-2006-5051 by making the fatal signal path terminate with _exit(1) instead of reaching unsafe logging behavior.
  2. An October 2020 logging refactor accidentally removed the DO_LOG_SAFE_IN_SIGHAND condition that preserved that special behavior. The regression entered Portable OpenSSH 8.5p1.
  3. When an unauthenticated client exceeded LoginGraceTime, grace_alarm_handler() could once again reach sigdie() and, on affected platforms, non-async-signal-safe logging functions.
  4. Qualys reconstructed the old bug class and demonstrated exploitation against a current 32-bit Debian Linux/glibc environment. Their laboratory attack needed roughly 10,000 race attempts and, with the tested limits and ASLR, averaged six to eight hours for a root shell.
  5. OpenSSH published version 9.8/9.8p1 on 1 July 2024. The new design performs minimal termination in the alarm handler and moves logging and per-source penalty work into normal listener context.

The name regreSSHion reflects this history: a security property fixed in 2006 was lost during a later refactor. The old vulnerable source was not copied back verbatim; the guarantee established by the old fix disappeared.

Who was actually affected?

The upstream affected range was Portable OpenSSH 8.5p1 through 9.7p1 inclusive. That version range is a starting point, not a complete exposure decision. Practical risk also depended on:

  • an internet-reachable sshd process;
  • a platform whose logging path was not async-signal-safe, notably glibc-based Linux;
  • a package without a vendor backport or downstream change that removed the vulnerable path;
  • LoginGraceTime and MaxStartups settings that allowed repeated pre-authentication attempts;
  • architecture, allocator behavior, ASLR, network stability, and attack time.

OpenSSH stated that successful root code execution had been demonstrated on 32-bit Linux/glibc with ASLR. At the time of the 9.8 release, 64-bit exploitation was believed possible but had not been demonstrated. Non-glibc systems had not been fully examined. OpenBSD was explicitly unaffected because its signal-time logging path used a safer implementation.

Distribution package versions must be checked through the vendor, not only through the SSH banner. For example, Ubuntu fixed affected supported releases with backported package updates whose version numbers remained below 9.8p1. Conversely, an upstream-looking version does not prove that a specific downstream build was exploitable with Qualys’s demonstrated method.

What I verified

  • In grace_alarm_handler(), the vulnerable path called sigdie() and formatted a timeout message; the patch replaces it with _exit(EXIT_LOGIN_GRACE).
  • The listener later recognizes EXIT_LOGIN_GRACE in child_reap(), logs synchronously, and applies the source penalty outside the signal handler.
  • I checked the 9.8 release notes and the public technical advisory against the patch to separate the code fact from platform-specific exploitability claims.
  • This is a static reproduction of the fixed control flow. I did not perform timing attempts, allocator manipulation, or exploit testing against any SSH service.

Deep dive: why logging from SIGALRM can corrupt the heap

Normal function calls are sequenced. A function updates its state, calls another function, and returns when its invariants are restored. An asynchronous signal is different: the handler may run while the interrupted code temporarily holds a lock, owns a half-updated allocator structure, or has changed global state that is not yet consistent.

POSIX therefore defines a small set of async-signal-safe operations. General logging is not among them. On affected glibc systems, syslog() can allocate and free memory. If the alarm interrupts the main authentication path during its own allocator operation, the handler may enter the same allocator again against transient state.

ASYNCHRONOUS RE-ENTRY Auth parsingnormal control flow malloc / freestate in transition SIGALRMdeadline expires syslogallocates again Heap statecorrupted The handler runs inside the privileged,pre-authentication server process.
The bug is not that a timeout exists. The bug is that timeout handling re-enters complex process state asynchronously while that state may be inconsistent.

Crashes are the easiest visible outcome, but not the security boundary. The Qualys research showed that carefully repeated connections could shape and interrupt allocator activity on a 32-bit Linux/glibc target, eventually reaching unauthenticated code execution as root. OpenSSH’s 9.8 notes report an average of six to eight hours in the demonstrated laboratory configuration and state that 64-bit exploitation was believed possible but had not been demonstrated at release.

These conditions should be preserved rather than simplified into “all SSH servers are instantly rootable.” Platform libc, architecture, ASLR behavior, downstream patches, LoginGraceTime, and connection limits change practical reachability. They do not remove the underlying unsafe handler.

The regression was a lost invariant, not a reverted patch

The 2006 correction made the fatal signal path call _exit(1) rather than unsafe logging logic. Qualys traced the regression to an October 2020 logging infrastructure change. The refactor removed the DO_LOG_SAFE_IN_SIGHAND guard from the function reached by the alarm handler.

This is an important patch-analysis pattern. A security fix often establishes an invariant that is larger than the exact diff: “the signal handler performs only safe termination.” Years later, a maintainer can reorganize logging correctly for ordinary callers while unknowingly reconnecting an exceptional caller with stricter rules.

Tests that assert only a historical line or macro survives are fragile. The durable regression test must identify the special calling context and prove that every path from it remains async-signal-safe.

Why privilege separation did not contain this path

OpenSSH uses privilege separation extensively, but the vulnerable operation occurred before authentication completed in a privileged server process. A remote client did not need a valid account or credential to keep a connection open until the grace timer expired.

This illustrates a common assessment error: a product can have excellent sandboxing and still retain a narrow privileged path for setup, monitoring, or teardown. Security analysis must map the specific process and lifecycle phase executing the vulnerable code. “The application is sandboxed” is not evidence until the affected instruction is placed inside that sandbox.

The 9.8p1 fix moves work out of the handler

The public fix was part of a broader server restructuring and per-source penalty work. Instead of performing unsafe cleanup and logging inside the alarm handler, the design moves meaningful work to the listener, where it can run synchronously. A minimal backport can also make sshsigdie() terminate with _exit(1) and omit the unsafe formatting and logging path.

BEFORE SIGALRMasync context Format + logunsafe calls Allocatorre-entered 9.8P1 PROPERTY SIGALRMsafe exit Listener eventnormal context Log / penalizesynchronous
The robust fix restores context separation: an asynchronous handler does the minimum safe action, while ordinary process logic performs logging and policy work.

The fix is architectural, not a timing adjustment. Increasing or decreasing LoginGraceTime may alter attack economics, but it does not make an unsafe signal handler safe. Connection throttling is useful defense in depth; patching removes the root cause.

Evidence matrix

QuestionWhat I checkedConfidenceLimit
Which modern versions regressed?I matched OpenSSH and Qualys records identifying Portable OpenSSH 8.5p1 through 9.7p1.HighVendor backports and platform-specific changes must be checked separately.
What triggers the path?I traced LoginGraceTime expiration to the SIGALRM handler before authentication completes.HighPractical timing depends on configuration and network behavior.
Why is logging dangerous there?I confirmed the old handler reaches logging that is not async-signal-safe and may re-enter allocator operations.HighExact libc internals vary by platform.
Was remote root execution demonstrated?I reviewed Qualys’s 32-bit Linux/glibc lab result; I did not repeat it.HighThe release record did not demonstrate the same result on 64-bit targets.
Why is OpenBSD excluded?I checked OpenSSH’s note that OpenBSD uses a safer logging path and is not vulnerable.HighOther non-glibc systems require their own analysis.
What does 9.8p1 change?Unsafe handler work is removed or shifted into synchronous listener processing.HighDistribution packages may express the fix with an older-looking version.

Safe validation without racing sshd

Start with the vendor package record. Capture the server’s Portable OpenSSH version, operating system, architecture, libc, package revision, and the distribution’s CVE status. Do not decide from the SSH banner alone: vendors can hide versions, backport fixes, or apply downstream patches that change reachability.

For source validation, inspect the vendor’s sshsigdie() or equivalent patch. The fixed assertion is structural: the alarm handler must not format messages, call general logging, allocate memory, or perform other non-async-signal-safe work. It should terminate safely or communicate minimal state to code that handles it synchronously.

A controlled regression test can launch an instrumented, non-production build in an isolated lab, allow an unauthenticated connection to exceed the grace period, and observe the handler’s call set. The test passes when the signal path reaches only allowed operations. There is no need to tune packet timing, corrupt a heap, or seek code execution.

Use these negative controls:

  • the same observation against 9.8p1;
  • a vendor build with a documented backport despite an older upstream version;
  • a platform whose advisory marks it unaffected;
  • a normal synchronous logging event, proving that logging still works outside the handler;
  • a configuration with the deadline disabled, treated only as reachability comparison—not a fix.

Repeated unauthenticated connections against an internet-facing service are not an appropriate verification method. They can exhaust resources and resemble exploitation. Static patch proof plus one bounded lab observation provides clearer evidence with less operational risk.

My conclusion

Upgrade to OpenSSH 9.8p1 or the distribution package that explicitly backports CVE-2024-6387. Before maintenance completes, connection-rate controls and reduced exposure can lower risk, but they should be documented as temporary compensating controls rather than remediation.

My conclusion is that the durable fix is the smaller signal handler, not a more careful logger. The handler now exits with a minimal signal-safe operation, while ordinary code performs logging later. Refactors that touch shared logging or cleanup code must explicitly re-check exceptional callers such as signal handlers instead of assuming ordinary call rules still apply.

Public sources

Sources & limits

Evidence used for this analysis

Sources checkedAugust 28, 2026

Version and exploitation status can change; follow the linked vendor records.

Review statusAuthor review complete

The author completed a technical review. This does not, by itself, claim lab reproduction.

◈ Cite This ResearchBibTeX · Markdown

Reference this analysis, root-cause teardown, or vulnerability discovery in your technical reports or academic publications:

@misc{jankesec_openssh_signal_race_cve_2024_6387_2025,
  author       = {Sevban D\"{o}nmez},
  title        = {CVE-2024-6387: How an OpenSSH Timeout Could Lead to Remote Root Access},
  year         = {2025},
  howpublished = {\url{https://jankesec.com/research/openssh-signal-race-cve-2024-6387/}},
  note         = {jankesec technical security research (CVE-2024-6387)}
}