Stack-based buffer overflows in file-ico
Environment/Versions
- GIMP version: 2.99.15 (Verified exists on master branch)
- Package: Compiled from source
- Operating System: All
Description of the bug
The current version of the file-ico
plugin contains a pair of buffer overflow vulnerabilities in ico-load.c
in the function ani_load_image()
in the two else if blocks beginning on line 822. These blocks both contain code like follows:
fread (&size, sizeof (size), 1, fp);
fread (&inam, sizeof (char), size, fp);
where in each case size
is an unsigned 32-bit integer, but inam
and its pair iart
are defined,
gchar inam[G_MAXSHORT] = {0};
gchar iart[G_MAXSHORT] = {0};
Because the value in size
is not validated before the second fread
, this results in a potential buffer overflow on the stack, allowing a maliciously-crafted file to place a desired value over the file pointer fp
or the function's return instruction pointer. An example file is attached.
In addition, there may be an issue with the frame
counter. If an ANI file were to have multiple icon
blocks, it may be possible to overflow the gint frame
variable and cause it to become negative. It can't happen in any single icon
block (thanks to the check on nlayers
on line 871), but since it isn't reset between them, it looks like multiple blocks can do the trick.
Reproduction
Is the bug reproducible? Always
Reproduction steps:
- Craft an ANI file containing an INAM or IART field such that the 4-byte size field is greater than
G_MAXSHORT
and the file itself is at least that large. (See example attached above) To cause a crash, the overflow should be at least 168B pastG_MAXSHORT
. - Load the file through the ordinary file selection dialog.
Expected result: It's not clear from the ANI file format spec whether INAM and IART fields of this size are valid, but the plug-in should either fail gracefully or accept the input.
Actual result:
A specially-crafted file has been demonstrated in a controlled environment to be able to execute arbitrary shellcode; outside that environment the program crashes in one of several locations depending on the overwritten values for the variables size
and fp
, or the return instruction pointer.
Additional information
Stack trace from an example run. Note the values in fp, and the overwritten return instruction pointer at #8.
/usr/local/lib/gimp/2.99/plug-ins/file-ico/file-ico: fatal error: Segmentation fault
/usr/local/lib/gimp/2.99/plug-ins/file-ico/file-ico (pid:34685): [E]xit, show [S]tack trace or [P]roceed: S
26 ../sysdeps/unix/sysv/linux/read.c: No such file or directory.
# Stack traces obtained from PID 34677 - Thread 34677 #
[New LWP 34678]
[New LWP 34679]
[New LWP 34680]
[New LWP 34681]
[New LWP 34682]
[New LWP 34683]
[New LWP 34684]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
__GI___libc_read (nbytes=255, buf=0x7ffffffed1c0, fd=10) at ../sysdeps/unix/sysv/linux/read.c:26
Id Target Id Frame
* 1 Thread 0x7ffff5397e80 (LWP 34677) "file-ico" __GI___libc_read (nbytes=255, buf=0x7ffffffed1c0, fd=10) at ../sysdeps/unix/sysv/linux/read.c:26
2 Thread 0x7ffff43ff640 (LWP 34678) "gmain" 0x00007ffff6918d7f in __GI___poll (fds=0x5555555aea30, nfds=1, timeout=-1) at ../sysdeps/unix/sysv/linux/poll.c:29
3 Thread 0x7ffff3bfe640 (LWP 34679) "gdbus" 0x00007ffff6918d7f in __GI___poll (fds=0x5555555c62f0, nfds=2, timeout=-1) at ../sysdeps/unix/sysv/linux/poll.c:29
4 Thread 0x7ffff31c8640 (LWP 34680) "worker" syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38
5 Thread 0x7ffff29c7640 (LWP 34681) "worker" syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38
6 Thread 0x7ffff21c6640 (LWP 34682) "worker" syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38
7 Thread 0x7ffff19c5640 (LWP 34683) "worker" syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38
8 Thread 0x7ffff11c4640 (LWP 34684) "worker" syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38
#0 __GI___libc_read (nbytes=255, buf=0x7ffffffed1c0, fd=10) at ../sysdeps/unix/sysv/linux/read.c:26
sc_ret = -512
sc_cancel_oldtype = 0
sc_ret = <optimized out>
#1 __GI___libc_read (fd=10, buf=0x7ffffffed1c0, nbytes=255) at ../sysdeps/unix/sysv/linux/read.c:24
#2 0x00007ffff7be5b85 in gimp_stack_trace_print (prog_name=0x7fffffffe3d0 "/usr/local/lib/gimp/2.99/plug-ins/file-ico/file-ico", stream=0x7ffff6a1a780 <_IO_2_1_stdout_>, trace=0x0) at ../libgimpbase/gimputils.c:1344
status = 1679428654
stack_printed = 0
gtrace = 0x0
gimp_pid = "34677\000\000\000\000\000\000\000\000\000\000"
buffer = "\002", '\000' <repeats 254 times>
read_n = 0
sync_fd = {8, 9}
out_fd = {10, 11}
fork_pid = 34727
pid = 34677
eintr_count = 0
tid = 34677
#3 0x00007ffff7be5e88 in gimp_stack_trace_query (prog_name=0x7fffffffe3d0 "/usr/local/lib/gimp/2.99/plug-ins/file-ico/file-ico") at ../libgimpbase/gimputils.c:1514
buf = "S\n\000UUU\000\000\340\303\373\367\377\177\000"
#4 0x00007ffff7f1c927 in gimp_plugin_sigfatal_handler (sig_num=11) at ../libgimp/gimp.c:1036
sigset = {__val = {0, 0, 13817034793688615063, 0, 13819393995346213842, 0, 68, 140737329653369, 140737331174528, 0, 0, 92, 140737488278672, 140737349704416, 0, 140737348426833}}
#5 0x00007ffff6842520 in <signal handler called> () at /lib/x86_64-linux-gnu/libc.so.6
#6 0x00007ffff687fbd2 in __GI__IO_fread (buf=0x7fffffffda7c, size=1, count=4, fp=0xffffffffffff) at ./libio/iofread.c:37
_IO_acquire_lock_file = <optimized out>
bytes_requested = 4
bytes_read = <optimized out>
#7 0x000055555555ef7a in ani_load_image (file=0x5555555c8e40, load_thumb=1, width=0x7fffffffdb2c, height=0x7fffffffdb28, error=0x7fffffffdb20) at ../plug-ins/file-ico/ico-load.c:797
fp = 0xffffffffffff
image = 0x0
parasite = 0x0
id = "\000\000\000"
size = 0
padding = 0 '\000'
file_offset = 0
frame = 0
header = {bSizeOf = 0, frames = 0, steps = 0, x = 0, y = 0, bpp = 0, planes = 0, jif_rate = 0, flags = 0}
inam = "\000", '\220' <repeats 32717 times>...
iart = '\000' <repeats 32766 times>
str = 0x0
#8 0x0000ffffffffffff in ()
#9 0x00007fffffffdb50 in ()
#10 0x0000000000000000 in ()
[Inferior 1 (process 34677) detached]