
Exploit für CVE-2021-40449 - Win32k-Schwachstelle zur Privilegienausweitung (LPE)
Exploit für CVE-2021-40449 (Win32k - LPE)
CVE-2021-40449 ist ein Use-after-Free in Win32k, der eine lokale Privilegienausweitung ermöglicht.
Die Schwachstelle wurde von Kaspersky als in freier Wildbahn ausgenutzt entdeckt.
Der entdeckte Exploit wurde für die Unterstützung der folgenden Windows-Produkte geschrieben:
Dieser Exploit wurde jedoch bisher nur auf den folgenden Versionen getestet:
Ich empfehle dringend, vor dem Fortfahren Kasperskys technischen Bericht zu lesen.
Wie im technischen Bericht von Kaspersky erwähnt, liegt die Schwachstelle in GreResetDCInternal. Wenn ein Angreifer den User-Mode-Callback DrvEnablePDEV hookt, der während hdcOpenDCW aufgerufen wird, ist es möglich, den ursprünglichen Gerätekontext durch den Aufruf von ResetDC zu zerstören, was einen Use-after-Free im Kernel verursacht, wenn der User-Mode-Callback zurückkehrt.
Der folgende Pseudo-Code basiert teilweise auf dem geleakten Windows-XP-Quellcode und auf dem Reverse Engineering des neuesten (vor dem Patch) GreResetDCInternal aus Win32kfull.sys. Die irrelevanten Teile wurden mit [...] entfernt. Suchen Sie nach den Kommentaren VULN: .
BOOL GreResetDCInternal(
HDC hdc,
DEVMODEW *pdmw,
BOOL *pbBanding,
DRIVER_INFO_2W *pDriverInfo2,
PVOID ppUMdhpdev)
{
// [...]
HDC hdcNew;
{
// Create DCOBJ from HDC
DCOBJ dco(hdc);
if (!dco.bValid())
{
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
}
else
{
// Create DEVOBJ from `dco`
PDEVOBJ po(dco.hdev());
// [...]
// Create the new DC
// VULN: Can result in a usermode callback that destroys old DC, which
// invalidates `dco` and `po`
hdcNew = hdcOpenDCW(L"",
pdmw,
DCTYPE_DIRECT,
po.hSpooler,
prton,
pDriverInfo2,
ppUMdhpdev);
if (hdcNew)
{
po->hSpooler = NULL;
DCOBJ dcoNew(hdcNew);
if (!dcoNew.bValid())
{
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
}
else
{
// Transfer any remote fonts
dcoNew->pPFFList = dco->pPFFList;
dco->pPFFList = NULL;
// Transfer any color transform
dcoNew->pCXFList = dco->pCXFList;
dco->pCXFList = NULL;
PDEVOBJ poNew((HDEV)dcoNew.pdc->ppdev());
// Let the driver know
// VULN: Method is taken from old (possibly destroyed) `po`
PFN_DrvResetPDEV rfn = po->ppfn[INDEX_DrvResetPDEV];
if (rfn != NULL)
{
(*rfn)(po->dhpdev, poNew->dhpdev);
}
// [...]
}
}
}
}
// Destroy old DC
// [...]
}
Wie aus dem Pseudo-Code ersichtlich ist, kann der alte Gerätekontext in einem User-Mode-Callback aus dem hdcOpenDCW-Aufruf freigegeben werden; später wird die Methode DrvResetPDEV aus dem alten Gerätekontext abgerufen und mit (po->dhpdev, poNew->dhpdev) aufgerufen.
Um einen Gerätekontext zu erstellen und zu hooken, kann man wie folgt vorgehen:
EnumPrinters findenOpenPrinter, GetPrinterDriver und LoadLibraryExA in den Speicher ladenGetProcAddress und DrvEnableDriver abrufenVirtualProtect aufhebenCreateDC(NULL, printerName, NULL, NULL) erstellenWir haben nun einen Gerätekontext für einen Drucker mit gehookten User-Mode-Callbacks.
Uns interessiert nur ein Hook, nämlich DrvEnablePDEV. Dieser Hook ist in zweierlei Hinsicht interessant: Er löst den UAF aus und ermöglicht die Kontrolle über die Argumente, wie bereits beschrieben. Um die UAF-Schwachstelle auszulösen, rufen wir ResetDC innerhalb des Hooks auf, wodurch der alte Gerätekontext zerstört wird. Wenn wir aus dem Hook zurückkehren, befinden wir uns immer noch im ersten GreResetDCInternal, das kurz darauf den Funktionszeiger für DrvResetPDEV aus unserem alten und zerstörten Gerätekontext abruft und mit den beiden Argumenten aufruft, die von DrvEnablePDEV zurückgegeben wurden: dem alten und dem neuen DHPDEV.
Wenn Ihr Prozess mit einer mittleren Integritätsstufe (Medium Integrity Level) läuft, sollte KASLR mit Hilfe von EnumDeviceDrivers und NtQuerySystemInformation kein Problem darstellen.
Kaspersky erwähnt, dass der ursprüngliche Exploit GDI-Palettenobjekte und einen einzelnen Kernel-Funktionsaufruf verwendete, um beliebiges Lesen und Schreiben im Speicher zu ermöglichen. Dieser Exploit nutzt eine Technik, um einen BitMapHeader im Big Pool zu allozieren und RtlSetAllBits, um alle Privilegien für unser aktuelles Prozess-Token zu aktivieren. Der BitMapHeader zeigt auf die _SEP_TOKEN_PRIVILEGES unseres aktuellen Prozess-Tokens. Durch den Aufruf von RtlSetAllBits(BitMapHeader) ist es möglich, alle Privilegien für unser aktuelles Prozess-Token mit einem einzigen Kernel-Funktionsaufruf zu aktivieren. Von hier aus kann man die neuen Privilegien missbrauchen, um SYSTEM zu erlangen. Dieser Exploit verwendet SeDebugPrivilege, um Shellcode in den Prozess winlogon.exe zu injizieren.
