Skip to content
KitploitKITPLOIT
OutilsBlog
Log in
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-50416-writeup-and-poc — CVE-2026-50416: contournement du KASLR sous Windows 11 | Kitploit
Outils/GitHubGitHub/karollooool/cve-2026-50416-writeup-and-poc
Frameworks d'ExploitationCriminalistique MémoireAnalyse des VulnérabilitésExploitationCollecte d'InformationsCTFAnalyse de BinairesArticles et RechercheApprentissage et Éducation
Labs et Pratique
GitHubkarollooool/cve-2026-50416-writeup-and-poc

CVE-2026-50416-writeup-and-poc

CVE-2026-50416: contournement du KASLR sous Windows 11

Voir le dépôtSite web
44513il y a 1 moisVérifié par Kitploit

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2026-50416 : Un QWORD de trop dans le tas de bureau (Desktop Heap)

Sur la build Windows 11 Insider 10.0.28020.2149, le mappage en mode utilisateur du tas de bureau Win32k exposait un pointeur brut de pool du noyau à l'offset 0x100.

La lecture en elle-même est presque offensivement petite :

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

Dans ma session de test, cela a renvoyé :

0xffffc600dcc00040

La valeur restait identique entre les processus sur le même bureau et changeait après un redémarrage. Un processus lancé sur un autre bureau recevait une valeur différente car il disposait d'un tas de bureau différent. À partir de ce seul QWORD, la preuve de concept (PoC) a récupéré la base du tas de bureau du noyau, puis a utilisé user32!gSharedInfo pour dériver les adresses noyau des objets fenêtre actifs.

La même lecture fonctionnait depuis une intégrité Low, un AppContainer, une configuration LPAC sans aucune capacité, et un enfant AppContainer à intégrité Low sans aucune capacité.

Le tas de bureau est censé être partagé. Le pointeur du noyau ne l'est pas.

Le tas de bureau depuis le mode utilisateur

Win32k stocke les objets USER tels que les fenêtres, menus, classes, hooks et métadonnées associées dans les tas de bureau. Chaque bureau possède son propre tas. Une partie de ce tas est mappée dans les processus associés au bureau afin que le mode utilisateur puisse lire l'état GUI partagé sans demander chaque champ au noyau.

Sur la build x64 testée, le mappage en mode utilisateur peut être atteint via les données client du TEB du thread courant :

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

Les offsets sont spécifiques à la build, mais le chemin est simple :

GS:[0x30]
    -> TEB
    -> ClientInfo à TEB + 0x800
    -> ClientInfo[5]
    -> mappage du tas de bureau en mode utilisateur

La PoC appelle VirtualQuery sur l'adresse renvoyée et enregistre la région mappée et sa protection. Rien n'a encore mal tourné. Un mappage de tas de bureau en lecture seule est un comportement Win32k normal.

Le problème commence 256 octets plus loin.

Le pointeur à l'offset 0x100

La PoC principale lit un QWORD depuis le tas mappé :

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

La valeur a passé les vérifications de base attendues pour une adresse virtuelle du noyau sur le système testé :

  • Bits hauts canoniques
  • Alignement sur huit octets
  • Aucune des valeurs sentinelles connues filtrées par la PoC
  • Stable pendant la création et la destruction de fenêtres
  • Identique dans les processus testés sur le même bureau
  • Différente après redémarrage
  • Différente sur un autre bureau

Le test de stabilité crée des fenêtres STATIC, BUTTON et EDIT, lit la valeur avant la création, la relit pendant que les fenêtres existent, les détruit, puis effectue une troisième lecture.

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

Les trois lectures ont renvoyé la même valeur. L'activité d'allocation de fenêtres ne l'a pas déplacée. Ce comportement est cohérent avec un champ des métadonnées du tas de bureau plutôt qu'avec un pointeur d'objet à courte durée de vie.

La propriété inter-processus est tout aussi importante. Deux processus attachés au même bureau observent la même valeur divulguée car ils regardent le même tas de bureau. Après un redémarrage, KASLR donne une nouvelle adresse à la session. Un enfant placé sur un autre bureau observe un autre pointeur car ce bureau possède un autre tas.

Cela donne à la fuite une identité utile :

même démarrage + même bureau      -> même pointeur
même démarrage + autre bureau     -> pointeur différent
nouveau démarrage                 -> pointeur différent

Récupération de la base du tas de bureau du noyau

Sur la build testée, le pointeur divulgué se situe 0x40 octets au-dessus de la base du tas de bureau du noyau utilisée par la PoC :

ULONG64 kernel_desktop_heap_base = leaked - 0x40;

En utilisant la valeur de session enregistrée :

pointeur divulgué            = 0xffffc600dcc00040
base du tas de bureau noyau  = 0xffffc600dcc00000

Cette relation est spécifique à la build. Pour la build utilisée lors des tests, elle fournit l'ancre côté noyau nécessaire pour l'étape suivante.

Un pointeur est déjà utile. Une adresse pour un objet choisi est beaucoup plus utile.

Résolution d'un objet fenêtre via gSharedInfo

user32.dll exporte gSharedInfo, qui expose la liste des entrées de handles USER et la taille de chaque entrée :

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

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

Un HWND contient un index dans la table des handles USER. La PoC prend les 16 bits de poids faible du handle, parcourt jusqu'à l'entrée correspondante et lit l'offset du tas de bureau qui y est stocké.

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

Le même offset désigne l'objet dans les deux mappages :

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

Le calcul complet est donc :

base du tas de bureau noyau = desktop_heap[0x100] - 0x40
index du handle             = HWND & 0xffff
offset du tas               = aheList[index du handle].offset
adresse fenêtre noyau       = base du tas de bureau noyau + offset du tas

La PoC crée six classes de fenêtres et effectue le calcul pour chacune :

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

Pour chaque objet, elle affiche le HWND, l'index du handle, l'adresse de l'objet en mode utilisateur, l'offset du tas et l'adresse noyau.

HWND
  -> index de handle sur 16 bits de poids faible
  -> entrée de handle gSharedInfo
  -> offset du tas de bureau
  -> base du tas de bureau noyau + offset
  -> adresse noyau de cet objet fenêtre

C'est la partie qui transforme la divulgation d'un simple pointeur noyau lâche en un oracle d'adresses pour des objets USER sélectionnés sur le tas de bureau testé.

Pourquoi les tests de sandbox comptent

Le tas de bureau arrive via un mappage partagé. Les niveaux d'intégrité et les restrictions AppContainer ne réécrivent pas le contenu de ce mappage pour chaque processus. Si le processus reçoit le tas de bureau, il reçoit le QWORD à 0x100 avec lui.

La PoC sandbox lance des enfants dans plusieurs contextes et fait lire à chaque enfant la valeur depuis son propre TEB et son propre mappage de tas de bureau.

ContexteConfigurationRésultat
Intégrité MediumProcessus utilisateur standardDivulgué
Intégrité LowIntégrité du jeton abaissée à LowDivulgué
AppContainerZéro capacité demandéeDivulgué
Configuration LPACTous les packages d'application en opt-out, zéro capacité demandéeDivulgué
AppContainer à intégrité LowLow IL plus AppContainer, zéro capacité demandéeDivulgué
Bureau alternatifEnfant assigné à un nouveau bureauDivulgué une valeur différente
Télécharger l’outil