
Exploit para CVE-2021-40449 - Vulnerabilidad de elevación de privilegios en Win32k (LPE)
Exploit para CVE-2021-40449 (Win32k - LPE)
CVE-2021-40449 es un use-after-free en Win32k que permite la escalada local de privilegios.
La vulnerabilidad fue descubierta en ataques reales por Kaspersky.
El exploit descubierto fue escrito para ser compatible con los siguientes productos de Windows:
Sin embargo, este exploit actualmente solo se ha probado en las siguientes versiones:
Recomiendo encarecidamente leer el informe técnico de Kaspersky antes de continuar.
Como se menciona en el informe técnico de Kaspersky, la vulnerabilidad existe en GreResetDCInternal. Si un atacante intercepta la devolución de llamada en modo usuario DrvEnablePDEV, que se llama durante hdcOpenDCW, es posible destruir el contexto de dispositivo original llamando a ResetDC, lo que provoca un use-after-free en el kernel cuando la devolución de llamada en modo usuario retorna.
El siguiente pseudocódigo se ha elaborado parcialmente a partir del código fuente filtrado de Windows XP y mediante ingeniería inversa del GreResetDCInternal más reciente (anterior al parche) de Win32kfull.sys. Las partes irrelevantes se han eliminado con [...]. Busque los comentarios 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
// [...]
}
Como se puede ver en el pseudocódigo, el contexto de dispositivo antiguo puede liberarse en una devolución de llamada en modo usuario desde la llamada a hdcOpenDCW, y más adelante, el método DrvResetPDEV se obtiene del contexto de dispositivo antiguo y se llama con (po->dhpdev, poNew->dhpdev).
Para crear e interceptar un contexto de dispositivo, se puede hacer lo siguiente:
EnumPrintersOpenPrinter, GetPrinterDriver y LoadLibraryExAGetProcAddress y DrvEnableDriverVirtualProtectCreateDC(NULL, printerName, NULL, NULL)Ahora deberíamos tener un contexto de dispositivo para una impresora con devoluciones de llamada en modo usuario interceptadas.
Solo nos interesa una intercepción, concretamente DrvEnablePDEV. Esta intercepción es interesante por dos aspectos: desencadenar el UAF y controlar los argumentos, como se describió anteriormente. Para desencadenar la vulnerabilidad UAF, llamaremos a ResetDC dentro de la intercepción, lo que destruirá el contexto de dispositivo antiguo. Cuando retornemos de la intercepción, todavía estaremos dentro de la primera llamada a GreResetDCInternal, que poco después obtendrá y llamará al puntero de función para DrvResetPDEV desde nuestro contexto de dispositivo antiguo y destruido, con los dos argumentos devueltos por DrvEnablePDEV: el DHPDEV antiguo y el nuevo.
Si tu proceso se ejecuta con un nivel de integridad medio, KASLR no debería ser un problema con la ayuda de EnumDeviceDrivers y NtQuerySystemInformation.
Kaspersky menciona que el exploit original utilizaba objetos de paleta GDI y una única llamada a una función del kernel para lograr lectura/escritura arbitraria de memoria. Este exploit utiliza una técnica para asignar un BitMapHeader en el big pool y RtlSetAllBits para habilitar todos los privilegios en el token de nuestro proceso actual. El BitMapHeader apuntará a _SEP_TOKEN_PRIVILEGES del token de nuestro proceso actual. Al llamar a RtlSetAllBits(BitMapHeader), es posible habilitar todos los privilegios para el token de nuestro proceso actual con una única llamada a una función del kernel. A partir de aquí, se pueden aprovechar los nuevos privilegios para obtener SYSTEM. Este exploit utiliza SeDebugPrivilege para inyectar shellcode en el proceso winlogon.exe.
