分析研究报告
漏洞介绍:
适用环境:
全补丁 xp, 2003系统
漏洞发现时间:2013-3-4
漏洞起因:
ImagePath 项读取的长度未做检查和限定,导致后面的复制有问题
影响系统:xp, 2003
危害
本地拒绝服务
攻击所需条件: 填充注册表ImagePath 修改成REG_MULTI_SZ, 填充数据 的长度
Len(DWORD 类型) 满足:Len > = 65536
一.漏洞poc的构造(poc.exe)
创建一个服务:xxxx2, 删除原有的ImagePath 键值,新建 REG_MULTI_SZ类型的ImagePath,填充 Len = 65536 ,内容为:
pBuffer[65536] = {‘\’,0,’A’.....后面全部是A};
二.漏洞分析(xp sp3 英文版)
05 f8af9bd8 8057e40e nt!KiTrap0E+0xd0
06 f8af9c80 80580f31 nt!IopBuildFullDriverPath+0xf6
07 f8af9d54 80581487 nt!IopLoadDriver+0x227
08 f8af9d7c 8053876d nt!IopLoadUnloadDriver+0x45
09 f8af9dac 805cff64 nt!ExpWorkerThread+0xef
0a f8af9ddc 805460de nt!PspSystemThreadStartup+0x34
0b 00000000 00000000 nt!KiThreadStartup+0x16
蓝屏时候的堆栈,一步步跟踪后,重点从IopBuildFullDriverPath开始下断点,看实际的系统操作,最终会调用(从调用堆栈就可以看出)
PAGE:805A5B8B lea eax, [ebp+Destination]
PAGE:805A5B8E push eax ; nDestLen
PAGE:805A5B8F push [ebp+BugCheckParameter1] ; KeyHandle
PAGE:805A5B92 lea eax, [ebp+pusDriverName]
PAGE:805A5B95 push eax ; pusDriverName
PAGE:805A5B96 call _IopBuildFullDriverPath@12 ; IopBuildFullDriverPath(x,x,x)
重点函数体在此:
此函数声明:
int __stdcall IopBuildFullDriverPath(PUNICODE_STRING pusDriverName, HANDLE KeyHandle, int nDestLen)
进入此函数内部:
直接调用IopGetRegistryValue 获取ImagePath 的KEY_VALUE_FULL_INFORMATION ,通过第三个参数返回结构体信息:
此结构体如下:
typedef struct _KEY_VALUE_FULL_INFORMATION
{
ULONG TitleIndex;
ULONG Type;
ULONG DataOffset;
ULONG DataLength;
ULONG NameLength;
WCHAR Name[1];
} KEY_VALUE_FULL_INFORMATION, *PKEY_VALUE_FULL_INFORMATION;
PAGE:004CF132 mov edi, edi
PAGE:004CF134 push ebp
PAGE:004CF135 mov ebp, esp
PAGE:004CF137 sub esp, 10h
PAGE:004CF13A push ebx
PAGE:004CF13B mov ebx, [ebp+arg_8]
PAGE:004CF13E push esi
PAGE:004CF13F push edi
PAGE:004CF140 lea eax, [ebp+pOutKeyFullInFormation]
PAGE:004CF143 push eax ; int
PAGE:004CF144 xor esi, esi
PAGE:004CF146 push offset word_4CF242 ; ImagePath:
PAGE:004CF14B push [ebp+ulLenWithoutNull] ; KeyHandle
PAGE:004CF14E mov [ebx+2], si
PAGE:004CF152 mov [ebx], si
PAGE:004CF155 mov [ebx+4], esi
PAGE:004CF158 mov [ebp+arg_8], esi
PAGE:004CF15B mov [ebp+var_8], esi
PAGE:004CF15E mov [ebp+pOutKeyFullInFormation], esi
PAGE:004CF161 call IopGetRegistryValue@12 ; IopGetRegistryValue(x,x,x)
PAGE:004CF166 test eax, eax
PAGE:004CF168 jl loc_4DE606
PAGE:004CF16E mov ecx, [ebp+pOutKeyFullInFormation] ; ecx = pOutInformation ;结构体输出 基地址0x81fbe00
PAGE:004CF171 mov eax, [ecx+_KEY_VALUE_FULL_INFORMATION.DataLength] ; eax = 0x10000 ,填充的Name 数组就是这么大
PAGE:004CF174 cmp eax, esi
PAGE:004CF176 jz loc_4DE606
PAGE:004CF17C mov esi, [ecx+_KEY_VALUE_FULL_INFORMATION.DataOffset]
PAGE:004CF17F add esi, ecx ; esi = 0x81d16028
PAGE:004CF181 add eax, 0FFFFFFFEh ; eax = eax - 2结果等于0xfffe
PAGE:004CF184 cmp word ptr [esi], 5Ch ; pValue[0] = L’\’
PAGE:004CF188 mov [ebp+ulLenWithoutNull], eax ; 这是Name字段的长度 不加后面的空(宽字符原因)大小为0xfffe
PAGE:004CF18B mov [ebp+pValue], esi;指向Name起始地址
PAGE:004CF18E jz short go1 ;第一个字符为 L’\’ 直接跳走
---
/***
执行查询ImagePath 的KEY_VALUE_FULL_INFORMATION
内存分布:
kd> db 81fbe000
81fbe000 00 00 00 00 07 00 00 00-28 00 00 00 00 00 01 00 ........(.......
81fbe010 12 00 00 00 49 00 6d 00-61 00 67 00 65 00 50 00 ....I.m.a.g.e.P.
81fbe020 61 00 74 00 68 00 29 10-5c 00 41 41 41 41 41 41 a.t.h.)..AAAAAA
81fbe030 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
81fbe040 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
81fbe050 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
81fbe060 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
81fbe070 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
前面是 结构体各个字段的信息,后面是 我们在ImagePath 里面填充的字符 “ AAAAA....”
Ebx 下面是一个局部结构体变量 ,逆向后其结构为:
00000000 _MEM_INFOR struc ; (sizeof=0x8)
00000000 wDataLen dw ? //字符长度,不带null
00000002 wLen dw ? // 整个mem 大小
00000004 pDstMem dd ? // 指针 ,指向开辟的空间
00000008 _MEM_INFOR ends
***/
PAGE:004CF19E go1:
PAGE:004CF19E mov ecx, [ebp+ulLenWithoutNull] ;ecx 为Name字段的长度 - 2
PAGE:004CF1A1 mov eax, [ebp+var_8] ; eax = 0
PAGE:004CF1A4 mov edi, [ebp+arg_8] ; edi = 0
PAGE:004CF1A7 add eax, ecx ;eax = ecx
PAGE:004CF1A9 lea eax, [edi+eax+2] ; eax= eax +2,恢复整体长度 ,就是Name的totalLen = 0x10000
PAGE:004CF1AD mov [ebx+_MEM_INFOR.wLen], ax ; Loword(eax) eax 为整体的长度,此时MemInfor.wLen 等于 原始Name长度的低位值,这里poc 调试LOWORD(eax) = 0
PAGE:004CF1B1 movzx eax, ax
PAGE:004CF1B4 push 20206F49h ; Tag
PAGE:004CF1B9 push eax ; NumberOfBytes ;size为0
PAGE:004CF1BA push 1 ; PoolType
PAGE:004CF1BC call _ExAllocatePoolWithTag@12 ; ExAllocatePoolWithTag(x,x,x) ;开辟的长度 为外部输入Name长度的低4位值,这里就明显没有根据了,可能没有他认为我们不可能输入这么长的字符数,这里取低位以后长度大小为 0
PAGE:004CF1C1 test eax, eax
PAGE:004CF1C3 mov [ebx+_MEM_INFOR.pDstMem], eax;存放指针
0xe1a79708
PAGE:004CF1C6 jz loc_519E9C
PAGE:004CF1CC mov cx, [ebx+_MEM_INFOR.wLen] ;为 0
PAGE:004CF1D0 sub cx, 2 ;0 -2 = 0xfffe
PAGE:004CF1D4 test edi, edi ;edi 一直为零
PAGE:004CF1D6 mov [ebx+_MEM_INFOR.wDataLen], cx ; 实际能放的字符长度0xfffe ,这里已经错了
PAGE:004CF1D9 jz short go2 ; 肯定跳转 edi = 0
/******
此时 pMemInforDst 的情况为:
kd> dd ebx
f8af9cf8 0000fffe e1a79708
wDataLen dw 0xfffe //允许存放的空间大小
wLen dw 0x0 //开辟的长度 为0
pDstMem dd 0xe1a79708
*******/
PAGE:004CF1F3 go2: ; CODE XREF: IopBuildFullDriverPath(x,x,x)+A7j
PAGE:004CF1F3 mov ecx, [ebp+ulLenWithoutNull]
PAGE:004CF1F6 test ecx, ecx
PAGE:004CF1F8 jz short loc_4CF20E
PAGE:004CF1FA mov edi, [ebx+_MEM_INFOR.pDstMem]
PAGE:004CF1FD add edi, [ebp+arg_8] ; arg8 = 0
PAGE:004CF200 mov eax, ecx
PAGE:004CF202 shr ecx, 2 ; ecx = ecx /4 这里就使用有问题了,它使用的是esi的size = 0x10000,而此时复制的目的DstMem 的size = 0(他是取得低四位数值)
PAGE:004CF205 rep movsd
;esi : pNameValue edi:pDstMem ,(ecx = 0x3ffff> (DstMem = 0) 开辟空间的大小 ,内存复制大小使用出错,导致蓝屏
至此 ,分析完毕