
Zwei Kernel-Schwachstellen in Razer Lycosa.sys (CWE-125 Speicherofflegung + CWE-121 Stack-Überlauf), verkettet zu lokaler Rechteausweitung. Materialien zur koordinierten Offenlegung, verifiziert unter Windows 11.
Entdeckt durch https://github.com/416rehman/DeepZero
Hersteller: Razer Inc.
Komponente: Lycosa.sys, der Razer-Lycosa-Tastaturfiltertreiber, x64
SHA-256: a120a6184ab16864e8a5f1dfd0cd178fca541de463b5cef946e18c34b9b6f716
Referenz des Melders: a120a6184ab16864
Status: noch nicht an den Hersteller gemeldet.
Dieses Verzeichnis meldet zwei verschiedene Defekte in derselben Routine desselben Treibers. Sie haben getrennte Ursachen und getrennte Korrekturen, daher hat jeder einen eigenen in sich geschlossenen Ordner und kann verfolgt und mit einer eigenen Kennung versehen werden:
| CVE | Defekt | Typ | Folge |
|---|---|---|---|
| CVE-01 | Die Ausgabelänge wird nicht gegen den Puffer geprüft, sodass der Treiber Kernel-Stack-Speicher zurückgibt | CWE-125 Out-of-Bounds-Read | Offenlegung von Kernel-Speicher, untergräbt die Adressraum-Layout-Randomisierung |
| CVE-02 | Die Eingabelänge wird nicht gegen den Puffer geprüft, sodass der Treiber seine eigene Rücksprungadresse überschreibt | CWE-121 Stack-Buffer-Overflow | Ausführung beliebigen Kernel-Codes |
Beide sind von jedem Konto aus erreichbar, das sich anmelden und ein Programm ausführen kann. Keine Administratorrechte, keine Erhöhung, kein besonderes Privileg. Beide wurden auf Windows 11 25H2 (Build 26200.8875) mit erzwungener Codeintegrität und deaktiviertem Testsigning bestätigt.
Abschnitt 3 beschreibt, was die beiden zusammen bewirken, was der Grund dafür ist, dass sie gleichzeitig gemeldet werden und warum der verkettete Proof of Concept in diesem Stammverzeichnis liegt.
Beide befinden sich im IRP_MJ_DEVICE_CONTROL-Handler bei RVA 0x1270, und beide wirken
auf denselben 0x400-Byte-Puffer auf dem Kernel-Stack. Der Prolog, gelesen aus dem
ausgelieferten Binary, legt die Geometrie für beide fest:
Lycosa+0x1270 48 89 54 24 10 mov [rsp+10h], rdx ; Irp
Lycosa+0x1275 48 89 4c 24 08 mov [rsp+8], rcx ; DeviceObject
Lycosa+0x127a 48 81 ec 98 04 00 00 sub rsp, 498h ; the frame
Lycosa+0x1291 ba 00 04 00 00 mov edx, 400h ; the buffer size
Lycosa+0x1296 48 8d 8c 24 80 00 00 00 lea rcx, [rsp+80h] ; the buffer
Ein 0x400-Byte-Puffer bei rsp+0x80, innerhalb eines 0x498-Byte-Frames, ohne gesicherte
nichtflüchtige Register. Vom Anfang des Puffers an gezählt:
0x000 .. 0x3FF the buffer, which both defects are supposed to stay inside
0x418 the return address of the dispatch routine
0x420 the saved DeviceObject argument
0x428 the saved Irp argument
0x418 ist 0x498 - 0x80. Die Offenlegung (CVE-01) liest über 0x3FF hinaus und gibt
zurück, was sie findet; der Overflow (CVE-02) schreibt über 0x3FF hinaus und ersetzt es.
DriverEntry erstellt das Gerät mit keinem Sicherheitsdeskriptor und veröffentlicht einen
symbolischen Link dafür, sodass es unter \\.\Lycosa erreichbar ist:
IoCreateDevice(param_1, 0x20, L"\\Device\\Lycosa", 0x22, 0, 0, &device);
IoCreateSymbolicLink(L"\\DosDevices\\Lycosa", L"\\Device\\Lycosa");
Jeder betroffene Steuercode dekodiert als FILE_DEVICE_UNKNOWN, METHOD_BUFFERED,
FILE_ANY_ACCESS. FILE_ANY_ACCESS bedeutet, dass kein bestimmtes Zugriffsrecht am
Handle gehalten werden muss, sodass der Sicherheitsdeskriptor des Geräteobjekts das einzige Tor ist, und
er gewährt allen Zugriff.
Alle Ergebnisse in diesen Berichten wurden von einem Standardbenutzerkonto erzeugt, dessen
einzige Gruppenmitgliedschaft die integrierte Gruppe Users ist. Das Konto hatte keine
Administratorrechte, war nicht erhöht und besaß keine über die
Standardwerte hinausgehenden Privilegien.
Der Treiber lädt auch auf Maschinen, an denen nie Razer-Hardware angeschlossen war, weil das Paket gültig katalog-signiert ist. Das ist das Muster, das bei Bring-Your-Own-Vulnerable-Driver-Angriffen verwendet wird.
Getrennt gemeldet, weil es getrennte Defekte sind, aber ein Hersteller, der sie triagiert, sollte wissen, dass jeder den anderen verschlimmert.
Modernes Windows lädt den Kernel an einer randomisierten Adresse. Ein Angreifer, der eine Rücksprungadresse überschreiben kann, muss immer noch wissen, womit er sie überschreiben soll, und das ist normalerweise das Hindernis. Dieser Treiber beantwortet beide Fragen von selbst:
CVE-01 beseitigt die Randomisierung. Die Offenlegung gibt die eigene Rücksprungadresse der
Dispatch-Routine zurück, eine Codeadresse innerhalb von ntoskrnl.exe. Subtrahiert man
ihren bekannten Offset innerhalb des Images, erhält man die Basis, an der der Kernel geladen ist,
und von dort ist jede Adresse innerhalb des Kernels bekannt. Das kostet nichts und
stört nichts.
CVE-01 liefert außerdem den Wert, den CVE-02 benötigt, um keinen Fehler auszulösen. Wie
in CVE-02 Abschnitt 4.4 beschrieben,
lädt der Treiber den Irp aus Offset 0x428 auf seinem Weg nach draußen neu und schreibt
durch ihn hindurch. Ein naiver Overflow, der die Rücksprungadresse erreicht, zerstört auch diesen
Zeiger und löst einen Fehler aus, bevor die Routine zurückkehrt. Der zuverlässige Exploit stoppt
stattdessen die Kopie genau bei 0x428 und lässt den aktiven Irp der aktuellen
Anfrage an Ort und Stelle, sodass der Treiber normal abschließt.
CVE-02 leitet dann die Ausführung um, wobei die Kernel-Basis bereits bekannt ist.
Von einem unprivilegierten Konto aus liest der verkettete Proof of Concept den Frame, berechnet die Kernel-Basis und die eigene Kernel-Adresse des Puffers und sendet eine Return-Oriented-Chain:
step 1, read what is above the buffer on the kernel stack:
+0x418 return address 0xFFFFF807D565CABB
+0x498 frame pointer 0xFFFFFD042D313750 (read twice, must match)
step 2, turn those into the two addresses the payload needs:
kernel base = 0xFFFFF807D565CABB - 0x25CABB = 0xFFFFF807D5400000
buffer on stack = 0xFFFFFD042D313750 - 0x500 = 0xFFFFFD042D313250
step 4, overflow with a chain that:
pivots the stack onto the buffer, calls nt!ZwCreateFile, and
resumes nt!IopfCallDriver+0x5b
sending 0x428 bytes
call returned: accepted=true error=0
PROOF: C:\Windows\System32\dz_lycosa_kernel_exec.txt now exists.
Dies ist die vollständige Kette, Ende-zu-Ende verifiziert. Das unprivilegierte Konto erstellte
eine Datei unter C:\Windows\System32, einem Verzeichnis, auf das ihm sonst der Schreibzugriff
verweigert wird, indem es nt!ZwCreateFile im Kernel-Modus ausführte. accepted=true bedeutet,
dass der Systemaufruf normal zurückkehrte: Die Kette setzt an der exakten Adresse fort, an die der Treiber
zurückkehren wollte (nt!IopfCallDriver+0x5b, was add rsp,0x38 ; ret ist),
sodass der Thread beendet wird und die Maschine weiterläuft. Die erstellte Datei wurde
unabhängig von einer Administrator-Shell aus bestätigt. Das vollständige Transkript ist in
logs/exec_create_file.log, und die Methode ist in
METHODOLOGY.md.