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

heap-buffer-overflow in sdscatlen #1187

Open
Jminis opened this issue May 17, 2023 · 0 comments
Open

heap-buffer-overflow in sdscatlen #1187

Jminis opened this issue May 17, 2023 · 0 comments

Comments

@Jminis
Copy link

Jminis commented May 17, 2023

Hello,

I encountered a crash while performing my fuzzing tasks, which prompted me to leave this message.

have a good day :)

📌 Main Information

Project hiredis
Fuzzer libfuzzer
Fuzz binary format_command_fuzzer
Sanitizer asan
Crash Type Heap-buffer-overflow
Crash State sdscatlen in /src/hiredis/sds.c:386:5
=================================================================
==643==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x603000000145 at pc 0x00000052e371 bp 0x7fffffffd7d0 sp 0x7fffffffcfa0
READ of size 260 at 0x603000000145 thread T0
SCARINESS: 26 (multi-byte-read-heap-buffer-overflow)
[Detaching after fork from child process 645]
    #0 0x52e370 in __asan_memcpy /src/llvm-project/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:22:3
    #1 0x57ab16 in sdscatlen /src/hiredis/sds.c:386:5
    #2 0x56d2de in redisvFormatCommand /src/hiredis/hiredis.c
    #3 0x56f2cd in redisFormatCommand /src/hiredis/hiredis.c:569:11
    #4 0x56c53e in LLVMFuzzerTestOneInput /src/hiredis/format_command_fuzzer.c:51:9
    #5 0x43de53 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:611:15
    #6 0x4295b2 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:324:6
    #7 0x42ee5c in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:860:9
    #8 0x458392 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    #9 0x7ffff7c58082 in __libc_start_main /build/glibc-SzIz7B/glibc-2.31/csu/../csu/libc-start.c:308:16
    #10 0x41f77d in _start (/out/hiredis/format_command_fuzzer+0x41f77d)

DEDUP_TOKEN: __asan_memcpy--sdscatlen--redisvFormatCommand
0x603000000145 is located 0 bytes to the right of 21-byte region [0x603000000130,0x603000000145)
allocated by thread T0 here:
    #0 0x52efe6 in __interceptor_malloc /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:69:3
    #1 0x56c4f0 in LLVMFuzzerTestOneInput /src/hiredis/format_command_fuzzer.c:44:15
    #2 0x43de53 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:611:15
    #3 0x4295b2 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:324:6
    #4 0x42ee5c in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:860:9
    #5 0x458392 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    #6 0x7ffff7c58082 in __libc_start_main /build/glibc-SzIz7B/glibc-2.31/csu/../csu/libc-start.c:308:16

DEDUP_TOKEN: __interceptor_malloc--LLVMFuzzerTestOneInput--fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long)
SUMMARY: AddressSanitizer: heap-buffer-overflow /src/llvm-project/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:22:3 in __asan_memcpy
Shadow bytes around the buggy address:
  0x0c067fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c067fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c067fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c067fff8000: fa fa 00 00 00 fa fa fa 00 00 00 fa fa fa 00 00
  0x0c067fff8010: 00 fa fa fa 00 00 00 00 fa fa 00 00 04 fa fa fa
=>0x0c067fff8020: 00 00 04 fa fa fa 00 00[05]fa fa fa fa fa fa fa
  0x0c067fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
==643==ABORTING
[Thread 0x7ffff40f9700 (LWP 644) exited]
[Inferior 1 (process 643) exited with code 01]

🎯 Fuzz target Information

  • format_command_fuzzer.c
#include <stdlib.h>
#include <string.h>
#include "hiredis.h"

int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
char *new_str, *cmd;

if (size &lt; 3)
    return 0;

new_str = malloc(size+1);
if (new_str == NULL)
    return 0;

memcpy(new_str, data, size);
new_str[size] = '\\0';

if (redisFormatCommand(&amp;cmd, new_str) != -1)
    hi_free(cmd);

free(new_str);
return 0;

}

  • Crash data
24 68 78 25 25 25 62 22 20 22 20 25 08 01 20 01 20 81 68 78

🧐 Analysis

  • The function is invoked in the following order with the input data $hx%%%b…:
    • $newarg = sdscatlen(curarg, c, 1);
    • hnewarg = sdscatlen(curarg, c, 1);
    • xnewarg = sdscatlen(curarg, c, 1);
    • %%newarg = sdscat(curarg, "%");
    • %bnewarg = sdscatlen(curarg, arg, size);
  • The crash occurs in the memcpy inside the last sdscatlen call, and the cause is that the size value is larger than the length of the input data.
  • The size value is initialized during the size = va_arg(ap, size_t); step before the sdscatlen function is called.
sds sdscatlen(sds s, const void *t, size_t len) {
    size_t curlen = sdslen(s);
s = sdsMakeRoomFor(s,len);
if (s == NULL) return NULL;
memcpy(s+curlen, t, len); // [*] -- crash state
sdssetlen(s, curlen+len);
s[curlen+len] = '\\0';
return s;

}

int redisvFormatCommand(char **target, const char *format, va_list ap) {
/* skip */
    while(*c != '\\0') {
        if (*c != '%' || c[1] == '\\0') {
            if (*c == ' ') {
                if (touched) {
                    newargv = hi_realloc(curargv,sizeof(char*)*(argc+1));
                    if (newargv == NULL) goto memory_err;
                    curargv = newargv;
                    curargv[argc++] = curarg;
                    totlen += bulklen(sdslen(curarg));

                    /* curarg is put in argv so it can be overwritten. */
                    curarg = sdsempty();
                    if (curarg == NULL) goto memory_err;
                    touched = 0;
                }
            } else {
                newarg = sdscatlen(curarg,c,1);
                if (newarg == NULL) goto memory_err;
                curarg = newarg;
                touched = 1;
            }
        } else {
            char *arg;
            size_t size;

            /* Set newarg so it can be checked even if it is not touched. */
            newarg = curarg;

            switch(c[1]) {
            case 's':
                arg = va_arg(ap,char*);
                size = strlen(arg);
                if (size > 0)
                    newarg = sdscatlen(curarg,arg,size);
                break;
            case 'b':
                arg = va_arg(ap,char*);
                size = va_arg(ap,size_t);
                if (size > 0)
                    newarg = sdscatlen(curarg,arg,size); // [*] -- crash func call
                break;
            case '%':
                newarg = sdscat(curarg,"%");
                break;
/* skip */
}
  • If a larger memcpy occurs than the size of the input data, it is expected that arbitrary data will be copied into the buffer.

debug

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