CVE-2010-0188漏洞初探

一.  TIFF文件格式介绍
      1、  图像文件头(Image File Header简称IFH):
 
           IFH结构描述(图1)
   IFH数据结构包含3个成员共计8个字节,Byte order成员可能是“MM”(0x4d4d)或“II”(0x4949),0x4d4d表示该TIFF图是Motoral整数格式 0x4949表示该图是Intel整数格式;Version成员总是包含十进制42(0x2a);第三个成员是IFD相对文件开始 处的偏移量。
2、图像文件目录(Image File Directory简称IFD):

         IFD及DE结构描述(图2)
IFD是TIF图中最重要的数据结构,它包含了一个TIF文件中最重要的信息,一个TIF图可能有多个IFD,这说明文件中有多个图像,每个IFD标识1个图像的基本属性。 IFD结构中包含了三类成员,Directory Entry Count指出该结构里面有多少个目录入口;接下来就是N个线性排列的DE序列,数量不定(这就是 为什么称TIF格式文件为可扩充标记的文件,甚至用户可以添加自定义的标记属性),每个DE标识了图像的某一个属性;最后就是一个偏移量, 标识下一个文件目录相对于文件开始处的位置,当然,如果该TIF文件只包含了一幅图像,那么就只有一个IFD,显然,这个偏移量就等于0;
3、目录入口(Directory Entry简称DE):
  共12个字节,见图2。简单说,一个DE就是一幅图像的某一个属性。例如图像的大小、分辨率、是否压缩、像素的行列数等。其中:tag成员是该属性的编号,在图像文件目录中,它是按照升序排列的。属性是用数据来表示的,那么type就是代表着该数据的类型,TIF官方指定的有5种数据类型。 type=1就是BYTE类型(8位无标记整数)、type=2是ASCII类型(7位ASCII码加1位二进制0)、type=3是SHORT类型(16位无标记整数)、type=4是LONG 类型(32位无标记整数)、type=5是RATIONAL类型(2个LONG,第一个是分子,第二个是分母)。length成员是数据的数量而不是数据类型的长度。第4个成员valueOffset很重要,它是tag标识的属性代表的变量值相对文件开始处的偏移量。如果变量值占用的空间小于4个字节,那么该值就存放在 valueOffset中即可,没必要再另外指向一个地方了。
二.分析漏洞产生的原因:
     分析样本发现,此漏洞与CVE-2006-3459类似,都是由于DotRange属性引起的。

     DotRange一般为两个值,即DotRange[0]和DotRange[1]。DotRange标签是一个目录项结构,12字节的数据定义了该标签的TAG、数据类型,数据长度以及值偏移。通常情况下,DotRange的目录项结构是这个样子的:
      TAG           Type                  Count             Value/Offset
      0x0150         0x0003/0x0001         0x00000002        0xAAAAAAAA
      DotRange 的TAG值为0x0150,数据类型或为SHORT型,或为BYTE型,数据长度如前所述,不能超过2,而数据偏移则指出了DotRange[0]和 DotRange[1]在文件中的位置。然而,如果我们此时恶意的将DotRange目录项中的Count字段设置为任意大于2的值,那会出现什么情况呢?事实证明,这就是DotRange漏洞利用最原始的想法,Libtiff根据Count的值,读入DotRange数据。如果攻击者精心构造了Count值,并在文件的特殊位置提供了恶意代码的话,那读入的多余数据将覆盖Libtiff的栈结构,引起缓冲区溢出,最终导致shellcode的执行。
  程序执行的流程图:
  
        TIFFReadDirectory()函数在switch-case中,在遇到TIFFTAG_PAGENUMBER、 TIFFTAG_HALFTONEHINTS、TIFFTAG_YCBCRSUBSAMPLING和TIFFTAG_DOTRANGE标签时进入TIFFFetchShortPair()的处理流程。TIFFFetchShortPair ()函数,这里没有判断Count的大小,从而产生了此漏洞。

     安装补丁之后,TIFFFetchShortPair ()增加了判读,当Count小于等于就跳过报错代码继续正常执行,当Count大于2时就报错(unexpect count ……):
    
    TIFFFetchShortPair()其调用的是TIFFFetchShortArray()。正常情况,Count≤2,流程调用的是函数中(dir->tdir_count <= 2)的分支;而恶意时,Count>2,执行流程又转入了TIFFFetchData()。
   TIFFFetchShortArray()函数调用TIFFFetchData ()处:

     在TIFFFetchData()函数中,tif->tif_size 为文件大小,dir->tdir_offset 为DotRange数据的起始位置,cc是数据真实长度,那dir->tdir_offset + cc也就是DotRange数据的结束位置,如果数据的结束位置超过了打开TIFF文件的大小,那也将会输出错误信息。这一点在构造恶意TIFF时非常重要,DotRange标签中的Count值并不是大于2的任意数值都可以,要保证恶意数据的结束位置≤TIFF文件尺寸,否则就算更改了数值,也可能因位置验证错误而覆盖失败。
     TIFFFetchData()函数调用了memcpy()处:

      缓冲区溢出的发生处是 “_TIFFmemcpy(cp, tif->tif_base + dir->tdir_offset, cc);”。_TIFFmemcpy包装了一个memcpy(见图3),将从tif->tif_base + dir->tdir_offset,即DotRange数据起始处开始的cc字节的数据拷贝到cp指定的内存。cc是恶意构造的、验证后的 Count值,而cp指向的正是TIFFFetchShortPair()中的栈变量uint16 v[4]的基地址。                           
      从上面的分析可知,恶意数据覆盖的是TIFFFetchShortPair()的栈帧结构。只要覆盖了TIFFFetchShortPair()的返回地址,让其跳转到我们的shellcode执行就可以想做你想做的任何事了。实际覆盖情况如下图:
     TIFFFetchData()函数执行前堆栈情况:

     TIFFFetchData()函数调用memcpy()函数后的堆栈情况,与上图相互比较,可以看出返回地址被修改了:

   调试到这里,产生了一个疑问:此漏洞和CVE-2006-3459如出一辙,而且libtiff早已通过增加判断修复了此漏洞,为什么Adobe公司当初没有修复此漏洞呢?

上传的附件 poc.rar[解压密码:pediy]