漏洞战争-cve-2012-0158
4 minute read
0x00 about
实验环境:
windows xp sp3
office word 2003 sp3
offvis1.1
漏洞描述:
MSCOMCTL.OCX中存在栈溢出漏洞
0x01 相关技巧
poc.doc是rtf格式,里面的ole数据以文本形式存储,offvis无法识别,导致不能通过offvis分析doc的结构,可通过winhex将
0xD0CF11E0开始至结尾的所有看上去是十六进制形式表示的文本数据保存为真正的十六进制值,最后有三个右大括号不属于十六
进制形式,也即将下面图1中poc.doc转成图2中的test.doc
操作步骤:
1>winhex打开poc.doc,选中text区中的D0CF11E0到最后的0000(左边为hex区,右边为text区)
2>单击右键选择edit|copy block|normally
3>新建一个空白文件,20k大小(15k应该也够了)
4>在新建文件的offset为0处右键选择edit|copyboard data|paste,在弹出的对话框中选择最后一种粘贴方式,并保存为
test.doc
图1
图2
2>offvis相关分析中,打开重新生成的test.doc,在分析模块中选择最后一个,如下图3所示,41414141对应的数据在offvis分析
中的对应字段如下图4中所示
图3
图4
0x02 调试分析
打开word,od附加,在word中打开poc.doc,弹出出错框,错误地址为41414141,用上面的方法通过offvis分析找到对应字段为
Data,或者直接双击打开poc.doc,也是直接弹出出错对话框,od没有在eip=0x41414141时捕获异常,但是immunity debugger能够
在eip=0x41414141处中断并退出,可达到和windbg一样的效果(在cve-2011-0104的分析中,windbg可捕获到eip为一个正常地址
时对应的汇编指令为正常的汇编指令,但是该指令中访问了异常内存数据,而这里的情况是eip=41414141,是一个不正常的地址,
这里面没有指令),与此同时,od附加exe时会被系统异常处理程序先捕获eip=0x41414141这个异常,对比od和immunity debugger,
发现od中的调试选项中的异常默认处理情况如下图5,immunity debugger如图6
图5
图6
尝试将od的选项设置成和immunity debugger一样,依然会弹出如下图7中的系统异常对话框,看来od在捕获异常的功能上不如
immunity debugger
图7
重新选择immunity debugger附加打开的word.exe,word.exe打开poc.doc,immunity debugger捕获到word.exe崩溃并得到崩溃时
的数据如下:(esp=121468,ebp=00000000,eip=41414141)
------------------寄存器窗口中--------------------
EAX 00000000
ECX 7C93003D ntdll.7C93003D
EDX 00140608
EBX 09FB0810
ESP 00121468
EBP 00000000
ESI 002100B4
EDI 00000000
EIP 41414141
C 0 ES 0023 32bit 0(FFFFFFFF)
P 1 CS 001B 32bit 0(FFFFFFFF)
A 0 SS 0023 32bit 0(FFFFFFFF)
Z 1 DS 0023 32bit 0(FFFFFFFF)
S 0 FS 003B 32bit 7FFDE000(FFF)
T 0 GS 0000 NULL
D 0
O 0 LastErr ERROR_SUCCESS (00000000)
EFL 00010246 (NO,NB,E,BE,NS,PE,GE,LE)
ST0 empty
ST1 empty
ST2 empty
ST3 empty
ST4 empty
ST5 empty
ST6 empty
ST7 empty
3 2 1 0 E S P U O Z D I
FST 4000 Cond 1 0 0 0 Err 0 0 0 0 0 0 0 0 (EQ)
FCW 127F Prec NEAR,53 Mask 1 1 1 1 1 1
---------------------end--------------------------
-------------------------堆栈窗口中-----------------------------
001213EC 00000001 ...
001213F0 0012F904 ?.
001213F4 7C92E900 .閽| ntdll.7C92E900
001213F8 7C930040 @.搢 ntdll.7C930040
001213FC FFFFFFFF
00121400 7C93003D =.搢 ntdll.7C93003D
00121404 275C8800 .圽' MSCOMCTL.275C8800
00121408 00140000 ...
0012140C 00000000 ....
00121410 08D9B008 百
00121414 00000000 ....
00121418 002100B4 ?!.
0012141C 09FB0810 ?
00121420 00008282 倐..
00121424 00121458 X.
00121428 275C8A0A .奬' MSCOMCTL.275C8A0A
0012142C 00121450 P.
00121430 08D9B008 百
00121434 00008282 倐..
00121438 00000000 ....
0012143C 002100B4 ?!.
00121440 09FB0810 ?
00121444 6A626F43 Cobj
00121448 00000064 d...
0012144C 00008282 倐..
00121450 00000000 ....
00121454 00000000 ....
00121458 00000000 ....
0012145C 41414141 AAAA
00121460 00000000 ....
00121464 00000000 ....
00121468 00000000 .... ------> <崩溃时esp的值为121468>
0012146C 00000000 ....
00121470 00000000 ....
00121474 00000000 ....
00121478 00000000 ....
0012147C 00000000 ....
00121480 00000000 ....
00121484 00000000 ....
00121488 00000000 ....
0012148C 00000000 ....
00121490 00000000 ....
00121494 00000000 ....
00121498 00000000 ....
0012149C 00000000 ....
001214A0 00000000 ....
001214A4 00000000 ....
001214A8 00000000 ....
001214AC 00000000 ....
001214B0 00000000 ....
001214B4 00000000 ....
001214B8 00000000 ....
001214BC 00000000 ....
001214C0 00000000 ....
001214C4 00000000 ....
001214C8 00000000 ....
----------------------------end---------------------------------
esp上面最近的一个返回地址为121428处的275c8a0a,尝试在275c8a0a处单独下断,无法直接下断,需要单独打开
MSCOMCTL.ocx,执行命令:bp 0x275c8a0a,单独打开c:\windows\system32\MSCOMCTL.ocx,ctrl+g:275c8a0a,具体指令如下:
-----------------275c8a0a附近----------------
275C89C7 $ 55 push ebp --------->函数入口点
275C89C8 . 8BEC mov ebp,esp
275C89CA . 83EC 14 sub esp,0x14
275C89CD . 53 push ebx ; MSCOMCTL.<ModuleEntryPoint>
275C89CE . 8B5D 0C mov ebx,dword ptr ss:[ebp+0xC] ; MSCOMCTL.27580000
275C89D1 . 56 push esi
275C89D2 . 57 push edi
275C89D3 . 6A 0C push 0xC
275C89D5 . 8D45 EC lea eax,dword ptr ss:[ebp-0x14]
275C89D8 . 53 push ebx ; MSCOMCTL.<ModuleEntryPoint>
275C89D9 . 50 push eax
275C89DA . E8 8EFDFFFF call MSCOMCTL.275C876D
275C89DF . 83C4 0C add esp,0xC
275C89E2 . 85C0 test eax,eax
275C89E4 . 7C 6C jl short MSCOMCTL.275C8A52
275C89E6 . 817D EC 436F6>cmp dword ptr ss:[ebp-0x14],0x6A626F43
275C89ED . 0F85 92A60000 jnz MSCOMCTL.275D3085
275C89F3 . 837D F4 08 cmp dword ptr ss:[ebp-0xC],0x8
275C89F7 . 0F82 88A60000 jb MSCOMCTL.275D3085
275C89FD . FF75 F4 push dword ptr ss:[ebp-0xC] ; MSCOMCTL.<ModuleEntryPoint>
275C8A00 . 8D45 F8 lea eax,dword ptr ss:[ebp-0x8]
275C8A03 . 53 push ebx ; MSCOMCTL.<ModuleEntryPoint>
275C8A04 . 50 push eax
275C8A05 . E8 63FDFFFF call MSCOMCTL.275C876D
275C8A0A . 8BF0 mov esi,eax
--------------------end----------------------
也即275c8a0a处是一个返回地址(od和immunity debugger的堆栈窗口中显示形如module.xxxxxxxx为某个模块的某个地址的
地方,大多数情况都是函数返回地址,也即在汇编窗口中对应为某条call ...指令的下一句的地址,有少数不是返回地址)
且275c8a0a在函数275c89c7中,bp 0x275c8a0a后重新加载word.exe,并用word.exe打开poc.doc,成功在275c8a0a处中断
,对应数据如下:
-------------------eip--------------------
275C8A05 . E8 63FDFFFF call MSCOMCTL.275C876D
275C8A0A . 8BF0 mov esi,eax ---->eip
-------------------end--------------------
-------------------堆栈窗口------------------
00121428 275C8A0A MSCOMCTL.275C8A0A
0012142C 00121450 ----------->esp
00121430 08D9A008
00121434 00008282
00121438 00000000
0012143C 002100FC
00121440 09FC0810
00121444 6A626F43
00121448 00000064
0012144C 00008282
00121450 00000000
00121454 00000000
00121458 00000000 ------------>ebp
0012145C 41414141
00121460 00000000
00121464 00000000
00121468 00000000
---------------------end---------------------
单步到275c8a56处的retn 0x8处时,eip=275C8A56,esp=12145c,[esp]=41414141
---------------汇编窗口------------
275C8A50 > 8BC6 mov eax,esi
275C8A52 > |5F pop edi
275C8A53 . |5E pop esi
275C8A54 . |5B pop ebx
275C8A55 . |C9 leave
275C8A56 . |C2 0800 retn 0x8 ---> eip=275c8a56
------------------end--------------
-------------堆栈窗口---------------
00121428 275C8A0A MSCOMCTL.275C8A0A
0012142C 00121450
00121430 08D9A008
00121434 00008282
00121438 00000000
0012143C 002100FC
00121440 09FC0810
00121444 6A626F43
00121448 00000064
0012144C 00008282
00121450 00000000
00121454 00000000
00121458 00000000
0012145C 41414141 ---> esp=12145c
00121460 00000000
00121464 00000000
00121468 00000000
---------------end------------------
再单步程序将跑飞到41414141处执行,并无法继续调试
这里分析的方法采用的是根据程序崩溃时堆栈窗口中在崩溃时的esp上方位置的函数返回地址回溯,此例中根据程序崩溃时的esp
上方(addr<esp,addr=121428,esp=121468)的121428处的返回地址275C8A0A回溯到275c8a0a处下断点,275c8a0a处于函数
275C89C7中.
根据崩溃时堆栈窗口中的esp上方的位置的函数返回地址回溯(也即崩溃时esp上方的位置[addr<esp]附近可以找到某
函数返回地址,就像这个例子一样),说明esp上方的函数返回地址已正常经执行(rent完)才去执行到当前崩溃时的esp对应eip的
地方,这样说明崩溃时esp上方的某函数返回地址的值与崩溃时的esp对应的刚执行完的eip(执行本例eip=41414141之前一步的
eip=275C8A56[对应rent 0x8])在汇编窗口中属于同一帧函数中,且某函数返回地址<该eip(本例中为275c8a56),这种情况可以直
接在崩溃时的esp上方的某函数返回地址在汇编窗口中跟随后的下断点,然后单步执行就能看到错误在哪里发生.
一般情况下,retn被覆盖
a.通常是当前函数帧中的一处子函数调用(形如call 子函数)导致当前函数的的返回地址被覆盖
或者说成是
b.当前函数中(子函数中)一处数据复制指令(形如resp ...)导致上一帧函数的返回地址被覆盖
a中的子函数中包括b中的复制指令,b中的上一帧函数对应a中的当前函数,此例中对应情况如下:
---------------------start------------------------
275e7004 push ebp
...
...
275e7015 call 275c89c7
275e701a
275c89c7 push ebp
...
...
275C8A05 call 275c876d
275c8a0a
...
...
275c8a56 retn 0x8
----------------------end-------------------------
275c8a05处的call 275c876d执行完后将覆盖栈中的275e701a为41414141,使得275c8a56处的retn 0x8返回到41414141处执行
在f7跟进275c8a05处的275c876d时,堆栈窗口中数据如下:
-------------start--------------
00121428 |275C8A0A 返回到 MSCOMCTL.275C8A0A 来自 MSCOMCTL.275C876D
0012142C |00121450
00121430 |09FC0810
00121434 |00008282
00121438 |00000000
0012143C |00210ECC
00121440 |09FC0810
00121444 |6A626F43
00121448 |00000064
0012144C |00008282
00121450 |00210FB8
00121454 |275859E4 返回到 MSCOMCTL.275859E4 来自 MSCOMCTL.27585BBA
00121458 ]00121480
0012145C |275E701A 返回到 MSCOMCTL.275E701A 来自 MSCOMCTL.275C89C7
00121460 |00210ECC
00121464 |09FC0810
00121468 |00000000
0012146C |00210EA8
00121470 |08C992F0
00121474 |275AC296 返回到 MSCOMCTL.275AC296 来自 MSCOMCTL.275AC3AC
--------------end---------------
按理来讲,由于进入到275c876d函数帧中时栈中数据如上所示,一般情况下是在275c876d函数中执行到retn语句前,275c876d中
的其他指令会覆盖堆栈窗口中的离00121428处的275c8a0a最近的下面的一个返回地址,也即121454处的275859e4,因为从堆栈窗
口中看,进入275c876d函数之后,下面最近的返回地址将是返回到275c8a0a之后在当前275c876d函数的上一帧函数中的retn指
令将要返回的地址,也即从堆栈窗口中来看,275c8a56处的retn 0x8应该是要返回到121454处的275859e4,但是实际上并不是这
样,实际是275c8a56处的retn 0x8将要返回到上面堆栈窗口中的离121428处的275c8a0a下面第二近的12145c处的275e701a,因为
在275c89c7函数帧中开头一般会有一个抬高栈空间的动作(sub esp,0x..),这个抬高动作正好把00121454中的275859e4“拉进”
了275c89c7函数帧的栈空间,也即121454处的275859e4返回地址属于之前没有执行275c89c7的“无意义残余数据”,从这里看出,
以后不能看到堆栈窗口中下面最近的返回地址将要首先返回,并不一定,还有的可能是最近的返回地址并不是“有意义的返回地
址”,而是之前残余的数据,这样就要看第二个,或者第三个靠近的返回地址了