Zerologon desatado: la vulnerabilidad que permite comprometer cualquier controlador de dominio de Windows fácilmente

El 11 de agosto Microsoft a través de Tom Tervoort de Secura trataba una vulnerabilidad en el servicio Netlogon. Netlogon Remote Protocol es una interfaz RPC disponible en los controladores de dominio de Windows. Se utiliza para diversas tareas relacionadas con la autenticación de usuarios y máquinas, sobretodo para facilitar que los usuarios inicien sesión en servidores utilizando el protocolo NTLM, pero también para otras cosas como la autenticación de respuestas NTP y para permitir que un equipo actualice su contraseña dentro del dominio. El servicio RPC se sirve en un puerto dinámico TCP asignado por el servicio "portmapper" del controlador de dominio, o mediante una canalización SMB en el puerto 445.

En aquel momento Microsoft sólo decía brevemente que para explotar la vulnerabilidad un atacante no autenticado podía usar MS-NRPC para conectarse a un controlador de dominio y obtener acceso de administrador de dominio. Pero un mes después, el 11 de septiembre de 2020, la misma empresa Secura lanzó un advisory con una herramienta para identificar las máquinas vulnerables a esta vulnerabilidad identificada como CVE-2020-1472 (CVSS 10.0) y tres días después publicó un paper técnico explicando la vulnerabilidad más en detalle, desatando una avalancha de PoCs y herramientas que están poniendo en vilo a toda la comunidad de seguridad, ya que cualquier atacante sin necesidad de autenticación y con visibilidad por red contra un controlador de dominio puede resetear la password del domain admin.

Lo interesante de este protocolo Netlogon es que no utiliza el mismo esquema de autenticación que otros servicios RPC. En su lugar, utiliza un protocolo criptográfico personalizado para permitir que un cliente (una computadora unida a un dominio) y un servidor (el controlador de dominio) se demuestren entre sí que ambos conocen un secreto compartido. Este secreto compartido es un hash de la contraseña de la cuenta de la computadora del cliente. La razón de esto es que las cuentas de equipo en tiempos Windows NT no podían hacer uso de esquemas de autenticación de usuario estándar como NTLM o Kerberos.

El problema: que la implementación de este protocolo basado en AES-CFB8 no está hecha correctamente. ¿Por qué? Porque para generar las credenciales tanto cliente como servidor usan la función ComputeNetlogonCredential que en su versión más moderna que usa AES-CFB8 define IVs fijos de 16 bytes de ceros en lugar de IVs aleatorios (de ahí que se haya bautizado también a la vulnerabilidad como Zerologon). Con esto, dada una clave aleatoria, hay una probabilidad de 1 entre 256 de que el cifrado AES de un bloque de todo ceros de como salida todo ceros.

A partir de ahí se pueden llevar una serie de pasos para conseguir la explotación. Yo los voy a resumir en español, pero los tenéis en detalle en el paper técnico de Secura.

Pasos para la explotación

Paso 1: spoofear las credenciales del cliente

Podemos pasar el challenge a 8 ceros y esto significará que para 1 de cada 256 claves de sesión, la ClientCredential correcta (parámetro de la función ComputeNetlogonCredential) también constará de 8 ceros. Dado que las cuentas de equipo no se bloquean después de intentos de inicio de sesión no válidos, simplemente podemos intentarlo varias veces hasta que obtengamos esa clave y la autenticación sea exitosa. La media esperada de intentos necesarios será de 256, lo que en la práctica sólo lleva unos tres segundos. Con este método, podemos iniciar sesión como cualquier ordenador del dominio. Esto incluye controladores de dominio de respaldo e incluso el propio controlador de dominio de destino.

Paso 2: deshabilitar cifrar y autenticar criptográficamente

La firma y el sellado o "signing and sealing" de los mensajes posteriores al handshake son opcionales y se pueden desactivar simplemente no estableciendo un flag en la llamada NetrServerAuthenticate. Los clientes modernos se negarán de forma predeterminada a conectarse cuando el servidor no establezca este flag (probablemente una medida para evitar ataques de degradación), pero los servidores no rechazarán a los clientes que no soliciten cifrado. Supongo que esta podría ser una opción de diseño para mantener la compatibilidad heredada. Dado que actuamos como el cliente durante este ataque, simplemente podemos omitir este flag y continuar.

Paso 3: falsificar una llamada

Incluso cuando el cifrado de llamadas está deshabilitado, cada call que haga algo interesante debe contener un valor de autenticador. Este valor se calcula aplicando ComputeNetlogonCredential (con la clave de sesión) al valor ClientStoredCredential más el Timestamp.

ClientStoredCredential es un valor creciente mantenido por el cliente. Al realizar el protocolo de enlace, se inicializa con el mismo valor que el ClientCredential que proporcionamos. Esta credencial de cliente consta únicamente de ceros, por lo que ClientStoredCredential será 0 para la primera llamada realizada después de la autenticación.

