
Эксплойт для CVE-2021-40449 — уязвимость повышения привилегий Win32k (LPE)
Exploit for CVE-2021-40449 (Win32k - LPE)
CVE-2021-40449 — это use-after-free в Win32k, который позволяет повышать привилегии локально.
Уязвимость была обнаружена в реальных атаках компанией Kaspersky.
Обнаруженный эксплойт был написан для поддержки следующих продуктов Windows:
Однако в настоящее время эксплойт протестирован только на следующих версиях:
Я настоятельно рекомендую прочитать технический разбор от Kaspersky перед продолжением.
Как упоминалось в техническом разборе Kaspersky, уязвимость существует в GreResetDCInternal. Если атакующий перехватывает пользовательский колбэк DrvEnablePDEV, который вызывается во время hdcOpenDCW, можно уничтожить исходный контекст устройства, вызвав ResetDC, что приводит к use-after-free в ядре, когда пользовательский колбэк возвращается.
Следующий псевдокод частично составлен из утёкшего исходного кода Windows XP и частично путём реверс-инжиниринга последней (до патча) версии GreResetDCInternal из Win32kfull.sys. Не относящиеся к делу части удалены с помощью [...]. Ищите комментарии 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
// [...]
}
Как видно из псевдокода, старый контекст устройства может быть освобождён в пользовательском колбэке из вызова hdcOpenDCW, и позже метод DrvResetPDEV извлекается из старого контекста устройства и вызывается с (po->dhpdev, poNew->dhpdev).
Чтобы создать и перехватить контекст устройства, можно сделать следующее:
EnumPrintersOpenPrinter, GetPrinterDriver и LoadLibraryExAGetProcAddress и DrvEnableDriverVirtualProtectCreateDC(NULL, printerName, NULL, NULL)Теперь у нас должен быть контекст устройства для принтера с перехваченными пользовательскими колбэками.
Нас интересует только один перехват, а именно DrvEnablePDEV. Этот перехват интересен в двух аспектах: запуск UAF и контроль над аргументами, как описано ранее. Чтобы запустить уязвимость UAF, мы вызовем ResetDC внутри колбэка, что уничтожит старый контекст устройства. Когда мы вернёмся из колбэка, мы всё ещё будем внутри первого GreResetDCInternal, который вскоре после этого получит и вызовет указатель на функцию DrvResetPDEV из нашего старого и уничтоженного контекста устройства с двумя аргументами, возвращёнными из DrvEnablePDEV; старым и новым DHPDEV.
Если ваш процесс работает со средним уровнем целостности, KASLR не должен быть проблемой с помощью EnumDeviceDrivers и NtQuerySystemInformation.
Kaspersky упоминает, что оригинальный эксплойт использовал объекты палитры GDI и один вызов функции ядра для достижения произвольного чтения/записи памяти. Этот эксплойт использует технику выделения BitMapHeader в больших пулах и RtlSetAllBits для включения всех привилегий в токене нашего текущего процесса. BitMapHeader будет указывать на _SEP_TOKEN_PRIVILEGES токена нашего текущего процесса. Вызвав RtlSetAllBits(BitMapHeader), можно включить все привилегии для токена нашего текущего процесса одним вызовом функции ядра. После этого можно злоупотребить новыми привилегиями для получения SYSTEM. Этот эксплойт использует SeDebugPrivilege для внедрения шеллкода в процесс winlogon.exe.
