漏洞战争-cve-2012-0158

on under 二进制
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的“无意义残余数据”,从这里看出,
以后不能看到堆栈窗口中下面最近的返回地址将要首先返回,并不一定,还有的可能是最近的返回地址并不是“有意义的返回地
址”,而是之前残余的数据,这样就要看第二个,或者第三个靠近的返回地址了
漏洞分析, 漏洞战争, 栈溢出
home
github
archive
category