chafa <= 4bac1466 is vulnerable to an out of bounds read vulnerability.
Build chafa with ASAN(address sanitizer)
$ git rev-parse HEAD
4bac14668535c09f6f47552bbd1566097dab4bf8
$ export CFLAGS="-g -O0 -fsanitize=address"; export CXXFLAGS="-g -O0 -fsanitize=address"; export CC=$(which clang-10); export CXX=$(which clang++-10)
$ ./autogen.sh
$ ./configure --disable-shared
$ make -j 8
PoC available here - google-drive
$ ./tools/chafa/chafa /tmp/cc6d16b6c395925244b398c849a02a47625f67dc
=================================================================
==4615==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62a000005259 at pc 0x0000005abae6 bp 0x7ffc0e5fae90 sp 0x7ffc0e5fae88
READ of size 1 at 0x62a000005259 thread T0
#0 0x5abae5 in lzw_decode /tmp/chafa/libnsgif/lzw.c:362:45
#1 0x5a7901 in gif_internal_decode_frame /tmp/chafa/libnsgif/libnsgif.c:868:47
#2 0x5a541c in gif_decode_frame /tmp/chafa/libnsgif/libnsgif.c:1151:16
#3 0x4d89c7 in maybe_decode_frame /tmp/chafa/tools/chafa/gif-loader.c:68:12
#4 0x4d8569 in gif_loader_get_frame_data /tmp/chafa/tools/chafa/gif-loader.c:204:10
#5 0x4d9b47 in media_loader_get_frame_data /tmp/chafa/tools/chafa/media-loader.c:282:12
#6 0x4cea97 in run_generic /tmp/chafa/tools/chafa/chafa.c:1786:22
#7 0x4ce3a6 in run /tmp/chafa/tools/chafa/chafa.c:1923:12
#8 0x4c74a4 in run_all /tmp/chafa/tools/chafa/chafa.c:1983:18
#9 0x4c5dd1 in main /tmp/chafa/tools/chafa/chafa.c:2035:11
#10 0x7fc448ca9c86 in __libc_start_main /build/glibc-CVJwZb/glibc-2.27/csu/../csu/libc-start.c:310
#11 0x41ddb9 in _start (/tmp/chafa/tools/chafa/chafa+0x41ddb9)
0x62a000005259 is located 9 bytes to the right of 20560-byte region [0x62a000000200,0x62a000005250)
allocated by thread T0 here:
#0 0x4964fd in malloc (/tmp/chafa/tools/chafa/chafa+0x4964fd)
#1 0x5a9f05 in lzw_context_create /tmp/chafa/libnsgif/lzw.c:91:22
#2 0x5a0823 in gif_initialise /tmp/chafa/libnsgif/libnsgif.c:946:34
#3 0x4d7e21 in gif_loader_new_from_mapping /tmp/chafa/tools/chafa/gif-loader.c:151:16
#4 0x4d93ba in media_loader_new /tmp/chafa/tools/chafa/media-loader.c:213:30
#5 0x4ce8cf in run_generic /tmp/chafa/tools/chafa/chafa.c:1747:20
#6 0x4ce3a6 in run /tmp/chafa/tools/chafa/chafa.c:1923:12
#7 0x4c74a4 in run_all /tmp/chafa/tools/chafa/chafa.c:1983:18
#8 0x4c5dd1 in main /tmp/chafa/tools/chafa/chafa.c:2035:11
#9 0x7fc448ca9c86 in __libc_start_main /build/glibc-CVJwZb/glibc-2.27/csu/../csu/libc-start.c:310
SUMMARY: AddressSanitizer: heap-buffer-overflow /tmp/chafa/libnsgif/lzw.c:363:45 in lzw_decode
Shadow bytes around the buggy address:
0x0c547fff89f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c547fff8a00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c547fff8a10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c547fff8a20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c547fff8a30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c547fff8a40: 00 00 00 00 00 00 00 00 00 00 fa[fa]fa fa fa fa
0x0c547fff8a50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c547fff8a60: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c547fff8a70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c547fff8a80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c547fff8a90: 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
Shadow gap: cc
==4615==ABORTING
I know that the stacktrace shows libnsgif
which has upstream - https://github.com/netsurf-plan9/libnsgif but this is the only downstream project that has a good security posture. Also the upsteam doesn’t build that well with missing instructions. I’ll try to build and update here in a later comment.
The table
is allocated with a fixed size of 1 << LZW_CODE_MAX
= 0x1000 members.
But here in the PoC it could be larger
gef➤ print (code >> current_bit) & ((1 << code_size) - 1)
$5 = 0x1002
This would read outof the bounds for this array. I haven’t looked into much on why we the values get to this state. This PoC is from a fuzzing session.
Thanks