MS05-043漏洞利用分析以及ntdll!RtlFreeHeap中关于lookaside链表的操作

环境:
windows2000 sp4 Rollup1
使用工具:
IDA v4.7
softice
这个漏洞出问题的代码是spoolsv.exe静态加载spoolss.dll,由spoolss.dll动态加载win32spl.dll中的函数:AddPrinterW
具体可以参见:
http://blog.0x557.org/Sandro/archives/0day_develop/index.html
(同时表示对作者 Sandro 的感谢)
首先反汇编了win32spl.dll:
.text:769F7C01          push    207h
.text:769F7C06          call    DllAllocSplMem
.text:769F7C0B          mov     edi, eax
.text:769F7C0D          cmp     edi, ebx
.text:769F7C0F          jz      short _text_769F7C38
.text:769F7C11          push    dword ptr [esi+4]
.text:769F7C14          push    [ebp+arg_0]
.text:769F7C17          push    offset aWsWs          ; LPCWSTR
.text:769F7C1C          push    edi                   ; LPWSTR
.text:769F7C1D          call    ds:wsprintfW      ;overflow
.text:769F7C23          add     esp, 10h
.text:769F7C26          push    edi
.text:769F7C27          call    AllocSplStr            ;这次分配先检查了字符串总长度,不可能再溢出。
.text:769F7C2C          mov     [ebp+var_278], eax
.text:769F7C32          push    edi
.text:769F7C33          call    DllFreeSplMem
实际调试过程中,一开始并没有能在内存中找到这段代码。
尝试远程使用添加打印机,仍然不能保证这段代码的出现(时有时无),因此判断是动态加载后有一个卸载的动作,条件不明。

这个漏洞的触发和039的方式一致,是一个RPC调用的结果。应该是只有2000可以直接利用的。
构造恶意数据包远程调用AddPrinter,经实验,这样可以保证win32spl.dll的加载。
溢出点是wsprintfW函数的调用。被格式化输出的第三个参数是severname,第四个参数是printername。其中printername作了长度检测,severname未作长度检测。可以用来构造超长数据。因为wsprintfW最多输出1024个长字符,所以能利用的空间最多有2048个字节。

参考网上的一些堆溢出资料上介绍的方法(Leven的《总结windows下堆溢出的三种利用方式》等……),作了多次实验,没有能触发任何异常。程序就像打了补丁一样健壮的运行着……

仔细分析了DllAllocSplMem所分配的块,发现其前后紧密地结合着一些已分配出去的忙块。后面覆盖的块大多是一些小块,存储着一些字符串,多为打印机的驱动程序名。多次实验,没有出现过别的情况。
感觉是这次分配后剩下的再分配和释放堆的过程中,都没有一个写任意四字节的机会。似乎跟lookaside这个结构相关比较大(参考《网络渗透技术》page293)。而在反汇编块分配所用到的ntdll中的RtlAllocateHeap、RtlFreeHeap时,发现了在关键流程上的两次比较:
cmp     byte ptr [edi+586h], 2
cmp     byte ptr [edi+586h], 1
此时,edi的值是当前堆的HEAP_ENTRY地址。而偏移0x586处的信息在各种资料上都没有找到,比如《网络渗透技术》page291就只有:
/*+0x580*/ PVOID LookAside;
/*+0x584*/ USHORT LookasideLockCount;
/*+0x588*/ HEAP_UNCOMMITED_RANGE UnCommittedRanges[8];
唯独漏掉了这个两字节的信息。不知是否有人知道含义……
在能找到的资料中,关于lookaside的介绍不多,能获得的信息是:大于等8小于1024字节的堆块再分配和释放的时候首先会从lookaside里操作。顺便提一下,《网络渗透技术》p293页,说“总共是0x1800字节加上一个8字节的头部结构。”似乎描述得不是很准确。8字节的头部结构,很模糊的概念。

结合调试过程对释放堆块时对lookaside进行的操作进行一下分析。大家看看这个溢出还有没有利用的可能。