Timetamp debe contener la hora actual de Posix y el cliente lo incluye en la llamada junto con el autenticador. Sin embargo, resulta que el servidor en realidad no impone muchas restricciones sobre lo que puede ser este valor (lo cual tiene sentido, de lo contrario, la desviación del reloj se volvería muy problemática), por lo que podemos simplemente fingir que es el 1 de enero de 1970 y también establecer esto valor a 0. Si pasamos por el paso 1, también sabemos que ComputeNetlogonCredential (0) = 0.

De modo que podemos autenticar nuestra primera llamada simplemente proporcionando un autenticador todo cero y una marca de tiempo todo cero. ¿Veis porque se habla de Zerologon? ;)

Paso 4: cambiar la contraseña de un equipo del Directorio Activo

Para ello se explota la llamada a NetrServerPasswordSet2 que se utiliza para establecer una nueva contraseña de equipo. Esta contraseña no tiene hash, pero está cifrada con la clave de sesión. ¿Cómo? Bueno, nuevamente usando CFB8 con un IV todo ceros...

La estructura de contraseña de texto sin formato en el protocolo Netlogon consta de 516 bytes. Los últimos cuatro bytes indican la longitud de la contraseña en bytes. Todos los bytes de la estructura que no forman parte de la función de contraseña se ven como relleno y pueden tener valores arbitrarios. Si simplemente proporcionamos 516 ceros aquí, esto se descifrará a 516 ceros, es decir, una contraseña de longitud cero. Resulta que establecer contraseñas vacías para una computadora no está prohibido en absoluto, por lo que esto significa que podemos establecer una contraseña vacía para cualquier equipo en el dominio.

Una vez hecho esto, podemos configurar una nueva conexión Netlogon en nombre de esta computadora. Esta vez conocemos la contraseña de la computadora (está vacía), por lo que podemos seguir el protocolo normalmente.

Tenemos que tener en cuenta que cuando cambiamos la contraseña de esta manera lo estamos haciendo el el Directorio Activo y que la contraseña anterior queda almacenada localmente, por lo que el equipo no podrá volver a autenticarse en el dominio al menos que se haga una resincronización manual.

Paso 5: del cambio de contraseña a administrador de dominio

Una de las computadoras a las que podemos cambiar la contraseña es la del propio controlador de dominio, incluso cuando se trata del mismo controlador de dominio al que nos estamos conectando a través de Netlogon. Hacerlo crea una situación interesante como decíamos antes, donde la contraseña de DC almacenada en el AD es diferente de la contraseña almacenada en su registro local (en HKLM\SECURITY\Policy\Secrets\$machine.ACC).

Esto parece hacer que el DC se comporte mal de varias formas impredecibles (por ejemplo la resolución DNS puede dejar de funcionar). Por eso lo ideal es usar el script "secretsdump" de Impacket con la nueva contraseña de DC. Este script extraerá correctamente todos los hashes de usuario del dominio a través del protocolo del Servicio de replicación de dominios (DRS). Esto incluye hashes de administrador de dominio (incluida la clave 'krbtgt', que se puede usar para crear golden tickets), que luego podrían usarse para iniciar sesión en el DC (usando un ataque estándar pass-the-hash) y actualizar la contraseña del equipo almacenado en registro local del DC. Ahora el DC vuelve a comportarse normalmente y el atacante se ha convertido en administrador de dominio.

Herramientas
Detección
  • @Sbousseaden proporciona un PCAP de muestra con un intento de explotar Zerologon.
  • La explotación exitosa que deriva en un cambio de contraseña se mostrará como ID de evento 4742, Password last set change, realizado por Anonymous Logon.
  • El siguiente script en Powershell analiza los eventos 5827, 5828, 5829, 5830 y 5831 de un .evtx y los exporta a un excel: https://github.com/mingchen-script/CVE-2020-1472-visualizer
  • Adam Swan de SOC Prime proporciona una regla Sigma que se puede utilizar para detectar intentos de Zerologon: https://socprime.com/blog/zerologon-attack-detection-cve-2020-1472/
  • Para detectar el uso predeterminado de pth en Cobalt Strike, se pueden buscar comandos que contengan /c echo y \\.\Pipe\ juntos. Cobalt Strike de forma predeterminada también usa 11 caracteres hexadecimales para el argumento de echo y 6 caracteres hexadecimales para el nombre de la pipe. Esto requiere parchear manualmente y el operador no lo puede configurar fácilmente.
  • Para detectar el uso de DCSync, se puede buscar el ID de evento 4662 que contenga el GUID {1131f6ad-9c07-11d1-f79f-00c04fc2dcd2}, que es el permiso extendido DS-Replication-Get-Changes-All requerido para la replicación. Cualquier replicación de un controlador que no sea de dominio es sospechosa. 
  • @James_inthe_box también proporciona esta regla de Snort: https://gist.github.com/silence-is-best/25ae0929c277642e86ecf592598a3254
  • Regla Yara de sbousseaden: https://github.com/sbousseaden/YaraHunts/blob/master/hunt_mimikatz_zerologon.yar

Contramedidas

El parche publicado en el Patch Tuesday de Agosto de Microsoft ya securiza NRPC por ejemplo forzando el cifrado y la firma de mensajes. Así que toca parchear, parchear y parchear!!

Comentarios