本文作者:G1ace@四葉草安全實驗室

1、漏洞簡介

漏洞公告相關鏈接:

ZDI-16-453 CVE-2016-3308

CloverSec Labs成員bear13oy在七月中旬也發現該內核漏洞,由于當時比較忙,本想在八月微軟的補丁周過后再對其進行分析和利用,不巧的是微軟八月份的補丁修復了該漏洞。再次撞洞!

附上當時精簡后的poc代碼截圖:

內核崩潰后棧回溯信息截圖:

2、漏洞原理

由于函數win32k!xxxInsertMenuItem在處理新增菜單插入時,獲得了錯誤的插入偏移,導致函數在計算MenuItem內核數據長度時發生錯誤,進而在調用memmove移動MenuItem數據時,發生越界操作。

win32k!xxxInsertMenuItem函數在調用memmove移動MenuItem內核數據前,兩次調用函數win32k!MNLookUpItem來獲取相關Item數據的內存地址。在此漏洞中,第一次調用win32k!MNLookUpItem時,由于傳入的wIndex不存在,函數會返回NULL,于是win32k!xxxInsertMenuItem會默認把新Item數據放到原Item數組的末尾,并把wIndex更新為新Item數據在原Item數組中的偏移。相關代碼如下所示:

    if (w == MFMWFP_NOITEM)
        w = pMenu->cItems;
    w--;
    pItemWalk = pMenu->rgItems + w;
#ifdef MEMPHIS_MENUS
    while (w && (pItemWalk->hbmp) && (pItemWalk->hbmp < (HBITMAP)MENUHBM_MAX))
#else // MEMPHIS_MENUS
    while (w && (pItemWalk->fType & MFT_BITMAP) && (pItemWalk->hTypeData < (HANDLE)MENUHBM_MAX))
#endif // MEMPHIS_MENUS
    {
        wIndex = w--;
        pItemWalk--;
    }

在調用AppendMenuA函數后,Menu中的Item數據剛好達到了8個,而由下面的代碼可以看出,內核中Item數據的分配剛好是以8個Item為分配粒度的。于是如下代碼中的pMenu->cItems >= pMenu->cAlloced的條件正好成立,于是win32k!MNLookUpItem會被再次調用,在新分配的Item數據中通過wIndex偏移找到需要插入的內存地址并保存在pItem中。

#define CMENUITEMALLOC 8
...
...
if (pMenu->cItems >= pMenu->cAlloced) {
    if (pMenu->rgItems) {
        pNewItems = (PITEM)DesktopAlloc(pMenu->head.rpdesk->hheapDesktop,
        (pMenu->cAlloced + CMENUITEMALLOC) * sizeof(ITEM));
    ...
    } else {
        pNewItems = (PITEM)DesktopAlloc(pMenu->head.rpdesk->hheapDesktop,
                sizeof(ITEM) * CMENUITEMALLOC);
    }
    ...
    pMenu->cAlloced += CMENUITEMALLOC;
    pMenu->rgItems = pNewItems;
    ...
    /*
     * Now look up the item again since it probably moved when we realloced the
     * memory.
     */
    if (wIndex != MFMWFP_NOITEM)
        pItem = MNLookUpItem(pMenu, wIndex, fByPosition, &pMenuItemIsOn);
}

然而win32k!MNLookUpItem函數會在所有的Item以及子菜單的Item中遞歸尋找,由于之前已經有7個Item數據存在,在調用InsertMenuA函數后wIndex被改寫為7,而此時由于notepad的第一個File的子菜單中的最后一個Item(Exit)的id正好是7,于是win32k!MNLookUpItem函數遍會把此Item的內核地址返回。之后在計算memmove移動長度時便會出現不可預料的情況,并在移動數據時發生訪問異常。

memmove(pItem + 1, pItem, (pMenu->cItems - 1) *
        sizeof(ITEM) - ((char *)pItem - (char *)pMenu->rgItems));
