该漏洞是我2010年4月6日晚上,通过自己的IoControl Fuzz工具挖掘的。漏洞存在于超级巡警ASTDriver.sys这个驱动中,影响超级巡警v4 Build0316和以前的版本。利用该漏洞能够实现本地特权提升,进Ring0。 PAGE_FAULT_IN_NONPAGED_AREA (50) Invalid system memory was referenced. This cannot be protected by try-except, it must be protected by a Probe. Typically the address is just plain bad or it is pointing at freed memory. Arguments: Arg1: 89441428, memory referenced. Arg2: 00000001, value 0 = read operation, 1 = write operation. Arg3: f9c7569b, If non-zero, the instruction address which referenced the bad memory address. Arg4: 00000000, (reserved) PROCESS_NAME: ast.exe TRAP_FRAME: f94f1b00 -- (.trap 0xfffffffff94f1b00) ErrCode = 00000002 eax=89441428 ebx=81266840 ecx=89441428 edx=ffa7c2d8 esi=81312da0 edi=811fc230 eip=f9c7569b esp=f94f1b74 ebp=f94f1b90 iopl=0 nv up ei ng nz ac pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010296 ASTDriver+0x169b: f9c7569b c70000000000 mov dword ptr [eax],0 ds:0023:89441428=???????? Resetting default scope STACK_TEXT:...
该漏洞是我2010年4月6日晚上,通过自己的IoControl Fuzz工具挖掘的。漏洞存在于超级巡警ASTDriver.sys这个驱动中,影响超级巡警v4 Build0316和以前的版本。利用该漏洞能够实现本地特权提升,进Ring0。 PAGE_FAULT_IN_NONPAGED_AREA (50) Invalid system memory was referenced. This cannot be protected by try-except, it must be protected by a Probe. Typically the address is just plain bad or it is pointing at freed memory. Arguments: Arg1: 89441428, memory referenced. Arg2: 00000001, value 0 = read operation, 1 = write operation. Arg3: f9c7569b, If non-zero, the instruction address which referenced the bad memory address. Arg4: 00000000, (reserved) PROCESS_NAME: ast.exe TRAP_FRAME: f94f1b00 -- (.trap 0xfffffffff94f1b00) ErrCode = 00000002 eax=89441428 ebx=81266840 ecx=89441428 edx=ffa7c2d8 esi=81312da0 edi=811fc230 eip=f9c7569b esp=f94f1b74 ebp=f94f1b90 iopl=0 nv up ei ng nz ac pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010296 ASTDriver+0x169b: f9c7569b c70000000000 mov dword ptr [eax],0 ds:0023:89441428=???????? Resetting default scope STACK_TEXT: f94f1634 804f9afd 00000003 89441428 00000000 nt!RtlpBreakWithStatusInstruction f94f1680 804fa6e8 00000003 00000000 c044a208 nt!KiBugCheckDebugBreak+0x19 f94f1a60 804fac37 00000050 89441428 00000001 nt!KeBugCheck2+0x574 f94f1a80 80520478 00000050 89441428 00000001 nt!KeBugCheckEx+0x1b f94f1ae8 80544568 00000001 89441428 00000000 nt!MmAccessFault+0x9a8 f94f1ae8 f9c7569b 00000001 89441428 00000000 nt!KiTrap0E+0xd0 WARNING: Stack unwind information not available. Following frames may be wrong. f94f1b90 f9c75184 89441428 5fea3278 50000408 ASTDriver+0x169b f94f1bc4 804efeb1 81286f18 81266840 806e5410 ASTDriver+0x1184 f94f1bd4 8057f688 812668b0 8125fbe8 81266840 nt!IopfCallDriver+0x31 f94f1be8 805804eb 81286f18 81266840 8125fbe8 nt!IopSynchronousServiceTail+0x60 f94f1c84 8057904e 00000928 00000000 00000000 nt!IopXxxControlFile+0x5c5 f94f1cb8 f74ecb12 00000928 00000000 00000000 nt!NtDeviceIoControlFile+0x2a f94f1d34 8054160c 00000928 00000000 00000000 BehaviorMon!HookNtDeviceIoControlFile+0x892 //省略…… 从上面分析中的栈回溯可以看出,问题发生在ASTDriver+0x169b所在的函数中,这个函数是从ASTDriver+0x1184所在的函数调用过来的。因此我们先定位到ASTDriver+0x1184所在的函数,如下所示: signed int __stdcall sub_110D0(int a1, PIRP Irp) { signed int result; // eax@2 int pIrpStack; // [sp+8h] [bp-1Ch]@1 int v4; // [sp+Ch] [bp-18h]@10 int secondDWORD; // [sp+10h] [bp-14h]@3 PVOID systemBuffer; // [sp+18h] [bp-Ch]@1 int firstDWORD; // [sp+1Ch] [bp-8h]@3 int IoControlCode; // [sp+20h] [bp-4h]@3 pIrpStack = (int)Irp->Tail.Overlay.CurrentStackLocation; systemBuffer = Irp->AssociatedIrp.MasterIrp; if ( *(_DWORD *)(pIrpStack + 8) == 16 ) { secondDWORD = *((_DWORD *)systemBuffer + 1); firstDWORD = *(_DWORD *)systemBuffer; IoControlCode = *(_DWORD *)(pIrpStack + 12); switch ( IoControlCode ) { case 0x50000404: v4 = sub_112B0(firstDWORD, secondDWORD, *((PVOID *)systemBuffer + 2), *((_DWORD *)systemBuffer + 3)); break; case 0x50000408: v4 = sub_11690((PVOID)firstDWORD, secondDWORD); break; case 0x5000040C: v4 = sub_11810((PVOID)firstDWORD, secondDWORD); break; } IofCompleteRequest(Irp, 0); result = v4; } else { IofCompleteRequest(Irp, 0); result = 0xC000000Du; } return result; } 该函数实际上就是驱动的派遣函数。当IoControlCode为0x50000408时,会调用sub_11690函数,参数有两个,第一个参数是用户输入缓冲区中的第一个DWORD,第二个参数是用户输入缓冲区的第二个DWORD。从Windbg输出的被随机化的用户输入数据可以看到,这两个 DWORD分别是0x89441428和0x5fea3278,这一点和栈回溯的结果是一致的。 f94f1b90 f9c75184 89441428 5fea3278 50000408 ASTDriver+0x169b 接下来,我们需要分析一下sub_11690函数的内部逻辑, signed int __stdcall sub_11690(PVOID firstDWORD, int secondDWORD) { signed int result; // eax@2 unsigned int v3; // [sp+0h] [bp-1Ch]@6 int v4; // [sp+4h] [bp-18h]@6 int v5; // [sp+14h] [bp-8h]@6 PVOID v6; // [sp+18h] [bp-4h]@15 *(_DWORD *)firstDWORD = 0; if ( secondDWORD == 32 ) { if ( MmIsAddressValid(firstDWORD) && MmIsAddressValid(firstDWORD + secondDWORD - 1) ) { //省略部分代码…… } } return result; } 这个函数有一个致命的错误,函数开头没有对firstDWORD进行任何检查,直接向firstDWORD地址所指向的DWORD赋值为0,而 firstDWORD是我们可以控制的。 至此,该漏洞已经分析完毕。漏洞利用起来也非常简单,只要将要修改的Ring0内存地址放在输入缓冲区的第一个DWORD即可。然后向设备\device \ASTDrivers发送IoControlCode为0x50000408的IoControl。这样便实现了向任意地址写0的作用。 另外,如果进一步研究上面sub_11690函数的内部逻辑,如果不利用“*(_DWORD *)firstDWORD = 0;”这句代码的漏洞,函数中还有其他几处漏洞可以利用,最终实现向任意地址写入任意数据的效果。 超级巡警 <= v4 Build0316 厂商补丁: 超级巡警 -------- 目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载: http://www.sucop.com