openjpeg icon indicating copy to clipboard operation
openjpeg copied to clipboard

Multiple crashes in parsing PPM and J2K files

Open retpoline opened this issue 4 years ago • 8 comments

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 avatar Feb 10 '22 17:02 retpoline

@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?

zodf0055980 avatar Jun 01 '22 00:06 zodf0055980

Thanks for taking a look. Attached the second repro file.

crash.j2k.txt

retpoline avatar Jun 01 '22 16:06 retpoline

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

zodf0055980 avatar Jun 02 '22 01:06 zodf0055980

Did you happen to repro in the console or in a debugger using libefence?

(gdb) set environment LD_PRELOAD=/usr/lib/libefence.so

retpoline avatar Jun 02 '22 01:06 retpoline

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

zodf0055980 avatar Jun 02 '22 02:06 zodf0055980

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

retpoline avatar Jun 02 '22 02:06 retpoline

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.

zodf0055980 avatar Jun 02 '22 03:06 zodf0055980

Perhaps it was fixed in a previous commit, it's been a few months since these crashes were found.

retpoline avatar Jun 02 '22 13:06 retpoline