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

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

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

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

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

Категории

Все категории
Loading categories
CVE-2016-3308 — Используйте CVE-2016-3308 для повреждения кучи рабочего стола win32k | Kitploit
Инструменты/GitHubGitHub/jackhuyh/cve-2016-3308
Криминалистика памятиАнализ уязвимостейЭксплуатацияЭксплуатация Бинарных Файлов
GitHubjackhuyh/cve-2016-3308

CVE-2016-3308

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

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

Популярное

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

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

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

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

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

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

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

##Введение

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

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

9 августа 2016 года Microsoft выпустила MS16-098. Код уязвимости находится в функции win32k!xxxInsertMenuItem, прототип функции:

BOOL xxxInsertMenuItem(
        PMENU pMenu, 
        UINT wIndex, 
        BOOL fByPosition, 
        LPMENUITEMINFOW lpmii, 
        PUNICODE_STRING pstrItem
    );

Сначала посмотрим на псевдокод ошибки в xxxInsertMenuItem:

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 и последующие байты будут перезаписаны из-за неправильного размера перемещения.

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

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:

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:

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.

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

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

Скачать инструмент