Skip to content
KitploitKITPLOIT
StrumentiExploitsBlog
Log in
Invia
StrumentiExploitsBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2026-50416-writeup-and-poc — CVE-2026-50416: Windows 11 KASLR bypass | Kitploit
Strumenti/GitHubGitHub/karollooool/cve-2026-50416-writeup-and-poc
Framework di ExploitMemory ForensicsAnalisi delle VulnerabilitàExploitRaccolta InformazioniCTFAnalisi di BinariPaper e RicercaApprendimento e Formazione
Lab e Pratica
GitHubkarollooool/cve-2026-50416-writeup-and-poc

CVE-2026-50416-writeup-and-poc

CVE-2026-50416: Windows 11 KASLR bypass

Vedi RepositorySito web
445131 mese faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

CVE-2026-50416: Un QWORD di Troppo nell'Heap del Desktop

Sulla build Insider di Windows 11 10.0.28020.2149, il mapping in modalità utente dell'heap del desktop Win32k esponeva un puntatore grezzo al pool della sessione kernel all'offset 0x100.

La lettura in sé è quasi offensivamente piccola:

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

Nella mia sessione di test, restituiva:

0xffffc600dcc00040

Il valore rimaneva identico tra processi sullo stesso desktop e cambiava dopo un riavvio. Un processo avviato su un altro desktop riceveva un valore diverso perché aveva un heap del desktop diverso. Da questo singolo QWORD, il PoC ha recuperato la base dell'heap del desktop kernel e poi ha utilizzato user32!gSharedInfo per derivare gli indirizzi kernel degli oggetti finestra attivi.

La stessa lettura funzionava da Low integrity, AppContainer, una configurazione LPAC con zero capacità, e un figlio AppContainer a Low integrity con zero capacità.

L'heap del desktop dovrebbe essere condiviso. Il puntatore kernel non lo è.

L'heap del desktop dalla modalità utente

Win32k memorizza oggetti USER come finestre, menu, classi, hook e metadati correlati negli heap del desktop. Ogni desktop ha il proprio heap. Parte di quell'heap viene mappata nei processi associati al desktop così che la modalità utente possa leggere lo stato GUI condiviso senza chiedere al kernel ogni campo.

Sulla build x64 testata, il mapping in modalità utente può essere raggiunto tramite i dati client del TEB del thread corrente:

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

Gli offset sono specifici della build, ma il percorso è semplice:

GS:[0x30]
    -> TEB
    -> ClientInfo a TEB + 0x800
    -> ClientInfo[5]
    -> mapping dell'heap del desktop in modalità utente

Il PoC chiama VirtualQuery sull'indirizzo restituito e registra la regione mappata e la sua protezione. Non è ancora successo nulla di sbagliato. Un mapping di sola lettura dell'heap del desktop è un comportamento normale di Win32k.

Il problema inizia 256 byte più avanti.

Il puntatore all'offset 0x100

Il PoC principale legge un QWORD dall'heap mappato:

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

Il valore ha superato i controlli di base attesi da un indirizzo virtuale kernel sul sistema testato:

  • Bit alti canonici
  • Allineamento a otto byte
  • Non uno dei valori sentinella noti filtrati dal PoC
  • Stabile mentre le finestre venivano create e distrutte
  • Identico nei processi testati sullo stesso desktop
  • Diverso dopo il riavvio
  • Diverso su un altro desktop

Il test di stabilità crea finestre STATIC, BUTTON ed EDIT, legge il valore prima della creazione, lo rilegge mentre le finestre esistono, le distrugge e lo legge una terza volta.

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);

Tutte e tre le letture hanno restituito lo stesso valore. L'attività di allocazione delle finestre non lo ha spostato. Questo comportamento è coerente con un campo nei metadati dell'heap del desktop piuttosto che con un puntatore a un oggetto di breve durata.

La proprietà cross-process è altrettanto importante. Due processi collegati allo stesso desktop osservano lo stesso valore divulgato perché guardano lo stesso heap del desktop. Dopo un riavvio, KASLR dà alla sessione un nuovo indirizzo. Un figlio posto su un altro desktop osserva un altro puntatore perché quel desktop possiede un altro heap.

Questo conferisce alla fuga di informazioni un'identità utile:

stesso avvio + stesso desktop      -> stesso puntatore
stesso avvio + desktop diverso    -> puntatore diverso
nuovo avvio                       -> puntatore diverso

Recupero della base dell'heap del desktop kernel

Sulla build testata, il puntatore divulgato si trova 0x40 byte sopra la base dell'heap del desktop kernel usata dal PoC:

ULONG64 kernel_desktop_heap_base = leaked - 0x40;

Usando il valore registrato della sessione:

puntatore divulgato            = 0xffffc600dcc00040
base heap desktop kernel       = 0xffffc600dcc00000

Questa relazione è specifica della build. Per la build usata durante i test, fornisce l'ancora lato kernel necessaria per il passo successivo.

Un puntatore è già utile. Un indirizzo per un oggetto scelto è molto più utile.

Risoluzione di un oggetto finestra tramite gSharedInfo

user32.dll esporta gSharedInfo, che espone l'elenco delle voci degli handle USER e la dimensione di ciascuna voce:

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

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

Un HWND contiene un indice nella tabella degli handle USER. Il PoC prende i 16 bit bassi dell'handle, cammina fino alla voce corrispondente e legge l'offset dell'heap del desktop memorizzato lì.

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

Lo stesso offset identifica l'oggetto in entrambi i mapping:

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

Quindi il calcolo completo è:

base heap desktop kernel = desktop_heap[0x100] - 0x40
indice handle             = HWND & 0xffff
offset heap               = aheList[indice handle].offset
indirizzo finestra kernel = base heap desktop kernel + offset heap

Il PoC crea sei classi di finestra ed esegue il calcolo per ciascuna:

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

Per ogni oggetto, stampa l'HWND, l'indice dell'handle, l'indirizzo dell'oggetto in modalità utente, l'offset dell'heap e l'indirizzo kernel.

HWND
  -> indice handle a 16 bit bassi
  -> voce handle gSharedInfo
  -> offset heap del desktop
  -> base heap desktop kernel + offset
  -> indirizzo kernel di quell'oggetto finestra

Questa è la parte che trasforma la divulgazione da un puntatore kernel vago in un oracolo di indirizzi per oggetti USER selezionati sull'heap del desktop testato.

Perché i test sandbox contano

L'heap del desktop arriva tramite un mapping condiviso. I livelli di integrità e le restrizioni AppContainer non riscrivono i contenuti di quel mapping per ogni processo. Se il processo riceve l'heap del desktop, riceve anche il QWORD a 0x100.

Il PoC sandbox lancia figli in diversi contesti e fa leggere a ciascun figlio il valore dal proprio TEB e dal proprio mapping dell'heap del desktop.

ContestoConfigurazioneRisultato
Integrità mediaProcesso utente standardDivulgato
Integrità bassaIntegrità del token abbassata a LowDivulgato
AppContainerZero capacità richiesteDivulgato
Configurazione LPACTutti i pacchetti applicativi con policy opt-out, zero capacità richiesteDivulgato
AppContainer a integrità bassaLow IL più AppContainer, zero capacità richiesteDivulgato
Desktop alternativoFiglio assegnato a un nuovo desktopDivulgato un valore diverso
Scarica lo strumento