Multiple crashes in parsing PPM and J2K files
Hi team,
Some crashes were found while fuzz testing of the obj_compress and obj_decompress binaries which can be triggered via malformed PPM and J2K files. Although these malformed files only crash the program, they could potentially be crafted further into security issues where these kinds of files would be able compromise the process's memory through memory corruption, so hardening the code to prevent these kinds of bugs would be great to mitigate such issues.
See details below for repro and debug information.
crash.ppm
$ echo -ne "\x50\x35\x0a\x36\x33\x33\x32\x35\x34\x38\x20\x31\x37\x39\x0a\x36\x56\x5c" > crash.ppm
$ LD_PRELOAD=/usr/lib/libefence.so opj_compress -o test.j2k -i crash.ppm
Electric Fence 2.2 Copyright (C) 1987-1999 Bruce Perens <[email protected]>
Segmentation fault (core dumped)
$ gdb -q opj_compress
Reading symbols from opj_compress...
(gdb) set environment LD_PRELOAD=/usr/lib/libefence.so
(gdb) r -o test.j2k -i crash.ppm
Starting program: opj_compress -o test.j2k -i crash.ppm
Electric Fence 2.2 Copyright (C) 1987-1999 Bruce Perens <[email protected]>
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7ad2b0b in mprotect () at ../sysdeps/unix/syscall-template.S:78
(gdb) bt
#0 0x00007ffff7ad2b0b in mprotect () at ../sysdeps/unix/syscall-template.S:78
#1 0x00007ffff7dc423e in Page_AllowAccess () from /usr/lib/libefence.so
#2 0x00007ffff7dc3745 in ?? () from /usr/lib/libefence.so
#3 0x00007ffff7dc3e7e in memalign () from /usr/lib/libefence.so
#4 0x00007ffff7dc3f0a in posix_memalign () from /usr/lib/libefence.so
#5 0x00007ffff7dad92d in opj_aligned_malloc () from libopenjp2.so.7
#6 0x00007ffff7d72d80 in opj_image_create () from libopenjp2.so.7
#7 0x0000555555562461 in pnmtoimage ()
#8 0x000055555555980a in main ()
(gdb) i r
rax 0xfffffffffffffff4 -12
rbx 0x7ffff78730f0 140737346220272
rcx 0x7ffff7ad2b0b 140737348709131
rdx 0x3 3
rsi 0x10e410000 4534108160
rdi 0x7fffe9462000 140737107075072
rbp 0x10e411000 0x10e411000
rsp 0x7fffffff3038 0x7fffffff3038
r8 0xffffffff 4294967295
r9 0x0 0
r10 0x22 34
r11 0x202 514
r12 0x7ffff7873118 140737346220312
r13 0x7ffff7873118 140737346220312
r14 0x10e40f130 4534104368
r15 0x7ffff7fc6128 140737353900328
rip 0x7ffff7ad2b0b 0x7ffff7ad2b0b <mprotect+11>
eflags 0x10202 [ IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) x/i $rip
=> 0x7ffff7ad2b0b <mprotect+11>: cmp $0xfffffffffffff001,%rax
(gdb) exploitable
Description: Segmentation fault on program counter
Short description: SegFaultOnPc (3/22)
Hash: 528849695cd9ced4e083e35832bab199.a91791d34d585cf7e2e269635926f278
Exploitability Classification: EXPLOITABLE
Explanation: The target tried to access data at an address that matches the program counter. This is likely due to the execution of a branch instruction (ex: 'call') with a bad argument, but it could also be due to execution continuing past the end of a memory region or another cause. Regardless this likely indicates that the program counter contents are tainted and can be controlled by an attacker.
Other tags: AccessViolation (21/22)
Repro J2K file: https://ufile.io/mnz1gy2x
$ gdb -q opj_decompress
Reading symbols from opj_decompress...
(gdb) set environment LD_PRELOAD=/usr/lib/libefence.so
(gdb) r -o test.png -i crash.j2k
Starting program: opj_decompress -o test.png -i crash.j2k
Electric Fence 2.2 Copyright (C) 1987-1999 Bruce Perens <[email protected]>
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[INFO] Start to read j2k main header (0).
[INFO] Main header has been correctly decoded.
[INFO] No decoded area parameters, set the decoded area to the whole image
[INFO] Header of tile 1 / 1220 has been read.
[INFO] Tile 1/1220 has been decoded.
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7ad2b0b in mprotect () at ../sysdeps/unix/syscall-template.S:78
(gdb) bt
#0 0x00007ffff7ad2b0b in mprotect () at ../sysdeps/unix/syscall-template.S:78
#1 0x00007ffff7dc423e in Page_AllowAccess () from /usr/lib/libefence.so
#2 0x00007ffff7dc3745 in ?? () from /usr/lib/libefence.so
#3 0x00007ffff7dc3e7e in memalign () from /usr/lib/libefence.so
#4 0x00007ffff7dc3f0a in posix_memalign () from /usr/lib/libefence.so
#5 0x00007ffff7dad92d in opj_aligned_malloc () from libopenjp2.so.7
#6 0x00007ffff7d7998b in opj_j2k_update_image_data.isra () from libopenjp2.so.7
#7 0x00007ffff7d816ce in opj_j2k_decode_tiles () from libopenjp2.so.7
#8 0x00007ffff7d833f7 in opj_j2k_decode () from libopenjp2.so.7
#9 0x0000555555558543 in main ()
(gdb) i r
rax 0xfffffffffffffff4 -12
rbx 0x7ffff2810e40 140737261932096
rcx 0x7ffff7ad2b0b 140737348709131
rdx 0x3 3
rsi 0x60e539000 26010161152
rdi 0x7fffe06e8000 140736958726144
rbp 0x60e53a000 0x60e53a000
rsp 0x7fffffff8fb8 0x7fffffff8fb8
r8 0xffffffff 4294967295
r9 0x0 0
r10 0x22 34
r11 0x206 518
r12 0x7ffff2810f58 140737261932376
r13 0x7ffff2810f58 140737261932376
r14 0x60e538c00 26010160128
r15 0x7ffff7fc6128 140737353900328
rip 0x7ffff7ad2b0b 0x7ffff7ad2b0b <mprotect+11>
eflags 0x10206 [ PF IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) exploitable
Description: Segmentation fault on program counter
Short description: SegFaultOnPc (3/22)
Hash: 528849695cd9ced4e083e35832bab199.620cb1a526c71671f0204702e9b3e2e0
Exploitability Classification: EXPLOITABLE
Explanation: The target tried to access data at an address that matches the program counter. This is likely due to the execution of a branch instruction (ex: 'call') with a bad argument, but it could also be due to execution continuing past the end of a memory region or another cause. Regardless this likely indicates that the program counter contents are tainted and can be controlled by an attacker.
Other tags: AccessViolation (21/22)
Expected behavior and actual behavior.
Not crash with memory corruption.
Steps to reproduce the problem.
Run obj_compress and obj_decompress on the appropriate repro files.
Operating system
Ubuntu 20.04 x64
openjpeg version
It has been compiled against openjp2 library v2.5.0.
@retpoline In first case, I can't reproduce it in current master (0535bfc3b7d5cd6fc73a7d4a6749a338fc5d7703) with ASAN.
➜ git:(master) ✗ echo -ne "\x50\x35\x0a\x36\x33\x33\x32\x35\x34\x38\x20\x31\x37\x39\x0a\x36\x56\x5c" > crash.ppm
➜ git:(master) ✗ ./opj_compress -o test.j2k -i crash.ppm
Missing data. Quitting.
Unable to load pnm file
And in second case, I can't download the file. Could you help to check in current master or release new file?
In second poc, It use a lot of memory and is killed by my system.
➜ git:(master) ✗ ./opj_decompress -o test.png -i ./crash.j2k
[INFO] Start to read j2k main header (0).
[INFO] Main header has been correctly decoded.
[INFO] No decoded area parameters, set the decoded area to the whole image
[INFO] Header of tile 1 / 1220 has been read.
[INFO] Tile 1/1220 has been decoded.
[1] 212562 killed ./opj_decompress -o test.png -i ~/openjpeg/build/bin/crash.j2k
Did you happen to repro in the console or in a debugger using libefence?
(gdb) set environment LD_PRELOAD=/usr/lib/libefence.so
I didn't use libefence, but I build with AddressSanitizer and not have memory error report. In second test I found it malloc 26010160128 byte here。 (24 GB) https://github.com/uclouvain/openjpeg/blob/0535bfc3b7d5cd6fc73a7d4a6749a338fc5d7703/src/lib/openjp2/j2k.c#L10111-L10112
And numcomps is 3 https://github.com/uclouvain/openjpeg/blob/0535bfc3b7d5cd6fc73a7d4a6749a338fc5d7703/src/lib/openjp2/j2k.c#L9952
So it will malloc 72GB and memset it. Maybe second test is killed by not enough memory
I'd recommend running it with efence and seeing if it repros as it works differently than ASAN and each of them may catch bugs that the other does not. It's pretty easy to install and use.
apt-get install -y electric-fence
LD_PRELOAD=/usr/lib/libefence.so opj_compress -o test.j2k -i crash.ppm
But efence report error in posix_memalign. https://github.com/uclouvain/openjpeg/blob/0bda7188b7b545232a341f1d978b1e4feda46fc2/src/lib/openjp2/opj_malloc.c#L61 I don't think this function have any problem, this just posix normal function use to return alloc address.
Perhaps it was fixed in a previous commit, it's been a few months since these crashes were found.