Exploit for CVE-2021-40449 (Win32k — LPE)
Description
CVE-2021-40449 is a use-after-free in Win32k that allows for local privilege escalation.
The vulnerability was found in the wild by Kaspersky.
The discovered exploit was written to support the following Windows products:
- Microsoft Windows Vista
- Microsoft Windows 7
- Microsoft Windows 8
- Microsoft Windows 8.1
- Microsoft Windows Server 2008
- Microsoft Windows Server 2008 R2
- Microsoft Windows Server 2012
- Microsoft Windows Server 2012 R2
- Microsoft Windows 10 (build 14393)
- Microsoft Windows Server 2016 (build 14393)
- Microsoft Windows 10 (build 17763)
- Microsoft Windows Server 2019 (build 17763)
However, this exploit is current only tested on the following versions:
- Microsoft Windows 10 (build 14393)
- Microsoft Windows 10 (build 17763)
Technical Writeup
I highly recommend reading Kaspersky’s technical writeup before proceeding.
As mentioned in the technical writeup by Kasperky, the vulnerability exists in
The following pseudo-code is made partially from the leaked Windows XP source code and by reverse-engineering the latest (before the patch)
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 // [...] }
As can be seen from the pseudo-code, the old device context can be freed in a user-mode callback from the
To create and hook a device context, one can do the following:
- Find an available printer with
EnumPrinters
- Load the printer driver into memory with
,OpenPrinterandGetPrinterDriverLoadLibraryExA
- Get the printer driver’s user-mode callback table with
andGetProcAddressDrvEnableDriver
- Unprotect the printer driver’s user-mode callback table with
VirtualProtect
- Overwrite the printer driver’s desired user-mode callback table entries
- Create a device context for the printer with
CreateDC(NULL, printerName, NULL, NULL)
We should now have a device context for a printer with hooked user-mode callbacks.
We’re interested in only one hook, namely
If your process is running with a medium integrity level, KASLR should not be an issue with the help of
Kaspersky mentions that the original exploit used GDI palette objects and a single kernel function call to achieve arbitrary memory read/write. This exploit uses a technique to allocate a BitMapHeader on the big pool and
PoC

References
- https://securelist.com/mysterysnail-attacks-with-windows-zero-day/104509/
- https://github.com/siberas/CVE-2016-3309_Reloaded/
- https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/how-kernel-exploits-abuse-tokens-for-privilege-escalation
- https://github.com/KaLendsi/CVE-2021-40449-Exploit
- https://mp.weixin.qq.com/s/AcFS0Yn9SDuYxFnzbBqhkQ