typedef struct tagITEM
{
    UINT fType;
    UINT fState;
    UINT wID;
    struct tagMENU* spSubMenu; /* Pop-up menu. */
    HANDLE hbmpChecked;
    HANDLE hbmpUnchecked;
    USHORT* lpstr; /* Item text pointer. */
    ULONG cch;
    DWORD_PTR dwItemData;
    ULONG xItem;   /* Item position. left */
    ULONG yItem;   /*     "          top */
    ULONG cxItem;  /* Item Size Width */
    ULONG cyItem;  /*     "     Height */
    ULONG dxTab;   /* X position of text after Tab */
    ULONG ulX;     /* underline.. start position */
    ULONG ulWidth; /* underline.. width */
    HBITMAP hbmp;  /* bitmap */
    INT cxBmp;     /* Width Maximum size of the bitmap items in MIIM_BITMAP state */
    INT cyBmp;     /* Height " */
} ITEM, *PITEM;

從上面代碼可以看出,win32k!xxxInsertMenuItem中觸發漏洞的memmove所用到的pItem地址正好是之前win32k!MNLookUpItem函數返回的內存地址。結合內存數據和PITEM數據結構可以看出,此時的pItem地址所指向的是notepad中File的子菜單中的最后一個Item(Exit)(數據結構如上圖紅色所示),而pMenu->rgItems為DesktopAlloc新分配的內存地址指針,這兩個地址分別屬于不同的Item數組中,所以相減的值是不可預料的,精確操作內核堆分配可控制memmove長度,從而控制特定的內存區域。

3、漏洞利用

漏洞利用主要用到兩個數據結構如下:

typedef struct tagPROPLIST {
        UINT cEntries;
        UINT iFirstFree;
        tagPROP aprop[1];
} PROPLIST, *PPROPLIST;
typedef struct tagMENU
{
    ...
    UINT cItems;              /* Number of items in the menu \*/
    ...
    PITEM rgItems;            /* Array of menu items \*/
    ...
} MENU, *PMENU;

通過構造數據覆蓋tagPROPLIST中的cEntries和iFirstFree便可以實現對tagPROPLIST結構之后的一段數據進行控制。

漏洞觸發過程中tagPROPLIST在內存中的變化:

漏洞觸發前
fce2f240  00000002 00000002 fb10afe0 0001c033   tagPROPLIST
fce2f250  00000000 0002c04a 00010002 08000004
fce2f260  00410041 00000041 00010002 08000002
fce2f270  00410041 00000041 00010002 08000002
fce2f280  00410041 00000041 00010002 08000002
fce2f290  00410041 00000041 00010002 08000002
fce2f2a0  00410041 00000041 00010002 08000002
fce2f2b0  00410041 00000041 00010002 08000002

漏洞觸發后
fce2f240  08000002 88888888 88888888 88888888   tagPROPLIST
fce2f250  88888888 88888888 88888888 88888888
fce2f260  88888888 88888888 88888888 88888888
fce2f270  88888888 88888888 88888888 88888888
fce2f280  88888888 88888888 88888888 88888888
fce2f290  88888888 88888888 88888888 88888888
fce2f2a0  00008888 00010004 0800000d 00000002
fce2f2b0  00000002 fb10afe0 0001c033 00000000

漏洞利用過程大概可以分為如下步驟:

1.  構造數據覆蓋tagPROPLIST中的cEntries和iFirstFree
2.  通過SetProp函數對tagPROPLIST對分布在其后的tagMENU結構中的cItems和rgItems字段進行控制
3.  通過SetMenuItemInfo實現對任意地址的寫操作
4.  改寫內核HalDispatchTable+4的數據實現EIP控制

4、漏洞演示

測試環境: Windows 7 SP1 x86 (更新于2016-07-16)

來源鏈接:CVE-2016-3308 / ZDI-16-453 Microsoft Windows內核提權漏洞原理分析與利用


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/37/