Headline
CVE-2021-44648: (CVE-2021-44648) GdkPixbuf Heap Buffer Overflow in lzw_decoder_new (#136) · Issues · GNOME / gdk-pixbuf · GitLab
GNOME gdk-pixbuf 2.42.6 is vulnerable to a heap-buffer overflow vulnerability when decoding the lzw compressed stream of image data in GIF files with lzw minimum code size equals to 12.
Summary
The GdkPixbuf library is vulnerable to heap-buffer overflow vulnerability when decoding the lzw compressed stream of image data in GIF files with lzw minimum code size equals to 12.
Technical Analysis
When parsing the Image Data section of GIF files with lzw minimum code size equals to 12, the library miscalculates the maximum indexes the LZWDecoder->code_table can have, overwriting the LZWDecoder->code_table_size in _LZWDecoder structure.
In the following code snippets, we can observe that the LZW_CODE_MAX is set to 12 at line:28 in gdk-pixbuf/lzw.h which when left shifted at line:22 in gdk-pixbuf/lzw.c will yield MAX_CODES value to 4096. This means that the LZW decoding operations ideally should always write to maximum 4096 indexes of LZWDecoder->code_table.
file gdk-pixbif/lzw.h
27:/* Maximum code size in bits */
28:#define LZW_CODE_MAX 12
file: gdk-pixbuf/lzw.c
21:/* Maximum number of codes */
22:#define MAX_CODES (1 << LZW_CODE_MAX)
23:
24:typedef struct
25:{
26: /* Last index this code represents */
27: guint8 index;
28:
29: /* Codeword of previous index or the EOI code if doesn't extend */
30: guint16 extends;
31:} LZWCode;
32:
33:struct _LZWDecoder
34:{
35: GObject parent_instance;
36:
37: /* Initial code size */
38: int min_code_size;
39:
40: /* Current code size */
41: int code_size;
42:
43: /* Code table and special codes */
44: int clear_code;
45: int eoi_code;
46: LZWCode code_table[MAX_CODES];
47: int code_table_size;
48:
49: /* Current code being assembled */
50: int code;
51: int code_bits;
52:
53: /* Last code processed */
54: int last_code;
55:};
The following code snippets shows that the library first initializes the lzw_decoder object by calling lzw_decoder_new function wih the user controlled frame->lzw_code_size value at line:339 in gdk-pixbuf/io-gif-animation.c.
When the value of frame->lzw_code_size is set to 12 (0xc), the lzw_decoder_new gets called with the code_size value equals to 13, which ultimately sets the value of self->eoi_code (end of information) to 4097 at line:131 in gdk-pixbuf/lzw.c, thus writing the values to overall 4098 indexes instead of predefined 4096 at line:133-137 in gdk-pixbuf/lzw.c.
file: gdk-pixbuf/io-gif-animation.c
317:static void
318:composite_frame (GdkPixbufGifAnim *anim, GdkPixbufFrame *frame)
319:{
320: LZWDecoder *lzw_decoder = NULL;
.
.
.
338:
339: lzw_decoder = lzw_decoder_new (frame->lzw_code_size + 1);
340: index_buffer = g_new (guint8, frame->width * frame->height);
file: gdk-pixbuf/lzw.c
118:LZWDecoder *
119:lzw_decoder_new (guint8 code_size)
120:{
121: LZWDecoder *self;
122: int i;
123:
124: self = g_object_new (lzw_decoder_get_type (), NULL);
125:
126: self->min_code_size = code_size;
127: self->code_size = code_size;
128:
129: /* Add special clear and end of information codes */
130: self->clear_code = 1 << (code_size - 1);
131: self->eoi_code = self->clear_code + 1;
132:
133: for (i = 0; i <= self->eoi_code; i++) {
134: self->code_table[i].index = i;
135: self->code_table[i].extends = self->eoi_code;
136: self->code_table_size++;
137: }
138:
139: /* Start with an empty codeword following an implicit clear codeword */
140: self->code = 0;
141: self->last_code = self->clear_code;
142:
143: return self;
144:}
Dynamic Analysis
With the following gdb output, we can confirm that the lzw_decoder_new function writes to two more indexes than predefined 4096 indexes of LZWDecoder->code_table and thus overwrites the value of LZWDecoder->code_table_size from 4096 to 268505089.
$43 = 4096
$44 = (int *) 0x629000018228
0x629000018228: 0x0000000000001000
$45 = 4096
Breakpoint 4, lzw_decoder_new (code_size=13 '\r') at ../gdk-pixbuf/lzw.c:134
134 self->code_table[i].index = i;
(gdb) c
Continuing.
$46 = 4097
$47 = (int *) 0x629000018228
0x629000018228: 0x0000000010011001
$48 = 268505089
Breakpoint 4, lzw_decoder_new (code_size=13 '\r') at ../gdk-pixbuf/lzw.c:134
134 self->code_table[i].index = i;
(gdb) c
Continuing.
$49 = 4098
$50 = (int *) 0x629000018228
0x629000018228: 0x1001000110011002
$51 = 268505090
Breakpoint 7, lzw_decoder_new (code_size=13 '\r') at ../gdk-pixbuf/lzw.c:140
140 self->code = 0;
Steps to reproduce
- Compile the gdk-pixbuf with address sanitizer.
- Execute the gdk-pixbuf/gdk-pixbuf-pixdata binary with the accompanied heap-buffer-overflow.gif file as follows and observed the ASAN dump:
command:
./gdk-pixbuf/gdk-pixbuf-pixdata ../../gdk-pixbuf/poc/heap_buffer_overflow.gif /tmp/bbbb
ASAN Dump:
==5565==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62900001336a at pc 0x00000053b142 bp 0x7fffc5dd1200 sp 0x7fffc5dd11f8
READ of size 2 at 0x62900001336a thread T0
#0 0x53b141 in write_indexes /root/gdk-pixbuf-poc/_build/../gdk-pixbuf/lzw.c:88:36
#1 0x53a8d9 in lzw_decoder_feed /root/gdk-pixbuf-poc/_build/../gdk-pixbuf/lzw.c:212:46
#2 0x5374c1 in composite_frame /root/gdk-pixbuf-poc/_build/../gdk-pixbuf/io-gif-animation.c:364:21
#3 0x5351ed in gdk_pixbuf_gif_anim_iter_get_pixbuf /root/gdk-pixbuf-poc/_build/../gdk-pixbuf/io-gif-animation.c:421:17
#4 0x5347bf in gdk_pixbuf_gif_anim_get_static_image /root/gdk-pixbuf-poc/_build/../gdk-pixbuf/io-gif-animation.c:117:16
#5 0x58e217 in gdk_pixbuf_animation_get_static_image /root/gdk-pixbuf-poc/_build/../gdk-pixbuf/gdk-pixbuf-animation.c:586:16
#6 0x532d08 in gif_get_lzw /root/gdk-pixbuf-poc/_build/../gdk-pixbuf/io-gif.c:522:24
#7 0x52caf7 in gif_main_loop /root/gdk-pixbuf-poc/_build/../gdk-pixbuf/io-gif.c:821:13
#8 0x52b370 in gdk_pixbuf__gif_image_load_increment /root/gdk-pixbuf-poc/_build/../gdk-pixbuf/io-gif.c:1013:11
#9 0x503404 in gdk_pixbuf_loader_load_module /root/gdk-pixbuf-poc/_build/../gdk-pixbuf/gdk-pixbuf-loader.c:467:16
#10 0x502127 in gdk_pixbuf_loader_close /root/gdk-pixbuf-poc/_build/../gdk-pixbuf/gdk-pixbuf-loader.c:835:25
#11 0x581dbb in gdk_pixbuf__qtif_image_load /root/gdk-pixbuf-poc/_build/../gdk-pixbuf/io-qtif.c:217:21
#12 0x4f47ec in _gdk_pixbuf_generic_image_load /root/gdk-pixbuf-poc/_build/../gdk-pixbuf/gdk-pixbuf-io.c:1064:26
#13 0x4f502b in gdk_pixbuf_new_from_file /root/gdk-pixbuf-poc/_build/../gdk-pixbuf/gdk-pixbuf-io.c:1135:18
#14 0x4efee7 in main /root/gdk-pixbuf-poc/_build/../gdk-pixbuf/gdk-pixbuf-pixdata.c:77:12
#15 0x7fe6f0e7582f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#16 0x41e518 in _start (/root/gdk-pixbuf-poc/_build/gdk-pixbuf/gdk-pixbuf-pixdata+0x41e518)
0x62900001336a is located 306 bytes to the right of 16440-byte region [0x62900000f200,0x629000013238)
allocated by thread T0 here:
#0 0x4be648 in malloc (/root/gdk-pixbuf-poc/_build/gdk-pixbuf/gdk-pixbuf-pixdata+0x4be648)
#1 0x7fe6f297e7b8 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x4f7b8)
SUMMARY: AddressSanitizer: heap-buffer-overflow /root/gdk-pixbuf-poc/_build/../gdk-pixbuf/lzw.c:88:36 in write_indexes
Shadow bytes around the buggy address:
0x0c527fffa610: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c527fffa620: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c527fffa630: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c527fffa640: 00 00 00 00 00 00 00 fa fa fa fa fa fa fa fa fa
0x0c527fffa650: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c527fffa660: fa fa fa fa fa fa fa fa fa fa fa fa fa[fa]fa fa
0x0c527fffa670: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c527fffa680: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c527fffa690: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c527fffa6a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c527fffa6b0: 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
Heap right redzone: fb
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
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
==5565==ABORTING
Credits
[email protected] (xen1thlabs)
POC File
Related news
An update for gdk-pixbuf2 is now available for Red Hat Enterprise Linux 9. Red Hat Product Security has rated this update as having a security impact of Moderate. A Common Vulnerability Scoring System (CVSS) base score, which gives a detailed severity rating, is available for each vulnerability from the CVE link(s) in the References section.This content is licensed under the Creative Commons Attribution 4.0 International License (https://creativecommons.org/licenses/by/4.0/). If you distribute this content, or a modified version of it, you must provide attribution to Red Hat Inc. and provide a link to the original. Related CVEs: * CVE-2021-44648: A flaw was found in gdk-pixbuf. The vulnerability occurs due to the index overwriting in the lzw_decoder_new function, leading to a heap buffer overflow. This flaw allows an attacker to input a specially crafted GIF file, leading to a crash or code execution. * CVE-2021-46829: A heap-based buffer overflow vulnerability was found in GNOME GdkP...
Ubuntu Security Notice 5607-1 - It was discovered that GDK-PixBuf incorrectly handled certain images. An attacker could possibly use this issue to execute arbitrary code or cause a crash.