Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Log in
Enviar
HerramientasExploitsBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

FeedsContactoPrivacidad© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2016-3308 — Usar CVE-2016-3308 para corromper el heap de escritorio de win32k | Kitploit
Herramientas/GitHubGitHub/jackhuyh/cve-2016-3308
Forensia de MemoriaAnálisis de VulnerabilidadesExplotaciónExplotación de Binarios
GitHubjackhuyh/cve-2016-3308

CVE-2016-3308

Usar CVE-2016-3308 para corromper el heap de escritorio de win32k

Ver Repositorio
12111hace 10 añosAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

Usar CVE-2016-3308 para corromper el desktop heap de win32k

autor : @55-AA, 18 de septiembre de 2016

##Introducción

El desktop heap es un pool del kernel usado por win32k; puede ser explotado por una aplicación en modo usuario. A continuación describiré en detalle cómo implementar una explotación fiable para leer/escribir direcciones arbitrarias en el kernel. Este writeup y el análisis asociado se realizaron en una instalación win7_sp1_x86 (build 17842).

##Vulnerabilidad

El 9 de agosto de 2016, Microsoft publicó MS16-098. El código vulnerable se encuentra dentro de la función win32k!xxxInsertMenuItem, cuyo prototipo es :

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

Primero veamos el pseudo-código del bug en 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 {

En el código anterior, cuando el 9.º elemento (a partir del 1.º) se añadía a pMenu, se llamaba a DesktopAlloc() para reasignar un nuevo pMenu->rgItems. Después se llamaba a MNLookUpItem() para obtener la ubicación del elemento en pMenu->rgItems. Pero el pItem devuelto por MNLookUpItem() es un rgItems de otro pSubMenu, no del pMenu, por lo que al llamarse a RtlMoveMemory(), el pItem del pSubMenu y los bytes posteriores se sobrescribían debido al tamaño incorrecto del movimiento.

El siguiente es el código de desensamblado del bug, que desencadenaría una sobrescritura del heap; puede aprovecharse para construir un chunk falso:

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)

Para seguir el bug, uso estos breakpoints en 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

Para desencadenar el bug, deben llevarse a cabo los siguientes pasos:

  1. Crear un menú.
  2. Crear un submenú como primer ITEM del menú con ID 0x123; es necesario establecer MENUITEMINFO.hbmpItem con HBMMENU_SYSTEM.
  3. Añadir otros 7 ITEMs con ID de 0x1001 a 0x1007 al menú.
  4. Añadir un ITEM con ID 1 al submenú.
  5. Añadir otros 8 ITEMs al submenú, para obtener un tagMENU.rgItems con 16 slots. Pero este paso no es necesario para desencadenar el bug si solo se necesita un crash.
  6. Añadir el 9.º ITEM con ID 0x123 al menú, de modo que se llama a RtlMoveMemory() con parámetros incorrectos.

Desktop Heap

El desktop heap es un pool global utilizado por todos los procesos GUI. Todos los objetos GUI, como Window o Menu, se almacenan en el desktop heap y son gestionados por el asignador de heap del kernel. El asignador de heap del kernel usa funciones conocidas como RtlAllocateHeap y RtlFreeHeap. A diferencia del heap en modo usuario, el desktop heap no emplea asignadores de front-end, por lo que no hay Low Fragmentation Heap, ni listas Lookaside, etc. Tampoco hay Heap Encoding hasta Windows 8 y posteriores. La siguiente es la estructura de chunk en win7_sp1_x86:

typedef struct _HEAP_ENTRY {
    USHORT Size;
    UCHAR Flags;
    UCHAR SegmentIndex;
    USHORT PreviousSize;
    UCHAR SegmentOffset;
    UCHAR UnusedBytes;
 } HEAP_ENTRY, *PHEAP_ENTRY;

Los campos Size y PreviousSize representan el tamaño de los chunks desplazado a la derecha HEAP_GRANULARITY_SHIFT bits (definido como 3 en sistemas de 32 bits). El campo Size especifica el chunk actual, y PreviousSize especifica el anterior. El bit más bajo de Flags normalmente se establece a HEAP_ENTRY_BUSY(0x01), lo que indica que el chunk está en uso; si no, es 0x00.

La siguiente figura muestra la relación entre estos campos y los bloques de chunks. La segunda WORD subrayada en verde (0x000f) representa que el tamaño del chunk actual es 0x78 bytes; la segunda WORD subrayada en negro (0x0003) representa que el tamaño del chunk anterior es 0x18 bytes; y las WORDs subrayadas en rojo (0x0001) representan que los chunks actuales están en uso. Aquí, el tamaño del chunk incluye el tamaño de la cabecera, definida como la estructura HEAP_ENTRY anterior.

Es la característica más importante para la corrupción del heap: el asignador de heap siempre obtiene el chunk liberado más recientemente. Esto significa que podemos asignar un chunk con cualquier tamaño y en la ubicación que queramos.

Corruption

Aprovechando el bug, puedo sobrescribir algunos bytes en el desktop heap, obteniendo así un chunk falso que reemplaza a un chunk normal. Luego libero el chunk reemplazado, de modo que el chunk falso se coloca en la cima de la lista de chunks libres. Posteriormente, el chunk falso se reutiliza, y puedo escribir cualquier byte en él; la región escribible se superpone con varios chunks normales, pero no puede cubrir todo el espacio del kernel. Por lo tanto, necesito construir otra primitiva R/W en la región superpuesta, aprovechando tagWND.strName para escribir en direcciones arbitrarias. El puntero de strName.Buffer puede llevarnos a cualquier lugar, tanto del kernel como del espacio de usuario. Por supuesto, nuestro objetivo es solo nt!HalDispatchTable.

La siguiente figura muestra el procedimiento de cambio del heap:

Según lo demostrado, corrompo el desktop heap paso a paso e implemento la explotación mediante las siguientes etapas:

Descargar herramienta