libpng汗青破绽剖析 | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

libpng汗青破绽剖析

申博_安全防护 申博 115次浏览 已收录 0个评论

申博网络安全巴士站

申博-网络安全巴士站是一个专注于网络安全、系统安全、互联网安全、信息安全,全新视界的互联网安全新媒体。

————————————-

源码下载

http://78.108.103.11/MIRROR/ftp/png/src/history/libpng12/

测试样本

https://gitee.com/hac425/data/tree/master/libpng

CVE-2004-0597

剖析

破绽代码

void /* PRIVATE */
png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
{
   png_byte readbuf[PNG_MAX_PALETTE_LENGTH]; // 0x100
   ..........................
   ..........................
   png_crc_read(png_ptr, readbuf, (png_size_t)length);
      png_ptr->num_trans = (png_uint_16)length;

readbuf 是一个 0x100字节的缓冲区, length从 png 文件中读取,最大可认为 0x7fffffff , 典范的栈溢出。

测试用例:

trns_stack_bof.png

libpng汗青破绽剖析

修复

对 length举行校验制止大于 PNG_MAX_PALETTE_LENGTH.

CVE-2007-5266

补钉地点

https://sourceforge.net/p/png-mng/mailman/png-mng-implement/thread/5122753600C3E94F87FBDFFCC090D1FF0400EA68@MERCMBX07.na.sas.com/

iCCP 的花样

iccp_name字符串+"\x00" + "\x00" + zlib紧缩后的数据 endata

endata 解压后的花样

profile_size: 4个字节

iCCP chunk 处置惩罚堆越界(基本无影响)

剖析

调试情况

ubuntu 16.04 64bit

测试用例

附件\libpng\iccp_memleak*.png

破绽代码

#if defined(PNG_iCCP_SUPPORTED)
void PNGAPI
png_set_iCCP(png_structp png_ptr, png_infop info_ptr,
             png_charp name, int compression_type,
             png_charp profile, png_uint_32 proflen)
{

   new_iccp_name = (png_charp)png_malloc_warn(png_ptr, png_strlen(name)+1);

   png_strncpy(new_iccp_name, name, png_sizeof(new_iccp_name));  //当 name 大于 8 字节时, strncpy 拷贝字符串不会再末端添0 , 能够内存泄漏

strncpy 的工作为

char* strncpy(char *dest, const char *src, size_t n){
    size_t i;
    for (i = 0 ; i < n && src[i] != '\0' ; i++)
        dest[i] = src[i];
    for ( ; i < n ; i++)
        dest[i] = '\0';
    return dest;
}
  1. 若是src的前n个字符内里没有’\0’,那末它不会在末端补上这个结束符
  2. 若是拷贝的数据不满n个字符,那末它会用 ‘\0’ 在末端添补

破绽是存在的,无影响的原因是在 linux x64 下分派内存的最小数据块为 0x20 , 可用的数据地区为 0x10.而 png_sizeof(new_iccp_name) 的巨细为 8 , 以是不会溢出到其他内存块的数据内里。

Case1

iccp_name 的长度大于 8 时, strncpy 不会再字符串末端填\x00, 背面的运用能够会致使内存数据泄漏(好比分派到的的内存块是位于 unsorted bin 中)。

iccp_memleak1.png

泄漏堆块的指针

libpng汗青破绽剖析

Case2

iccp_name 的长度小于 8 时 , malloc 的巨细会小于 png_sizeof(new_iccp_name) , 这个会形成越界。

iccp_memleak2.png

当 iccp_name 为 k\x00 时, 分派 2 字节

libpng汗青破绽剖析

背面拷贝时会拷贝 8 字节

libpng汗青破绽剖析

修复体式格局

png_strncpy(new_iccp_name, name, png_strlen(new_iccp_name)+1);

总结: strncpy 要警惕运用

sPLT Chunk处置惩罚

剖析

测试用例

附件\libpng\splt.png

sPLT 的数据域的花样

字符串 + '\x00' + entries

entries 的组织

depth, 用来透露表现每一个entry的size: 1字节
entry 数组

entry 的组织

typedef struct png_sPLT_entry_struct
{
   png_uint_16 red;
   png_uint_16 green;
   png_uint_16 blue;
   png_uint_16 alpha;
   png_uint_16 frequency;
} png_sPLT_entry;

照样 strncpy 的运用, 没有 设置 ‘\x00’ 能够会 leak.

png_set_sPLT(png_structp png_ptr,
             png_infop info_ptr, png_sPLT_tp entries, int nentries)
{
to->name = (png_charp)png_malloc_warn(png_ptr,png_strlen(from->name) + 1);
png_strncpy(to->name, from->name, png_strlen(from->name));

假定 from->name 为 8 字节

libpng汗青破绽剖析

修复

png_strncpy(to->name, from->name, png_strlen(from->name)+1);

CVE-2007-5269

通知布告地点

https://sourceforge.net/p/png-mng/mailman/png-mng-implement/thread/3.0.6.32.20071004082318.012a7628@mail.comcast.net/

zTXt

剖析

测试用例

附件\libpng\ztxt.png

破绽代码

void /* PRIVATE */
png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
{

   png_crc_read(png_ptr, (png_bytep)chunkdata, slength);
   if (png_crc_finish(png_ptr, 0))
   {
      png_free(png_ptr, chunkdata);
      return;
   }

   chunkdata[slength] = 0x00;

   for (text = chunkdata; *text; text++)
      /* empty loop */ ;

   /* zTXt must have some text after the chunkdataword */
   if (text == chunkdata + slength - 1)
   {
      png_warning(png_ptr, "Truncated zTXt chunk");
      png_free(png_ptr, chunkdata);
      return;
   }

起首读取 chunkdata , 然后末端填 ‘\x00’, 然后会在 chunkdata 最先地位找字符串

for (text = chunkdata; *text; text++)
      /* empty loop */ ;

背面的推断前提涌现了题目

if (text == chunkdata + slength - 1)

chunkdata 中的字符悉数都不是’\x00‘ 时, text 会即是 chunkdata + slength

libpng汗青破绽剖析

背面就会越界读了。

修复

/* zTXt must have some text after the chunkdataword */
   if (text >= chunkdata + slength - 2)
   {
      png_warning(png_ptr, "Truncated zTXt chunk");
      png_free(png_ptr, chunkdata);
      return;
   }

总结:用 == 号来推断是不是涌现数组越界是不安全的

sCAL

测试用例

附件\libpng\scal.png

剖析

破绽代码

png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
{

   png_crc_read(png_ptr, (png_bytep)buffer, slength);
   buffer[slength] = 0x00; /* null terminate the last string */
   ep = buffer + 1;        /* skip unit byte */
   width = png_strtod(png_ptr, ep, &vp);
   if (*vp)
   {
       png_warning(png_ptr, "malformed width string in sCAL chunk");
       return;
   }
   for (ep = buffer; *ep; ep++)
      /* empty loop */ ;
   ep++;

当 buffer 内里的每一个字符都不是 \x00 时, 末了会实行这一局部代码后, ep 会凌驾分派的内存块的巨细,形成越界接见。

修复

在背面增添校验

if (buffer + slength < ep)
   {
       png_warning(png_ptr, "Truncated sCAL chunk");
#if defined(PNG_FIXED_POINT_SUPPORTED) && \
    !defined(PNG_FLOATING_POINT_SUPPORTED)
       png_free(png_ptr, swidth);
#endif
      png_free(png_ptr, buffer);
       return;
   }

CVE-2008-1382

测试用例

附件\libpng\unknown.png

剖析

破绽代码

void /* PRIVATE */
png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
{
   png_uint_32 skip = 0;

    png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr, length);
    png_ptr->unknown_chunk.size = (png_size_t)length;
    png_crc_read(png_ptr, (png_bytep)png_ptr->unknown_chunk.data, length);

在处置惩罚 unknown范例的 chunk 时, 若是 length0png_malloc会返回0 , 然后背面的代码没有校验png_malloc的返回值直接运用,致使空指针援用。

LLVM初探

0x01 LLVM简介 The LLVM Project is a collection of modular and reusable compiler and toolchain technologies. LLVM是模块化、可重用的编译器以及工具链的集合,有些人把LLVM当成是一个低层的虚拟机(low level virtual machine),但官方给出的解释是这样的: The name “LLVM” itself is not an acronym; it is the full name of the project. 也就是说LLVM并不是一个缩写,而是整个项目的全名。 LLVM和传统的编译器(GCC)是有差别的 传统的编译器架构 传统的编译器架构主要分为三个部分 Frontend:前端 包括词法分析、语法分析、语义分析

修复

length 举行校验

if (length == 0)
         png_ptr->unknown_chunk.data = NULL;
       else
       {
         png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr, length);
         png_crc_read(png_ptr, (png_bytep)png_ptr->unknown_chunk.data, length);
       }

PS: 对1.2.19 用测试样本跑时,会触发栈溢出,溢出在 strncpy 函数内部,很奇异。

CVE-2008-3964

测试用例

ztxt_off_by_one.png

剖析

破绽代码

void /* PRIVATE */
png_push_read_zTXt(png_structp png_ptr, png_infop info_ptr)
{

         if (!(png_ptr->zstream.avail_out) || ret == Z_STREAM_END)
         {
            if (text == NULL)
            {
               text = (png_charp)png_malloc(png_ptr,
                     (png_uint_32)(png_ptr->zbuf_size
                     - png_ptr->zstream.avail_out + key_size + 1));
               png_memcpy(text + key_size, png_ptr->zbuf,
                  png_ptr->zbuf_size - png_ptr->zstream.avail_out);
               png_memcpy(text, key, key_size);
               text_size = key_size + png_ptr->zbuf_size -
                  png_ptr->zstream.avail_out;
               *(text + text_size) = '\0';
            }
            else
            {
               png_charp tmp;

               tmp = text;
               text = (png_charp)png_malloc(png_ptr, text_size +
                  (png_uint_32)(png_ptr->zbuf_size 
                  - png_ptr->zstream.avail_out));
               png_memcpy(text, tmp, text_size);
               png_free(png_ptr, tmp);
               png_memcpy(text + text_size, png_ptr->zbuf,
                  png_ptr->zbuf_size - png_ptr->zstream.avail_out);
               text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out;
               *(text + text_size) = '\0';

分派内存时

png_malloc(png_ptr, text_size +
                  (png_uint_32)(png_ptr->zbuf_size 
                  - png_ptr->zstream.avail_out));

末了一步给解压后的字符串末端赋值时

*(text + text_size) = '\0';

经由过程代码能够晓得

text_size = text_size +
                  (png_uint_32)(png_ptr->zbuf_size 
                  - png_ptr->zstream.avail_out)

典范的单字节数组越界即

buf[buf_length]

分派内存时 ,分派了 0x4006

libpng汗青破绽剖析

末了赋值 \x00 时 , 运用 0x4006作为索引 off-by-one

libpng汗青破绽剖析

这个破绽的样本组织需要让 zTXt 的紧缩数据的巨细大于 0x2000 , 由于zstream.avail_out初始值为 2000.zTXt 的紧缩数据的巨细大于 0x2000 时能力进入破绽分支。

修复

分派的时刻多分派一个字节

tmp = text;
     text = (png_charp)png_malloc(png_ptr, text_size +
     (png_uint_32)(png_ptr->zbuf_size 
     - png_ptr->zstream.avail_out + 1));

CVE-2008-5907

测试样本

iccp_longkeyword.png

剖析

破绽代码

png_size_t /* PRIVATE */
png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key)
{
   key_len = strlen(key);
   ............
   ............
   if (key_len > 79)
   {
      png_warning(png_ptr, "keyword length must be 1 - 79 characters");
      new_key[79] = '\0';  // new_key 是一个指针数组
      key_len = 79;
   }

当 key_len 大于 79时,会运用

new_key[79] = '\0';

往地点写 0 , 注意到 new_key是一个 char**p, 以是上面的代码现实是往一个随机的地位写 8 字节的 0 .

对应的汇编代码

lea     rsi, aKeywordLengthM ; "keyword length must be 1 - 79 character"...
mov     rdi, png_ptr    ; png_ptr
call    _png_warning
mov     qword ptr [new_key+278h], 0  // new_key[79] = '\0'; 
mov     eax, 4Fh ; 'O'
jmp     loc_12237

png_write_tEXt 为例

void /* PRIVATE */
png_write_tEXt(png_structp png_ptr, png_charp key, png_charp text,
   png_size_t text_len)
{
   if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0)
   {
      png_warning(png_ptr, "Empty keyword in tEXt chunk");
      return;
   }

这里 new_key 是一个栈变量 ,当触发破绽时 ,就会往 png_write_tEXt函数栈帧某个地位写8字节 0 。

调试时能够看到往栈内里写 0x000000000000000

libpng汗青破绽剖析

修复

准确运用指针

if (key_len > 79)
   {
      png_warning(png_ptr, "keyword length must be 1 - 79 characters");
      (*new_key)[79] = '\0';
      key_len = 79;
   }

CVE-2009-0040

剖析

破绽代码

png_read_png(png_structp png_ptr, png_infop info_ptr,
                           int transforms,
                           voidp params)
{

      info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr,
         info_ptr->height * png_sizeof(png_bytep));
      for (row = 0; row < (int)info_ptr->height; row++)
      {
         info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr,
            png_get_rowbytes(png_ptr, info_ptr));
      }
   }

这里会分派多个 row_pointer , 当内存不足时 , png_malloc 会运用 longjmp 去释放掉row_pointers数组内的指针,row_pointers 中背面的一些没有初始化的内存地区中的残留数据也有能够会被当作指针而 free

修复

分派内存前,初始化为 0

png_memset(info_ptr->row_pointers, 0, info_ptr->height
         * png_sizeof(png_bytep));
      for (row = 0; row < (int)info_ptr->height; row++)
         info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr,
            png_get_rowbytes(png_ptr, info_ptr));
   }

CVE-2009-5063

剖析

破绽代码

png_write_iCCP(png_structp png_ptr, png_charp name, int compression_type,
   png_charp profile, int profile_len)
{

   png_size_t name_len;
   png_charp new_name;
   compression_state comp;
   int embedded_profile_len = 0;


   if (profile == NULL)
      profile_len = 0;

   if (profile_len > 3)
      embedded_profile_len =
          ((*( (png_bytep)profile    ))<<24) |
          ((*( (png_bytep)profile + 1))<<16) |
          ((*( (png_bytep)profile + 2))<< 8) |
          ((*( (png_bytep)profile + 3))    );

   if (profile_len < embedded_profile_len)
   {
      png_warning(png_ptr,
        "Embedded profile length too large in iCCP chunk");
      return;
   }

   if (profile_len > embedded_profile_len)
   {
      png_warning(png_ptr,
        "Truncating profile to actual length in iCCP chunk");
      profile_len = embedded_profile_len;
   }
    if (profile_len)
      profile_len = png_text_compress(png_ptr, profile,
        (png_size_t)profile_len, PNG_COMPRESSION_TYPE_BASE, &comp);

能够看到这里的 profile_len 和 embedded_profile_len 都是 int 范例, embedded_profile_len从png图片的数据内里掏出,当embedded_profile_len为负数时 好比(0xffffffff) , 终究会进入

profile_len = embedded_profile_len;

以后会将profile_len 传入

profile_len = png_text_compress(png_ptr, profile,
        (png_size_t)profile_len, PNG_COMPRESSION_TYPE_BASE, &comp);

而 png_text_compress 吸收的参数为 png_size_t 即无标记整数,以是会形成越界。

修复

修正范例为 png_size_t.

CVE-2010-1205

处置惩罚 PNG 的 IDAT数据时会发作堆溢出。测试样本

xploit.png

libpng汗青破绽剖析

剖析

处置惩罚PNG 图片中的 IDAT 数据时,会把 IDAT 中的数据一行一行的掏出来生存后,然后举行处置惩罚。顺序在一最先会运用 rpng2_info.height (即IHDR chunk 中的 heigth) 分派一些内存,用来生存每一行的数据。

static void rpng2_x_init(void)
{
    rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height); // 0xaf0
    rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *));// 这里只分派一个指针空间, 由于 heigh 为 1, 并且是 malloc 会有内存残留

libpng汗青破绽剖析

以上图为例,rpng2_info.height1, 起首会分派 rowbytes 的空间用来存储一切的 IDAT 数据, 然后会分派 1 个指针数组 row_pointers , 用来生存指向生存每一行数据的内存地区。个中 rowbytes 是经由过程 IHDR 内里的字段盘算出来的

void __cdecl png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
{

  png_ptr->pixel_depth = png_ptr->channels * png_ptr->bit_depth;// 4*8
  v4 = png_ptr->width * (png_ptr->pixel_depth >> 3);
  png_ptr->rowbytes = v4;                       // 0xaf0

照样上图为例, 终究盘算的效果为 0xaf0. 以后顺序会每次读取 0xaf0数据,然后从 rpng2_info.row_pointers 掏出一个指针, 然后往指针对应的内存空间内里写数据, 直到读取完一切的 IDAT 数据。背面会运用越界的指针举行内存拷贝,致使内存写。

触发越界接见的代码以下:

static void readpng2_row_callback(png_structp png_ptr, png_bytep new_row,
                                  png_uint_32 row_num, int pass)
{
    png_progressive_combine_row(png_ptr, mainprog_ptr->row_pointers[row_num],// row_num 会为1, 而 row_pointers的长度为1, 典范溢出
      new_row);

在溢出前,row_pointers[1] 背面有残留的内存指针,由于 row_pointers 的分派运用的是 malloc ,以是会有内存残留。0x612020 是一个堆上的指针。

libpng汗青破绽剖析

实行终了后会触发堆溢出把堆上的数据给覆盖了。

libpng汗青破绽剖析

总结:分派内存空间时运用的是 png 图片中的字段, 然后现实运用的空间是依据数据长度举行盘算的,二者的不一致致使了破绽。

修复

readpng2_row_callbackrow_num 举行推断。

CVE-2011-2692

剖析

破绽代码

void /* PRIVATE */
png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32
length) {
   png_charp ep;
   ...
   png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1);
   ...
   slength = (png_size_t)length;
   ...
   png_ptr->chunkdata[slength] = 0x00; /* Null terminate the last
   string */

   ep = png_ptr->chunkdata + 1;        /* Skip unit byte */
   ...
   width = png_strtod(png_ptr, ep, &vp);
   ...
   swidth = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1);
  --

length0 时, ep 会涌现越界接见


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明libpng汗青破绽剖析
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址