En enero de este mismo año, Peleg Hadar (@peleghd) y Tomer Bar reportaron a Microsoft, sin liberar públicamente los detalles, una vulnerabilidad en el servicio de impresión de Windows que permitiría la elevación de privilegios en los sistemas Windows 7 en adelante. Los correspondientes parches fueron liberados por Microsoft el 12 de mayo y el CVE asignado a la vulnerabilidad fue el (CVE-2020-1048).
El mismo día en que fueron liberadas las actualizaciones Alex Ionescu (@aionescu) y Yarden Shafir (@yarden_shafir), los cuales también encontraron dicha vulnerabilidad, publicaron los detalles de su investigación en el artículo PrintDemon: Print Spooler Privilege Escalation, Persistence & Stealth (CVE-2020-1048 & more). Aprovechando este conocimiento y después de algunas pruebas logré encontrar un bypass para el parche de Microsoft utilizando junction points y symlinks (todo el mérito para James Forshaw @tiraniddo).
Mi reporte, con fecha 19 de mayo de 2020, fué marcado por Microsoft como duplicado dado que la causa raíz al parecer ya había sido reportada previamente. A la vulnerabilidad se le asignó el CVE-2020-1337, el cual comparto con otros investigadores.
En este artículo trataré brevemente de detallar el proceso de explotación de la vulnerabilidad CVE-2020-1048 y el bypass que logré encontrar, incluyendo referencias de interés de obligada lectura.
CVE-2020-1048
Como ya he comentado, el 12 de mayo de este mismo año Microsoft publicó las actualizaciones de seguridad para el CVE-2020-1048, una vulnerabilidad que permitiría una escalada de privilegios aprovechando el servicio de impresión de Windows:
An elevation of privilege vulnerability exists when the Windows Print Spooler service improperly allows arbitrary writing to the file system. An attacker who successfully exploited this vulnerability could run arbitrary code with elevated system privileges. An attacker could then install programs; view, change, or delete data; or create new accounts with full user rights.
To exploit this vulnerability, an attacker would have to log on to an affected system and run a specially crafted script or application.
The update addresses the vulnerability by correcting how the Windows Print Spooler Component writes to the file system.
Yarden Shafir y Alex Ionescu publicaron su artículo PrintDemon: Print Spooler Privilege Escalation, Persistence & Stealth (CVE-2020-1048 & more) detallando el origen de la vulnerabilidad y dando algunas pistas para su explotación sin liberar una PoC totalmente funcional, al menos no utilizándola “as is”.
El proceso de explotación pasa por crear un puerto local de impresora un tanto especial para, posteriormente, crear una impresora virtual asignándole dicho puerto y utilizando un driver genérico. Lo especial del puerto local asignado a la impresora consiste en hacer que apunte a un fichero en un path interesante, por ejemplo C:\Windows\system32\evil.dll
, dado que durante el proceso de creación del puerto Windows no comprueba si el usuario tiene permisos de escritura en dicha ruta. Si posteriormente desde el contexto de un usuario estándar lanzamos una tarea de impresión utilizando la impresora virtual, la tarea devolverá un error al intentar crear el fichero dado que no tenemos los permisos necesarios para imprimir en dicha ubicación; el servicio de impresión ejecuta el proceso impersonando al usuario que lanzó la tarea, así que hasta aquí todo normal.
Nótese que para apuntar el puerto local a un fichero en C:\Windows\system32
y posteriormente poder crear la impresora virtual con este puerto y un driver genérico deberemos utilizar PowerShell o hacerlo mediante código, ya que si intentamos utilizar el asistente de creación de impresoras el proceso nos devolverá un error de Acceso denegado al intentar crear el puerto, dado que en este caso si se comprueban correctamente los permisos incluso antes de “crear” realmente el fichero.
El artículo, cuya lectura es absolutamente recomendada, detalla la causa de esta discordancia además de desmenuzar el proceso llevado a cabo por el servicio de impresión de Windows al lanzar una tarea de impresión. Durante este proceso se generan en el directorio C:\Windows\system32\spool\PRINTERS
los siguientes ficheros:
FPnnnnn.SPL
ónnnnn.SPL
que básicamente contiene lo que será impreso.FPnnnnn.SHD
ónnnnn.SHD
que contiene los metadatos que definen la tarea de impresión.
No ahondaré en la estructura de el fichero SHD
, ya que conocerla no resulta imprescindible para entender el procedimiento de explotación llevado a cabo (consultar los siguientes repositorios). Como resumen, cuando se inicia el servicio de impresión de Windows éste comprueba si existe algún fichero shadow (.SHD
) en el directorio del spooler (normalmente C:\Windows\system32\spool\PRINTERS
) analizando su contenido y lanzando la impresión del fichero de spool (.SPL
) asociado y, lo que es más interesante, ejecutando el proceso directamente como SYSTEM
sin impersonar al usuario que lanzó inicialmente la tarea. Ahora sí, con todas estas piezas, ya podemos montar una prueba de concepto funcional.
Utilizando Visual Studio para abrir el proyecto una vez descargado desde el repositorio, el primer paso será modificar en printserver\pserver.c
el fichero destino utilizado como puerto local de la impresora:
|
|
Modificaremos también la declaración de las variables hPrinter
y hMonitor
asignándoles un valor por defecto igual a NULL
:
|
|
Posteriormente agregaremos el fichero de cabecera strsafe.h
y el siguiente fragmento código, adaptado a partir de una respuesta en stackoverflow, y que se encargará de copiar el contenido de la DLL maliciosa en una cadena de texto y almacenarla en memoria:
|
|
Por último indicar que será necesario tener instalado el Windows Driver Kit para que el proyecto compile correctamente, ya que depende de la librería ntdllp.lib
.
Ahora, en un sistema Windows 10, sin la actualización correspondiente al CVE-2020-1048 instalada, utilizaremos el binario printserver.exe
generado colocando en el mismo directorio un fichero de nombre evil.dll
desde el contexto de un usuario estándar:
La ejecución del binario se quedará esperando nuestra interacción y así lo dejaremos. Si ahora observamos las impresoras disponibles en el sistema advertiremos la existencia de un nuevo dispositivo, PrintDemon, que tiene un documento pausado en la cola:
Necesitaremos reiniciar el servicio de impresión (spooler
), y dado que estamos utilizando un usuario estándar y no contamos con los permisos suficientes reiniciaremos el sistema para lograrlo. Una vez arrancado accederemos a la cola de la impresora maliciosa y reanudaremos las tareas de impresión:
Como resultado habremos conseguido plantar nuestro fichero en un directorio privilegiado:
Podemos utilizar esta vulnerabilidad junto con la DLL maliciosa desarrollada también por Yarden Shafir y Alex Ionescu en su artículo Faxing Your Way to SYSTEM — Part Two, publicado el 30 de abril, para realizar un ataque de tipo DLL hijacking y escalar privilegios en el sistema. No es el objetivo del presente artículo detallar esa parte del ataque pero puede consultarse el siguiente hilo en twitter para hacerse una idea del proceso completo a seguir.
Unos días despues de la publicación del código de PrintDemon, concretamente el 15 de mayo, BC Security (@BCSecurity1), conocido por ser el encargado del mantenimiento del conocido framework Empire, liberó una versión del exploit desarrollada en PowerShell. Seguramente sea mi culpa pero lo cierto es que no he sido capaz de hacerlo funcionar correctamente.
Por otra parte, el pasado 5 de agosto Peleg Hadar (@peleghd) y Tomer Bar liberaron los detalles de su investigación acerca del CVE-2020-1048 en sendas charlas en la Black Hat y DEF CON (video, slides y whitepaper) de este año. El exploit, junto con material adicional, puede encontrarse en su repositorio de github.
Mi bypass para el CVE-2020-1048
Primero eliminaremos la impresora e instalaremos el parche correspondiente iniciando nuevamente el proceso de creación de la impresora utilizando ahora PowerShell dado que nos permitirá obtener un resultado mas esclarecedor:
Tal como puede apreciarse en la captura obtenemos un error de acceso denegado al intentar crear el puerto para la impresora. Las modificaciones introducidas por la actualización de seguridad realizan correctamente una comprobación de permisos en el directorio indicado como destino, deteniendo el proceso en este punto; pero si le damos una vuelta, seguro que se nos ocurre algún bypass…
Durante el proceso de creación del puerto local de la impresora Windows comprueba si el usuario tiene permisos de escritura en el directorio indicado así que tendremos que utilizar algún truco para eludir esta comprobación. En este caso utilizaremos los siguientes comandos de PowerShell para probar nuestra idea.
En primer lugar agregaremos un driver genérico y crearemos un directorio que posteriormente utilizaremos como destino para la impresión:
Seguidamente crearemos un puerto local apuntando a un fichero en ese directorio y confirmaremos que se haya creado correctamente:
El proceso se completará correctamente dado que nuestro usuario estándar tiene control total tanto sobre el directorio como sobre su contenido. Por último agregaremos la impresora virtual utilizando el driver genérico y el puerto local recién creado:
En este momento pondremos la impresora virtual en pausa:
Ahora mandaremos a imprimir un documento de prueba de forma que se quedará pausado en la cola de la impresora virtual:
Tal y como hicimos anteriormente, ahora será necesario reiniciar el servicio de impresión, y dado que estamos utilizando un usuario estándar y no contamos con los permisos suficientes nuevamente reiniciaremos el sistema para lograrlo.
Hasta aquí todo normal pero ahora, ¿qué podemos hacer para conseguir escribir en un directorio privilegiado con la configuración que hemos desplegado hasta el momento? La respuesta viene de la mano de James Forshaw y su suite de herramientas symboliclink-testing-tools.
Sin ahondar demasiado en los detalles (recomiendo consultar la siguiente presentación) lo que haremos será generar un junction point para el directorio donde apunta nuestro puerto local, C:\Users\me\test
, para posteriormente mediante otro tipo de enlace simbólico apuntar el fichero dentro de nuestro directorio, C:\Users\me\test\foobar
, a C:\windows\system32\evil.dll
:
A continuación accederemos a la cola de la impresora maliciosa y reanudaremos la impresión, comprobando posteriormente que se ha creado el fichero, C:\windows\system32\evil.dll
, aunque con un contenido inservible:
Una vez confirmado el bypass y para demostrar el proceso de explotación he desarrollado una PoC en C# utilizando el paquete NtApiDotNet publicado por James Forshaw para el uso mediante programación de enlaces simbólicos y los paquetes IlMerge 3.0.40 y MSBuild.ILMerge.Task para obtener un binario autocontenido. Después de molestar un poco y tras alguna que otra prueba puedo decir que el código es funcional.
El binario, si se indica como primer parámetro el primer paso en la secuencia de explotación, recibe como segundo parámetro la ruta donde se encuentra el fichero malicioso que será plantado en C:\Windows\system32
; finalizada su ejecución nos indica que debemos reiniciar el sistema:
Una vez reiniciado el sistema ejecutaremos el segundo paso, indicando en este caso como segundo parámetro el nombre con el que el contenido del fichero indicado en el primer paso será creado en el directorio C:\Windows\system32
:
Como resultado habremos conseguido copiar una DLL maliciosa en el directorio C:\Windows\system32
lo que nos permitiría llevar a cabo un ataque de DLL hijacking para elevar privilegios en un sistema vulnerable.
Enlaces de interés
- CVE-2020-1048 | Windows Print Spooler Elevation of Privilege Vulnerability
- PrintDemon: Print Spooler Privilege Escalation, Persistence & Stealth (CVE-2020-1048 & more)
- PrintDemon (CVE-2020-1048)
- Invoke-PrintDemon
- DEF CON Safe Mode - Peleg Hadar and Tomer Bar - After Stuxnet Printing still the Stairway to Heaven
- Print Spooler Research Tools
- Windows Print Spooler Patch Bypass Re-Enables Persistent Backdoor
- CVE-2020-1337 Windows Privilege Escalation
- CVE-2020-1337 – PrintDemon is dead, long live PrintDemon!
- cve-2020-1337-poc
- CVE-2020-1337 - Binary Planting (CVE-2020-1048 bypass)