分析中提到的实际值都是在这次调试过程中的内存信息。经多次实验一致。
先分析了函数RtlFreeHeap。用IDA反汇编了版本号5.00.2195.7006的ntdll.dll。
在函数入口处,每个堆块释放时都要做的比较:
PAGE:77FCAD44    cmp     byte ptr [edi+586h], 2
edi的值是当前堆的HEAP_ENTRY地址。[edi+586h]实际值为1。
(如果是2,检查堆块头部的SegmentIndex,如果为0xFF会有一些操作。这里不是很清楚,因为没见过这个情况,没继续分析。不知道是不是跟调试堆有关)
然后是对堆块的释放符号和堆的ForceFlags作比较。
PAGE:77FCB737     mov     eax, [ebp+arg_4]
PAGE:77FCB73A     or      eax, [edi+10h]
PAGE:77FCB73D     mov     [ebp+arg_4], eax
PAGE:77FCB740     test    eax, 7D030F60h        ;实际这里eax == 0;具体意义不是很清楚。
接着检查欲释放块的地址的合法性,忙位是否置1,SegmentIndex是否小于40h,不满足则抛出异常。(?)
检测块标志是否置上HEAP_ENTY_SETTABLE_FLAG1-3,是则直接转入合并算法或是内存释放。

否则进行对[edi+586h]的第二次比较:
PAGE:77FCB781    cmp     byte ptr [edi+586h], 1
PAGE:77FCB788    jnz     short PAGE_77FCB792
PAGE:77FCB78A    mov     ecx, [edi+580h]
PAGE:77FCB790    jmp     short PAGE_77FCB794

当满足条件:
[edi+586h] == 1
LookAside != NULL
LookasudeLockCount == 0
HEAP_ENTRY_VIRTUAL_ALLOC 标志未置
块大小 < 1024字节(传说中的小堆块)
就会将欲释放的堆块加入到lookaside对应位置的链表上去。
否则转入合并算法或是释放内存。
首先利用以下代码算出堆块大小对应的HEAP_LOOKASIDE在堆中的地址:
PAGE:77FCB7B4      lea     eax, [eax+eax*2]  
PAGE:77FCB7B7      shl     eax, 4           ; 相当于*48,HEAP_LOOKASIDE大小为48字节
PAGE:77FCB7BA     add     eax, ecx         ; ecx = LookAside,算出对应的HEAP_LOOKASIDE地址
PAGE:77FCB7BC     push    eax
PAGE:77FCB7BD     call    _text_77FB2C40

这个函数调用对HEAP_LOOKASIDE中的一些数据作一些检测和更改,最终由:
.text:77FBB202       mov     ebp, ecx        ; ecx指向lookaside对应此堆大小的头部的HEAP_LOOKASIDE
.text:77FBB204       mov     ebx, edx        ; edx->ebx要释放的块的数据部分指针
.text:77FBB206       mov     edx, [ebp+4]
.text:77FBB209       mov     eax, [ebp+0]
.text:77FBB20C       mov     [ebx], eax
.text:77FBB20E       mov     ecx, edx
.text:77FBB210       add     ecx, 10001h     ; 给Depth, Sequence都加1。
.text:77FBB216       lock cmpxchg8b qword ptr [ebp+0]   ;头插法加入链表。汗,这个指令以前都没见过……
.text:77FBB21B       jnz     short _text_77FBB20C


在调试过程中,检测到的堆块释放,都是在lookaside上进行的操作。而且被溢出的块更是从lookaside中直接取出,而lookaside位置靠前无法覆盖,溢出后能覆盖的堆块都是存储文件名字符串的小堆块(似乎不会释放),没有空闲块。似乎也就丧失了写任意4字节的机会。望高手指点。

在后来的调试过程中,发现另一个远程调用触发了一个读异常。这个与堆块的释放分配没有关系,看来dos还是可以做的:〉不过,要想进一步利用还要再好好分析一下。有兴趣的来讨论一下。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值