16 noviembre, 2021

Detectar, alertar y bloquear intentos de conexión por fuerza bruta a escritorio remoto RDP

Antes de nada comentar que es una muy mala práctica y está totalmente desaconsejado publicar hacia internet servicios de escritorio remoto. RDP es de los servicios más comprometidos por atacantes externos, con más intentos de ataques y que facilitan un vector de entrada directo a la red de una empresa si el acceso se configura de esta forma.

En el caso de tener que publicar RDP a internet -sin una VPN o una conexión intermedia hacia un equipo bastión- aunque sea de una manera temporal, hay que hacerlo de la forma "más segura" posible.

Lista de acceso de usuarios mediante NLA y privilegios

Bloqueo de cuenta por intentos fallidos de inicio de sesión

Este artículo no se centra en la identificación, investigación y seguimiento de los eventos relacionados con el servicio RDP, para eso dejo esta buena referencia de estudio sobre logs RDP. Al igual que unas diapositivas que representan los diagramas de flujo de conexiones RDP

Está enfocado especialmente a los intentos de conexión originados desde máquinas externas hacia servicios RDP expuestos de forma pública a internet, aunque también es aplicable a conexiones de equipos remotos entre redes internas.

Detectar evento de intento de conexión TCP hacia RDP

Cuando se realiza un intento de conexión RDP hacia un equipo y este tiene la autenticación NLA habilitada lo primero que se genera son eventos de conexión de red, después de autenticación y finalmente un login satisfactorio, fallido o reconexiones de sesión.

Haciendo referencia al conjunto de diapositivas anteriores de diagramas de flujo de conexiones RDP. Se destacan principalmente dos eventos del ProviderName "Microsoft-Windows-TerminalServices-RemoteConnectionManager" el event ID 261 y 1149.
  • Event ID 261: Se trata del primer evento que se produce indicando que el puerto RDP-Tcp que estaba escuchando recibió una conexión.
  • Event ID 1149: NO indica una autenticación exitosa a un objetivo, simplemente una conexión de red RDP exitosa.
Figura 1: Event ID 261 y 1149.

El evento 1149 es interesante ya que nos muestra el login de usuario empleado en el intento de conexión hacia los servicios RDP, pero ninguno de estos eventos nos da información sobre la IP origen remota que realizó esta conexión al intentar autenticarse.

En el siguiente flujograma se puede observar que con NLA habilitado el primer evento de conexión de red que se genera es el event ID 131 correspondiente al ProviderName "Microsoft-Windows-RemoteDesktopServices-RdpCoreTS".

Figura 2: Flujograma de alerta de eventos y bloqueo de una conexión RDP (fuente imagen).

El event ID 131 ya nos aporta la dirección IP remota y puerto TCP origen que realizó la solicitud de conexión a los servicios de escritorio remoto. Teniendo esta información ya podremos automatizar una detección, generar una alerta y bloquear la conexión localmente en el Firewall de Windows como veremos más adelante.

Figura 3: Detectando evento de conexión TCP hacia RDP (event id 131).

Automatizar las detecciones y crear alertas de los intentos de conexión RDP

Para detectar este evento de forma automática y alertarnos de ello, primero se creará un fichero por lotes .bat el cual simplemente se usará para realizar la llamada al script Powershell a través de la acción de la tarea programada que se creará más adelante.

Script .bat.
powershell.exe -ExecutionPolicy Bypass -File "C:\Users\adrian\Tools\Scripts\RDPBruteForce\Hunting_RDPBruteForce.ps1"
Comentaré dos formas para generar las notificaciones de alerta: vía Slack o correo Gmail y aunque se trate de una cuenta sin importancia y dedicada a ello tenemos que intentar no hardcodear passwords en texto plano en los scripts por lo que crearemos una password cifrada y almacenada en un fichero que solo podrá ser leída desde el equipo local que la creó.
"MiPassword" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-File "C:\Users\adrian\Tools\Scripts\RDPBruteForce\email.pass"
El siguiente script Powershell enviará una alerta de email notificando un nuevo intento de conexión correspondiente al event ID 131.
$PSDefaultParameterValues['*:Encoding'] = 'utf8'
$usuarioEmail = "EMAIL@gmail.com" 
$passwdEmailFile = "C:\PATH\email.pass"
$secPasswdEmail = Get-Content $passwdEmailFile | ConvertTo-SecureString
$credencialesEmail = New-Object System.Management.Automation.PSCredential ($usuarioEmail, $secPasswdEMail)
$asuntoEmail = "Intento de conexión RDP"

