CVE-2026-50416: bypass de KASLR no Windows 11
No sistema afetado, esta leitura do mapeamento de usuário do heap da área de trabalho do Win32 retornou um endereço de kernel:
ULONG64 leaked = *(ULONG64 *)(desktop_heap + 0x100);
Essa é toda a divulgação. No início, era apenas um valor de oito bytes com bits altos convincentes. Passei mais tempo verificando o que o valor seguia do que lendo-o.
A Microsoft publicou o problema como CVE-2026-50416, uma divulgação de informações do Win32k classificada como CWE-200.
O PoC começa no TEB x64. GS:[0x30] fornece o TEB atual, TEB + 0x800 alcança os dados do cliente Win32 na build que testei, e o slot 5 contém o endereço de usuário do heap da área de trabalho:
PVOID teb = (PVOID)__readgsqword(0x30);
PVOID *client_info = (PVOID *)((BYTE *)teb + 0x800);
BYTE *desktop_heap = (BYTE *)client_info[5];
O slot 5 é outro 0x28 bytes dentro desses dados do cliente. A Trellix documentou a mesma rota TEB + 0x800 + 0x28 ao estudar mapeamentos de tagWND no Windows 10 1909. Esses offsets não são uma ABI. Eu os codifiquei diretamente e chamo VirtualQuery sobre o resultado antes de usar a região mapeada.
A verificação do ponteiro cobre a extensão de sinal canônica do x64, o alinhamento de oito bytes e padrões sentinela conhecidos. O valor então precisa sobreviver aos testes de comportamento abaixo.
O PoC principal lê o offset 0x100, cria uma janela STATIC, uma BUTTON e uma EDIT, lê novamente, destrói as três janelas e então lê mais uma vez.
ULONG64 before = *(ULONG64 *)(desktop_heap + 0x100);
/* Create STATIC, BUTTON and EDIT windows. */
ULONG64 after_create = *(ULONG64 *)(desktop_heap + 0x100);
/* Destroy all three. */
ULONG64 after_destroy = *(ULONG64 *)(desktop_heap + 0x100);
Os três QWORDs coincidiram. Essas alocações de janela vieram e foram embora; o valor em 0x100 não.
kaslr_sandbox_proof.c trata da comparação entre processos. O processo pai redireciona o stdout de cada processo filho através de um pipe, analisa o QWORD que o filho leu e compara-o com a linha de base.
Os processos filhos associados ao desktop predefinido devolveram o mesmo valor. Um processo filho atribuído a um desktop criado com CreateDesktop devolveu um valor diferente. O harness também exercita processos filhos com integridade Low, AppContainer, a sua configuração LPAC e AppContainer com integridade Low. Todos eles realizam a leitura a partir do seu próprio TEB e mapeamento, em vez de receberem o valor do processo pai.
supporting_proof_no_window.c realiza a leitura sem chamar CreateWindowEx. Não é necessária nenhuma nova janela USER.
O PoC principal trata o valor divulgado menos 0x40 como a base do lado do kernel correspondente ao heap do desktop:
ULONG64 candidate_kernel_heap_base = leaked - 0x40;
Em seguida, ele consulta user32!gSharedInfo, usa os 16 bits inferiores de um HWND como índice da tabela de handles USER e avança por aheList em HeEntrySize. Se o primeiro QWORD nessa entrada for diferente de zero e menor que o heap mapeado, o PoC o utiliza como um deslocamento relativo do heap da área de trabalho:
ULONG index = (ULONG)(ULONG_PTR)hwnd & 0xffff;
BYTE *entry = (BYTE *)shared->aheList + index * shared->HeEntrySize;
ULONG64 heap_offset = *(ULONG64 *)entry;
if (heap_offset > 0 && heap_offset < heap_size) {
BYTE *user_candidate = desktop_heap + heap_offset;
ULONG64 kernel_candidate = candidate_kernel_heap_base + heap_offset;
}
Esta é a mesma relação geral usada em técnicas mais antigas de heap de desktop: as visões do usuário e do kernel colocam um objeto no mesmo deslocamento a partir de suas respectivas bases de heap. O Unit 42 fornece contexto útil sobre a tabela de handles USER e os vazamentos de endereço mais antigos gSharedInfo e HMValidateHandle.
Os endereços resultantes são os candidatos a objeto de kernel do PoC. A relação 0x40, o layout da entrada de handle e os deslocamentos do TEB pertencem à build usada para o teste.
Deixo os candidatos a objeto ali. O QWORD em desktop_heap + 0x100 já é informação estável de endereço no espaço de kernel em um mapeamento legível pelo usuário. Um bug separado de corrupção de memória no Win32k pode usá-lo no lugar de um palpite de endereço.