$cuerpoEmail = Get-WinEvent -ProviderName "Microsoft-Windows-RemoteDesktopServices-RdpCoreTS" | Where-Object {$_.Id -eq "131"} | Format-List | Select -First 3 | Out-String

Send-MailMessage -From $usuarioEmail -To $usuarioEmail -Subject "$asuntoEmail - $fechaHoraActual" -Body "$cuerpoEmail" -SmtpServer smtp.gmail.com -UseSsl -Credential $credencialesEmail
Creamos un filtro indicando el ID de evento 131 a partir de ahí adjuntamos este filtro personalizado a una nueva tarea programada de modo que la tarea se ejecutará solamente cuando se produzca dicho evento. 

Figura 4: Crear tarea programada a partir de un filtro personalizado.

Cargar el fichero de llamada .bat. Es opcional pero recomiendo especificar el path absoluto donde inicia el script.

Figura 5: Iniciar el script .bat de llamada al script Powershell en la acción de la tarea programada. 

En las opciones de seguridad la tarea se ejecutará tanto si el usuario inició sesión somo si no.

Figura 6: Tipo de usuario para la ejecución de la tarea programada.

Notificación de alertas de intentos de conexión RDP

Configuración de alertas vía Gmail

Una vez se genere un nuevo evento 131 se ejecutará la tarea y el script asociado que disparará la alerta de correo. Para el envío de alerta por correo vía Gmail a través del script Powershell es necesario activar el acceso a "Aplicaciones menos seguras" en la cuenta de Google. Por seguridad se debería crear una cuenta específica para esta finalidad.

Figura 7: Ejemplo de notificación de alerta vía Gmail alerta intento de conexión RDP.

Configuración de alertas vía Slack


Figura 8: Crear canal, app y añadir nuevo Webhook a Slack. 

Sustituiríamos el anterior script Powershell de notificaciones vía email Gmail por este otro siendo este al que llamaría el fichero .bat en la tarea programada.
$eventRdp = Get-WinEvent -ProviderName "Microsoft-Windows-RemoteDesktopServices-RdpCoreTS" | Where-Object {$_.Id -eq "131"} | Format-List | Select -First 3 | Out-String
$uriSlack = "Webhook_URI"
$menssage = ConvertTo-Json @{ 
    text = $eventRdp
}
try {
    Invoke-RestMethod -uri $uriSlack -Method Post -body $menssage -ContentType 'application/json' | Out-Null
} catch {
    Write-Warning "Notification to Slack went wrong."
Figura 9: Ejemplo de notificación vía Slack alerta intento de conexión RDP.

Bloqueo de IPs externas en intentos de fuerza bruta a RDP

Al tener el servicio de RDP activo en WFAS (Windows Defender Firewall con seguridad avanzada) podemos encontrar habilitada la regla por defecto de Escritorio remoto (TCP de entrada) permitiendo la conexión por el puerto 3389/tcp por defecto. 

Figura 10: Regla por defecto RDP puerto TCP 3389.

Manualmente creamos una nueva regla pero esta vez en un modo de acción de "Bloquear la conexión". Las reglas de bloquear conexión o denegación tendrán un orden de preferencia frente a las reglas de permitir conexión.

Figura 11: Crear nueva regla de bloqueo de conexión.

Para este caso no se han cambiando los puertos por defecto por lo que el protocolo y puerto será el mismo que la regla por defecto 3389/tcp.

Figura 12: Regla de bloqueo de conexión protocolo TCP y puerto local 3389.

Una vez conocemos la dirección IP remota (pública en este caso) vamos agregando estas direcciones IP en el ámbito específico de "Dirección IP remota" estas se refieren a direcciones IP remotas dentro de la misma red o direcciones IP públicas-externas. Las direcciones IP locales se refieren a las propias de la máquina en caso de tener múltiples interfaces de red.

Al estar esta regla en un modo de acción de bloqueo de conexión denegará para esa IP origen el intento de conexión TCP hacia el servicio RDP no permitiendo la conexión inicial desde el primer intento.

Figura 13: Ámbito de regla - incluir IPs remotas de intento de conexión a bloquear.
 

Publicar escritorio remoto RDP de una forma más segura

Por lo general los servicios RDP suelen estar publicados en equipos servidores o máquinas que ofrecen uno o varios servicios y que remotamente necesitan gestionarse, por definición un servidor no debería de disponer de acceso a internet. Sino se disponen de tecnologías de control de seguridad SIEM o HIPS que puedan alertarnos de estos accesos, ni de arquitecturas más avanzadas y seguras para conectarse a una infraestructura interna como puedan ser servicios del tipo RD-Gateway, VDI o una VPN bien configurada con protocolos seguros, perfiles definidos y segmentación entre redes internas. 

Pero es necesario publicar el servicio RDP de una máquina interna de forma temporal, podemos hacerlo de una forma mucho más económica, sencilla de implementar y un poco más segura que tenerlo expuesto a través de un port forwarding local de puertos entre el router o firewall frontera hacia la IP del servidor interno.
  1. Crear una máquina puente o bastión. Adquirir un servicio en cloud (AWS, Azure, GCP) y creamos una nueva máquina limpia con una dirección IP pública fija (esto es importante) y únicamente con el servicio de SSH disponible de forma pública. Por defecto la forma de conexión SSH a este tipo de máquinas cloud se realiza mediante clave pública-privada.
  2. En la configuración avanzada del firewall de Windows editamos la regla RDP del propio servidor. En el ámbito de conexión del rango de IPs internas añadimos el rango de red interno (en el caso de que también queramos permitir conexiones de los equipos internos de la red).
  3. Agregamos también la dirección IP pública asignada a la máquina bastión creada en el primer punto.
  4. Para conectarnos si usamos una máquina Windows como cliente:
    1. Agregar la clave privada de la conexión SSH de la máquina bastión.
    2. Desde un cliente SSH como PuTTY establecemos una nueva conexión SSH port forwarding local.
  5. Para conectarnos si usamos una máquina Linux como cliente:
    1. Agregar la clave privada de la conexión SSH de la máquina bastión.
    2. ssh -L LOCAL_IP:LOCAL_PORT:DESTINATION:DESTINATION_PORT USER@SSH_SERVER
    3. Ejemplo: ssh -L 127.0.0.1:9999:192.168.1.10:3389 user@90.80.60.34
Estos recursos son económicos pero tampoco es una solución a largo plazo, lo ideal sería no exponer ningún servicio RDP ni SSH aunque se autentique con una PKI. Pero si por el momento no se disponen de otras implementaciones alternativas, hacerlo de este modo será más seguro que tenerlo expuesto directamente.

Saludos!

Autor: Adrián Lois

15 julio, 2021

Seth: Ataques MITM a conexiones RDP y mitigación

Seth es una herramienta desarrollada en python y bash que nos permite realizar un ataque MITM man in the middle en conexiones RDP aprovechándose una configuración insegura como es el NO habilitar la autenticación a nivel de red NLA para conexiones de escritorio remoto.

Como requerimiento previo para usar Seth es necesario tener instalado arpspoof por lo que será necesario instalar el paquete dsniff.

apt install dsniff -y

Clonar el reporsitorio de Seth. 

git clone https://github.com/SySS-Research/Seth.git

Escenario en un entorno de dominio Active Directory

  • Atacante: 1.0.0.38 (Kali)
  • Víctima: 10.0.0.50 (Win10)
  • Host remoto: 10.0.0.76 (WServer 2019)
Seth se ejecutará en la máquina atacante 10.0.0.38 y se mantendrá a la escucha esperando que se establezca una conexión, la máquina víctima 10.0.0.50 (el usuario que se conectará a la máquina remota 10.0.0.76) le aparecerá el prompt de autenticación de credenciales, el certificado autofirmado no confiable del equipo remoto (que acabará aceptando) y finalmente se establecerá la conexión RDP. 

Seth interceptará el certificado y se podrá en medio de la comunicación insegura y sin cifrar capturando los eventos de teclado y en consecuencia las credenciales de acceso a la máquina remota.

Desde la máquina Kali 10.0.0.38 descargamos y previamente ejecutamos el script de seth.sh.

./seth.sh <INTERFACE> <ATTACKER IP> <VICTIM IP> <GATEWAY IP|HOST IP> [<COMMAND>]

Veremos que captura las pulsaciones de teclado DOMINIO\USUARIO:PASSWORD incluso después de que se establezca la conexión RDP.

Figura 1: Seth - MitM en conexiones RDP.

Haciendo uso del cliente rdesktop nos autenticamos con las credenciales capturadas y vemos como se establece una conexión RDP hacia la máquina remota.

rdesktop -g1024x768 <REMOTE IP> -u <USER> -p <PASSWORD> -d <DOMAIN>

Figura 2: rdesktop - Conexión RDP a la máquina remota víctima.

Mitigar ataques MITM en conexiones RDP

NLA (Network Level Authentication) requiere que el usuario que se conecta desde un cliente RDP se autentique antes de que se establezca una sesión con el servidor RDP. Utiliza el proveedor de soporte de seguridad, CredSSP, que está disponible a través de SSPI (Security Support Provider Interface). 

En sistemas Windows esta opción está habilitada por defecto para mantener una comunicación segura y evitar este tipo de ataques.

Figura 3: Habilitar NLA (Autenticación a nivel de red) en conexiones remotas RDP.

Conclusiones

Debido al constante y acelerado cambio tecnológico es una gestión complicada la migración de servicios y sistemas en las empresas. Sería más que necesario implementar medidas de control en el bastionado y configuraciones seguras para cualquier máquina que forme parte de una infraestructura corporativa, independientemente del entorno en el que se esté ejecutando (DES, PRE o PRO).

A día de hoy es muy común encontrarse con este tipo de configuraciones o bien por desconocimiento o bien por mantener una retrocompatibilidad en sistemas legacy ya existentes (anteriores a Windows XP SP3 y Windows Server 2008) que se resisten a ser actualizados por las compañías debido a las complejidades de migración que supone para los servicios que están ejecutando. Por lo que este tipo de técnicas siguen siendo efectivas y deben considerarse como un riesgo alto de seguridad.

Saludos!

Autor: Adrián Lois

25 marzo, 2021

Firmar scripts PowerShell [Parte 2 de 2]: Entidad de certificación CA (ADCS), despliegue de certificado vía GPO e integridad de firma

Firmar scripts PowerShell

Continuando con el artículo sobre como firmar scripts PowerShell estableciendo una política de ejecución AllSigned. En este post comentaré como hacer uso de las plantillas de certificados para el propósito de "Firma de código" emitidas por una Entidad de certificación CA implementada en un entorno de dominio haciendo uso del servicio ADCS (Active Directory Certificate Services), desplegando el certificado vía GPO y finalmente realizar una comprobación de integridad del script PowerShell firmado.

CA raíz y CA subordinadas

La CA raíz contiene la clave privada de toda la jerarquía de certificados, si un atacante obtuviera este certificado o emite un certificado para una entidad no autorizada, se compromete la seguridad basada en el certificado de la organización de todos los certificados que se han emitido en esa jerarquía.

Lo ideal y por seguridad sería disponer de una CA raíz que emita certificados para una CA subordinada, de modo que no se exponga directamente el primer nivel de certificado de la CA raíz, las CA subordinadas requieren una jerarquía de PKI establecida.

Entidades de certificación: empresarial e independientes

  • Entidad de certificación empresarial: Deben pertenecer al dominio, se suele utilizar cuando es necesario emitir muchos certificados y aprobarlos rápidamente para un entorno de dominio Active Directory.
  • Entidad de certificación independiente: Pueden pertenecer a un grupo de trabajo o a un dominio. No requieren de ADDS, se suele utilizar para emitir certificados directamente a los clientes. Dado que no tiene el certificado raíz, ofrece una mayor seguridad. La desventaja es que todos los certificados que se emiten deben ser aprobados.

Descripción del escenario para este lab

Por motivo de falta de recursos y no desplegar una CA subordinada, para estos ejemplos se hará uso únicamente de una CA raíz tipo empresarial ya implementada con el nivel de confianza máximo en la jerarquía de PKI de la organización y un equipo Windows 10 unido al dominio en el que se probará la ejecución de scripts PowerShell firmados donde se le aplicará una GPO con el certificado necesario. 

Entidad de certificación CA (ADCS) y plantilla de Firma de código

Esto sería opcional pero para tener un control de permisos podemos crear un nuevo grupo en AD para este fin y asignarlo desde la consola de "Plantillas de certifiado" (certtmpl.msc) a la plantilla de que tiene como propósito la "Firma de código". De modo que todos los usuarios/grupos que formen parte del grupo añadido tengan permisos para poder solicitar esta plantilla a la entidad de certificación desde las propia consola local de administración de certificados de usuario (certmgr.msc).

Figura 1: Asignación de permisos a la plantilla de certificado "Firma de código".

Desde la consola de "Entidad de certificación" (certsrv.msc). Emitimos la publicación de una nueva plantilla de certificado "Firma de código".

Figura 2: Emitir plantilla de certificado "Firma de código" desde una Entidad de certificación CA.

Es el momento de solicitar un certificado para "Firma de código". Esto lo haremos desde el propio almacén de certificados de Windows (certmgr.msc) y con un usuario que tenga permisos para poder solicitar el certificado de Firma de código a la Entidad de certificación.

Figura 3: Solicitar a la CA un certificado de Firma de código. 

Una vez solicitado, exportamos el certificado en formato .cer.

Figura 4: Exportar el certificado .cer con propósitos de Firma de código.

Despliegue de certificado vía GPO

Para que el resto de equipos del dominio dispongan del certificado .cer en su almacén de certificados, se crea una nueva GPO desde la consola de Administración de directivas de grupo (gpmc.msc). 

A nivel de directiva de equipo, importamos el certificado .cer en el almacén de "Editores de confianza" (Trusted Publishers). Es importante que esté importado en este almacén, de lo contrario la ejecución de scripts PowerShell firmados en cualquier máquina unida al dominio y que tenga establecida una política de ejecución de scripts en modo AllSigned nos arrojará un mensaje de advertencia inicial (como se muestra en la Figura 7).

Figura 5: Desplegar certificado para firma de código vía GPO. 

Firmar scripts PowerShell con un certificado tipo "Firma de código"

Con el mismo usuario que hemos solicitado el certificado, firmamos un script PowerShell. Si visualizamos el fichero de script vemos que se ha incluido una firma complementaria al final del código.
Get-AuthenticodeSignature -FilePath .\MyScript.ps1
$cert = Get-ChildItem -Path Cert:\CurrentUser\My -CodeSigningCert
Set-AuthenticodeSignature -FilePath MyScript.ps1 -Certificate $cert
Figura 6: Firmar y ejecutar script PowerShell desde el mismo usuario.

Ejecución del script firmado en otro equipo y usuario del dominio

Probamos a ejecutar el script firmado desde otro usuario y equipo unido al dominio, en este caso un cliente Windows 10 con la política de ejecución de scripts establecida en AllSigned.

Como vemos en la siguiente captura, hay posibilidad de ejecutar el script pero nos muestra un advertencia indicando que el certificado no está importado en el almacén de "Editores de confianza" (Trusted Publishers). Por esta razón lo comentado en el punto anterior de la figura 5. De momento cancelamos la ejecución (ctrl+c).

Figura 7: Ejecutar script PowerShell desde otra máquina y usuario del dominio (sin certificado importado).

Para importar el certificado en el almacén local de la máquina realizamos un forzado de la actualización de políticas (gpupdate /force). Una vez tenemos el certificado ya importado a través de la aplicación de la GPO creada en la Figura 5, podemos observar que al volver ejecutar el script firmado ya no se muestra ninguna advertencia.

Lógicamente si ejecutamos un script no firmado (MyScript_noSigned.ps1) se nos mostrará el mensaje de error común debido a la restricción aplicada AllSigned en la política de ejecución de scripts.

Figura 8: Ejecutar script PowerShell desde otra máquina y usuario del dominio (con certificado importado).

Comprobación de la integridad de firma en scripts PowerShell

¿Qué pasa si se modifica el código del script firmado?

Para realizar la verificación de integridad de la firma del script se modifica el código del script firmado y se vuelve a comprobar el estado del certificado. 

Si ejecutamos el script después de modificarlo se nos muestra un error indicando que "es posible que alguien no autorizado haya modificado el contenido del archivo", comprobamos nuevamente la firma del fichero y veremos un estado HashMismatch indicando claramente que se ha modificado rompiendo su integridad respecto a la firma.

Será necesario volver a firmarlo con el mismo certificado, generando así una nueva firma asignada al script. 

Figura 9: Modificar script PowerShell firmado para comprobar su integridad.

Saludos!

Autor: Adrián Lois

18 febrero, 2021

Wynis: Checking de auditorías de seguridad para Windows y Active Directory

Wynis es un script de auditoría desarrollado por el usuario Sneakysecdoggo que realiza una evaluación de controles de indicadores del CIS (Center for Internet Security) siguiendo los benchmarks del CIS Best Practices Windows 10 y Windows Server 2016. Los puntos de referencia del CIS son reconocidos internacionalmente como estándares de seguridad para la defensa de sistemas y datos de IT contra ciberataques.

Se trata de una herramienta de script similar a Lynis, usada en sistemas basados en Linux.

Después de ejecutarlo creará un directorio con todos los resultados reportados en formato de texto y csv. El checklist reporta:

  • Lista de puertos, servicios
  • Reglas de firewall 
  • Software instalado 
  • Tareas programadas 
  • Software que se ejecuta al inicio 
  • Información general del sistema 
  • Recursos compartido, 
  • Políticas de seguridad y GPOs
  • Actualizaciones instaladas 
  • Características opcionales instaladas 
  • Lista de usuarios locales, 
  • Entre otros reportes adicionales...

No solo para sistemas Windows 10, sino que también dispone de un versión para realizar este tipo de auditoría en Server 2016 que sea controladores de dominio Active Directory.

Figura 1: Ejecución de Wynis en un sistema Windows.

Saludos!