Páginas

Mostrando entradas con la etiqueta Seguridad. Mostrar todas las entradas
Mostrando entradas con la etiqueta Seguridad. Mostrar todas las entradas

10 agosto, 2022

Port Knocking: Ocultar y mejorar la seguridad en conexiones SSH

Port knocking o golpeteo de puertos es un método que proporciona seguridad frente ataques de servicios de red, consiste en realizar una serie de peticiones de conexión con una secuencia ordenada de puertos para poder habilitar una regla previamente configurada en el máquina remota que nos permita abrir una comunicación única entre cliente/servidor y establecer finalmente una conexión abierta SSH hacia el servidor remoto. El uso de esta implementación puede ser catalogado como seguridad por oscuridad

¿Cómo funciona una conexión SSH usando Port knocking? 

  1. Tenemos un servidor SSH que expone el puerto por defecto 22 o bien otro un puerto específico como por ejemplo 4422 (será el puerto que se trate en este escenario).
  2. Se añade una regla a nivel de firewall del propio servidor para rechazar todas conexiones entrantes sobre el puerto SSH previamente configurado 4422.
  3. Para poder conectarnos a este servidor usando una técnica de port kockning de hasta tres "golpeteos" de puertos para establecer una conexión SSH, necesitamos realizar una petición a estos puertos, en este escenario mostraré tres formas de hacerlo (knockd, telnet y nmap).
  4. Una vez realizado el knocking de puertos en el orden establecido (que veremos posteriormente en el fichero de configuración) este disparará la acción de ejecutar un comando, este comando no será más que establecer una nueva regla en este caso vía iptables para permitir la conexión SSH únicamente desde la IP origen que realizó la secuencia de golpeteo de puertos y no desde ninguna otra IP alcanzable de la misma red.
  5. Finalmente para volver aplicar la regla de bloqueo y restablecer esta configuración al terminar nuestra sesión, se lanzará el golpeteo de puertos de salida de SSH. De modo que la configuración vuelva a restablecerse y vuelva a cerrarse el puerto hacia el exterior.

Configurando Iptables

Bloquear puerto SSH configurado vía Iptables

En este escenario el servidor SSH se ejecuta en un entorno Debian y expone el puerto 4422 (en vez del puerto 22 por defecto).

Añadimos un regla iptables para rechazar todas las conexiones entrantes hacia el puerto 4422 del servidor SSH configurado. 

sudo iptables -A INPUT -p tcp --dport 4422 -j REJECT --reject-with tcp-reset

Iptables: diferencias entre DROP y REJECT (estado de puertos "filtered" o "closed")

La teoría nos dice que uso de DROP no hará nada con el paquete recibido, simplemente lo descartará sin enviar ningún tipo de respuesta. Al contrario de REJECT donde se genera un paquete de respuesta rechazando la conexión, si no se especifica el tipo de respuesta por defecto este paquete sería un "ICMP Destination port unreachable".

En la práctica esto no es del todo cierto. Cuando la regla es DROP es cierto que el paquete se descarta y no se envía ningún paquete de respuesta, sin embargo una regla DROP anunciará que existe un control de puertos y los escáneres sabrán que se está haciendo uso de un firewall devolviendo un estado de puerto "filtered". Ocurre lo mismo si solo usamos REJECT sin especificar un tipo concreto de respuesta, este responderá con un paquete "ICMP Destination port unreachable" anunciando así el filtrado de dicho puerto.

Si lo que queremos es ocultar el puerto y devolver un estado "closed" como resultado en un escaneo de puertos, haciendo creer que el puerto o servicio que se ejecuta en el lado servidor no se está usando o simplemente no existe, debemos especificar un tipo de respuesta en una regla REJECT. 

Cuando usamos TCP o UDP sobre un puerto la respuesta debe responder con los flags RST/ACK, de este modo estaremos indicando a los escáneres un estado de puerto "closed".

  • TCP: iptables -A INPUT -p tcp --dport 4422 -j REJECT --reject-with tcp-reset
  • UDP: iptables -A INPUT -p udp --dport 4422 -j REJECT --reject-with icmp-port-unreachable
Capturando este tipo de tráfico de red con un sniffer como Wireshark, en la siguiente captura de pantalla se muestran todas las combinaciones posibles para este caso donde se puede ver el tipo de paquete de respuesta que genera según el tipo de regla aplicada en el lado servidor.

Reglas DROP y REJECT con estado "FILTERED" (TCP/UDP)

iptables -A INPUT -p tcp --dport 4422 -j DROP
Figura 1: Estado de puerto "filtered" usando DROP en TCP.
iptables -A INPUT -p udp --dport 4422 -j DROP
Figura 2: Estado de puerto "open|filtered" usando DROP en UDP.
iptables -A INPUT -p tcp --dport 4422 -j REJECT
Figura 3: Estado de puerto "filtered" usando REJECT en TCP.

Reglas DROP y REJECT con estado "CLOSED" (TCP/UDP)

iptables -A INPUT -p tcp --dport 4422 -j REJECT --reject-with tcp-reset
Figura 4: Estado de puerto "closed" usando REJECT en TCP.
iptables -A INPUT -p udp --dport 4422 -j REJECT --reject-with icmp-port-unreachable
o
iptables -A INPUT -p udp --dport 4422 -j REJECT
Figura 5: Estado de puerto "closed" usando REJECT en UDP.

Guardado y persistencia de reglas Iptables después de reiniciar la máquina

Instalamos el paquete iptables-persistent.

sudo apt-get install iptables-persistent

Guardamos y cargamos reglas configuradas de Iptables.

sudo netfilter-persistent save
sudo netfilter-persistent reload
Para proporcionar la persistencia de las reglas configuradas de Iptables después un reinicio de la máquina. En el fichero /etc/crontab añadimos la ejecución para cargar las reglas guardas después cada arranque del sistema.
sudo echo "@reboot sudo netfilter-persistent reload &" >> /etc/crontab

Implementación de Port Knocking - knockd

Instalamos el servicio de knockd

sudo apt install -y knockd

Configuración de knockd

Editamos el fichero de configuración de /etc/knockd.conf.

[options]
        #UseSyslog
        logfile     = /var/log/knockd.log

[openSSH]
        sequence    = 7500,8200,9800
        seq_timeout = 5
        command     = /sbin/iptables -I INPUT -s %IP% -p tcp --dport 4422 -j ACCEPT
        tcpflags    = syn

[closeSSH]
        sequence    = 9300,4500,6200
        seq_timeout = 5
        command     = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 4422 -j ACCEPT
        tcpflags    = syn
Por cuestiones de monitorización y depuración de posibles errores establecemos un logfile específico para este servicio con el path donde se almacenarán los logs de knockd. Comentamos UseSyslog ya que si mantenemos esto por defecto los logs se almacenarán en el fichero de sistema /var/log/syslog.

Disponemos de dos principales directivas. Una para abrir la conexión SSH (openSSH) y otra para cerrar y restablecer la configuración de la conexión SSH (closeSSH).

Sección openSSH:
  • sequence: Se define la configuración secreta de puertos a golpear. Se puede especificar tres o más puertos y su vez indicar el tipo de protocolo, bien sea por tcp o udp. Por defecto se estable a tres puertos en protocolo tcp. En este caso modifiqué los puertos y secuencia en el siguiente orden: 7500,8200,9800.
  • seq_timeout: Tiempo de esperará entre la ejecución de un golpeteo a otro de puertos. Por defecto se establece a 5 segundos, aunque podemos aumentarlo si no fuese suficiente. No se recomienda establecer grandes intervalos de tiempo para evitar ataques de fuerza bruta que realizan esta acción de forma automatizada intentando encontrar la combinación correcta de puertos. Una herramienta utilizada podría ser KnockIt - https://github.com/eliemoutran/KnockIt.
  • command: Comando que el servidor ejecutará cuando detecte la combinación correcta de puertos previamente golpeados. En este caso se agregará en una nueva regla (iptables -I) en el primer orden de la jerarquía de prioridad de la tabla INPUT de iptables para permitir únicamente desde la dirección IP origen la la conexión hacia el puerto 4422 configurado para este servidor SSH.
  • tcpflags: Tipo de paquetes que reconocerá el servidor como válidos para el port knocking (en este caso con un flag syn).
Sección closeSSH:

Los parámetros son los mismos que las anteriores con la diferencia de indicar otro número de secuencia distinta (en este caso: 9300,4500,6200) para ejecutar otra regla en el parámetro de command.
  • command: Se eliminará (iptables -D) la regla añadida anteriormente. De modo que el servidor vuelva a "restablecer" con la configuración inicial rechazando todas las solicitudes origen hacia el puerto 4422 de SSH.

Habilitar el daemon knockd

Editamos el fichero /etc/default/knockd. Establecemos START_KNOCKD a valor "1" para habilitar el servicio y en KNOCKD_OPTS indicamos la interfaz de red que que usará el servicio, en este caso enp0s3.

START_KNOCKD=1
KNOCKD_OPTS="-i enp0s3"

Iniciamos el servicio knockd y habilitamos un tipo de inicio automático del servicio en el arranque del sistema.

sudo systemctl start knockd
sudo systemctl enable knockd

¿Cómo conectarse a un servidor SSH usando Port Knocking?

Analizando el escenario actual

  • Máquina Debian - Cliente: 10.0.0.23
  • Máquina Debian - Servidor: 10.0.0.22
En la siguiente captura se muestra un intento de conexión SSH desde la máquina cliente (10.0.0.23) sin el uso de Port knocking y vemos como la conexión ha sido rechazada.

Si comprobamos el estado de puerto con Nmap vemos que el puerto se muestra en un estado cerrado "closed". Esto es así porque previamente habíamos especificado el tipo de respuesta en REJECT con un tcp-reset el cual nos devuelve un paquete RST/ACK (ver figura 1 para más detalles).  

Si revisamos la configuración de reglas de iptables en la máquina servidor (10.0.0.22) vemos como hay una regla que rechaza cualquier tipo de conexión por el puerto 4422 desde cualquier IP origen.

Figura 6: Intento de conexión SSH usando knockd sin el uso de secuencia Port Knocking.

Opción 1: usando knock con Port Knocking

Desde la máquina cliente instalamos knockd. Para conectarnos al servidor remoto ejecutamos lo siguiente.

$ knock -v 10.0.0.22 7500 8200 9800
hitting tcp 10.0.0.22:7500
hitting tcp 10.0.0.22:8200
hitting tcp 10.0.0.22:9800

Una vez golpeado los puertos el orden de secuencia de correcto, ejecutamos ssh para conectarnos al servidor por el puerto configurado 4422.

ssh -p 4422 user@10.0.0.22

En la siguiente captura de pantalla podemos ver como después realizar el intento de conexión SSH al servidor remoto usando la combinación correcta de golpeteo de puertos este ejecuta el comando establecido en la directiva "openSSH" configurado en el fichero /etc/knockd.conf donde se indica que se añada en primer orden una nueva regla iptables para permitir la comunicación únicamente a la IP origen de la máquina cliente permitiendo así la conexión SSH hacia la máquina servidor remota.

Como prueba podemos comprobar el estado de conexión de puertos con Nmap, desde la máquina cliente permitida vemos como el puerto 4422 está en estado abierto "open", sin embargo si comprobamos lo mismo desde otra máquina de la red con otra IP origen distinta vemos como para esa otra máquina el estado del puerto 4422 es "closed". Por lo tanto podemos decir que las reglas están funcionando correctamente y estamos "ocultando" o más bien filtrando el uso de las comunicaciones en un puerto concreto entre otras máquinas, la máquina cliente origen y la máquina servidor.

Si revisamos la máquina servidor vemos como ahora existe una nueva regla de iptables en el orden de prioridad 1 (será la primera regla que interprete el firewall) permitiendo la conexión al puerto 4422 desde la IP origen 10.0.0.23 (máquina cliente). Si analizamos los logs /var/log/knockd.log también verificamos que se ejecutó correctamente el orden de secuencia de golpeteo de puertos y el comando que agrega esta regla al firewall local de iptables.

Finalmente salimos de la sesión SSH remota, para volver restablecer las reglas iptables de la máquina servidor y así volver rechazar las conexiones por el puerto 4422 a todas las máquinas e incluida la máquina cliente. Ejecutamos la secuencia de golpeteo de puertos establecida en la directiva de "closeSSH". De esta forma si volvemos a revisar las reglas en la máquina servidor vemos como esta se elimina y se vuelve establecer con prioridad 1 la regla de bloqueo que rechaza todas las conexiones desde cualquier IP origen.

Figura 7: Intento de conexión SSH usando knockd con la secuencia correcta de Port Knocking.

Opción 2: usando telnet con Port Knocking

Otra forma de realizar port knocking es hacer uso del comando telnet. Recibirá un mensaje de "Connection refused", pero está bien, ya que telnet está deshabilitado en ese puerto y solo queremos enviar un paquete con flag "TCP SYN". 
$ telnet 10.0.0.22 7500
Trying 10.0.0.22...
telnet: Unable to connect to remote host: Connection refused
$ telnet 10.0.0.22 8200
Trying 10.0.0.22...
telnet: Unable to connect to remote host: Connection refused
$ telnet 10.0.0.22 9800
Trying 10.0.0.22...
telnet: Unable to connect to remote host: Connection refused
Una vez se concluya el comando telnet durante las tres secuencias de puertos, la conexión SSH por el puerto 4422 se abrirá para la máquina cliente.

Figura 8:  Intento de conexión SSH usando telnet con la secuencia correcta de Port Knocking.

Opción 3: usando Nmap con Port Knocking

Otra forma de realizar port knocking es hacer uso de la herramienta de escaneo de puertos Nmap. Con el parámetro -sS podemos enviar hacia el servidor paquetes con flag "TCP SYN". Concatenando la secuencia de comandos correcta podemos lanzar este tipo de paquetes ejecutando así la regla que nos permite establecer conexión con el servidor SSH remoto.
sudo nmap -sS -p7500 10.0.0.22 ; nmap -sS -p8200 10.0.0.22 ; nmap -sS -p9800 10.0.0.22
Figura 9: Intento de conexión SSH usando Nmap con la secuencia correcta de Port Knocking.

Conclusiones

Añadir este mecanismo como método de autenticación SSH en servidores es una buena medida de seguridad por oscuridad. Si bien este proceso es divertido de implementar y verlo en acción, es posible que no sea factible implementarlo en entornos de producción donde hay una gran cantidad de servidores SSH o si se necesita automatizar accesos SSH.

Considero que es una buena medida de seguridad a implementar solamente para aquellos servidores realmente críticos y que tengan una cierta exposición externa en los que dado su diseño de arquitectura subyacente de red no permita ser protegido en capas anteriores con un firewall o DMZ. Incluso podríamos añadir una segunda capa de protección como sería la implementación de un segundo factor de autenticación 2FA en conexiones SSH.

Saludos!

22 junio, 2022

Segundo factor de autenticación 2FA en conexiones SSH

Implementar un segundo factor de autenticación 2FA en servicios SSH sin duda es una muy buena opción de seguridad no solo para servidores expuestos a internet sino para aquellos usuarios que pueden elevarse a un contexto privilegiado de sudo y servidores críticos en una red interna corporativa. 

Con este mecanismo de autenticación en dos pasos se estaría reduciendo en gran medida la superficie de ataque de los vectores de entrada a estos sistemas por parte de un posible atacante.

Implementación 2FA en SSH (Google Authenticator)

Instalar el paquete del módulo PAM para 2FA libpam-google-authenticator de Google Authenticator, también se instalará el paquete libqrencode4 que permite generar códigos QR en la propia terminal. 

sudo apt install libpam-google-authenticator -y

Configurar SSH para que haga uso de este módulo PAM. Añadir la siguiente línea en el fichero /etc/pam.d/sshd.

auth required pam_google_authenticator.so

Habilitar esta configuración el fichero de configuración del servicio de SSH /etc/ssh/sshd_config. Añadimos o modificamos si ya existe la siguiente política a un valor "yes".

ChallengeResponseAuthentication yes

Reiniciamos el servicio sshd para aplicar los cambios realizados en el servicio de SSH.

sudo systemctl restart sshd

Finalmente vinculamos la cuenta de usuario en la que queremos habilitar 2FA. Se nos mostrará un código QR el cual debemos vincular con nuestra App de doble factor, ya sea Google Authenticator u otra que utilicemos para generar códigos de un solo uso basado en tiempo TOTP.

google-authenticator

Se lanzará el asistente de configuración donde asociaremos la clave secreta con nuestra App de segundo factor. También se generarán "5 códigos de emergencia de un solo uso" los cuales los almacenaremos de forma segura en caso de perder el acceso a nuestra App de TOTP instalada en un dispositivo físico.

Figura 1: Creación de código QR - Asistente de vinculación para habilitar 2FA. 

Una vez finalizado el asistente de vinculación. Si nos intentamos autenticar en el servidor nos solicitará la contraseña de usuario y un código de verificación TOTP.

Figura 2: Verificación del uso de 2FA en la autenticación SSH al servidor. 

En este caso se ha vinculado con la App de Google Authenticator donde cada 30 segundos se generará un nuevo código TOTP. 

Figura 3: Código TOTP en la App de Google Authenticator.

Aclarar que aunque tengamos la política de "PasswordAuthentication no" establecida en nuestro fichero de servicio SSH al implementar 2FA será ignorada y será exigido introducir la contraseña de usuario para autenticarse en el servidor.

Paranoid Mode 3FA: Combinar autenticación SSH Public Key + Password + TOTP 

¿Qué pasa si previamente tenemos configurado una autenticación de clave pública en nuestro servicio de conexión SSH hacia el servidor y añadimos 2FA TOTP?

Si ya tenemos configurado un tipo de autenticación asimétrico de clave pública y añadimos un segundo factor bastado en TOTP a priori con esta configuración actual, no nos funcionará.

Si queremos combinar un tipo de factor TOTP junto con nuestra autenticación de clave pública en SSH debemos añadir la siguiente política en el fichero de configuración del servicio de SSH /etc/ssh/sshd_config

AuthenticationMethods publickey,keyboard-interactive

Reiniciamos el servicio SSH para aplicar los cambios.

sudo systemctl restart sshd

De este modo cuando nos conectamos al servidor SSH no solo estaremos combinando SSH Public Key y TOTP sino que también debemos introducir la contraseña de usuario. Por lo que estaremos usando tres factores de autenticación (3FA) para acceder al servidor vía SSH.

  • Autenticación con clave pública/privada.
  • Autenticación password de la cuenta de usuario.
  • Autenticación con código de verificación TOTP.
Aunque en el fichero de configuración del servicio SSH tengamos establecida la política "PasswordAuthentication no" al combinarla con "keyboard-interactive" nos obligará a introducir la password de la cuenta de usuario con el que nos autenticamos.

Figura 4: Verificación del uso de 3FA en la autenticación SSH al servidor.

Permitir autenticación SSH sin 2FA para usuarios que aún no configuraron Google Authenticator

Si en la máquina servidor hay más usuarios que necesiten autenticarse vía SSH y, por el momento, estos usuarios aún no han configurado 2FA para sus cuentas, o si son usuarios sin privilegios para los que no se considera la implementación de 2FA, pero que igualmente deben poder autenticarse e iniciar sesión en el servidor sin necesidad de utilizar TOTP como segundo factor.

Podemos añadir el valor nullock a la siguiente directiva del módulo de PAM editando el fichero /etc/pam.d/sshd.

auth required pam_google_authenticator.so nullok

Saludos!

11 enero, 2022

Instalar y configurar Microsoft LAPS para evitar movimientos laterales (Local Administrator Password Solution)

Microsoft LAPS (Local Administrator Password Solution) se trata de una herramienta de Microsoft que pone solución a la gestión de contraseñas administrador local de los equipos unidos a un dominio. 

Entendiendo el riesgo de seguridad y escenarios comunes en las organizaciones

En la mayoría de compañías es una práctica común establecer la misma contraseña para la cuenta del administrador local en todos los equipos que forman parte de un dominio, principio de localidad entre máquinas, ya puedan ser clientes o servidores (exceptuando los Domain Controller que por defecto carecen de una cuenta de administrador local).

Se considera una reducción del riesgo alto la implementación de este tipo de soluciones que evita configurar una misma contraseña para la cuenta de administrador local en todas las máquinas, al hacer que cada máquina use una contraseña diferente y compleja para la cuenta de administrador local se establece una importante medida defensiva que evita principalmente el movimiento lateral entre equipos (pivoting), una vez se obtuviese una contraseña o un hash NTLM válido en el que poder realizar técnicas de Pass the Hash (PtH).

Este tipo de soluciones es conocida por muchas empresas pero implementada por muy pocas en la realidad, no tanto por su complejidad sino por el impacto significativo que supone en la operativa diaria de gestión del cambio en la administración remota de los equipos, despliegues automáticos mal configurados en las que se haga uso de la contraseña de administrador local parametrizada, mantenimiento en los posibles endpoints que se encuentren fuera de la oficina en la que la conexión con la VPN a la red central se realice en pocas ocasiones y sea necesario gestionarlo a través de una cuenta local privilegiada, etc. 

En cualquier caso para entornos on-premise existen alternativas de gestión remota para estos equipos como puede ser la creación de un grupo en AD en el cual se añada a través de GPO al grupo administradores locales de las máquinas de modo que cada usuario que pertenezca a ese grupo principal pueda autenticarse en el equipo con su contraseña de dominio de forma nominal. En el caso de disponer de un entorno híbrido integrado con algún proveedor cloud MDM no solo se simplifica la gestión de los equipos sino que se gana en monitorización y administración de seguridad de los mismos.

Características de LAPS

LAPS determina si la contraseña de la cuenta del administrador local ha caducado. Si la contraseña ha caducado, cambia la contraseña del administrador local a un nuevo valor aleatorio y transmite la nueva contraseña y la fecha de caducidad a Active Directory donde se almacena en unos atributos especiales asociados con el objeto de equipo de AD. Las contraseñas se almacenan en Active Directory y están protegidas por listas de control de acceso (ACLs) por lo que solo los usuarios elegibles pueden leerlas o solicitar su restablecimiento.
  • Gestión centralizada y almacenamiento de las contraseñas de los administradores locales de forma segura dentro de AD DS.
  • Las contraseñas se almacenan en valores de atributos que sólo estarán visibles para los usuarios o grupos de seguridad que se definan a través de Active Directory.
  • Las contraseñas de los administradores locales son únicas en cada máquina.
  • Las contraseñas se cambian de forma automática, estableciendo contraseñas aleatorias y complejas en base a patrones que previamente estén definidos en su propia plantilla GPO (AdmPwd.admx).
  • Transmite las contraseñas al cliente de manera segura y cifrada.
  • Permite controlar los permisos en usuarios o grupos para el acceso a la visualización y restablecimiento de las contraseñas.
  • Dispone de sus propios ID de eventos para su análisis en auditorías.

Instalación de LAPS

Instalador de LAPS (Local Administrator Password Solution).

En uno de los controladores de dominio, preferiblemente un PDC (Primary Domain Controller), instalamos y agregamos las características del cliente pesado, módulo de PowerShell y las plantillas del editor de políticas de grupo.

GPO CSE (Client-Side Extension) solo deberá estar presente en cada endpoint gestionado.

Figura 1: Instalar características para la gestión LAPS.

Figura 2: LAPS instalado.

Importamos el módulo AdmPwd.ps, se actualiza el esquema de Active Directory, y se definen las unidades organizativas donde se quieran aplicar los permisos para la gestión de LAPS.

Import-Module AdmPwd.ps
Update-AdmPwdADSchema
Set-AdmPwdComputerSelfPermission -Identity "<OU_AD>"

Figura 3: Importar módulo AdmPwd y actualizarlo en el esquema de AD.

El permiso de escritura en los atributos ms-Mcs-AdmPwdExpirationTime y ms-Mcs-AdmPwd de todas las cuentas del equipo tiene que ser añadido a la cuenta integrada. Esto es necesario para que la máquina pueda actualizar la contraseña y la fecha de caducidad de su propia contraseña de administrador local gestionada.

Para visualizar todos los cmdlets disponibles de un módulo podemos usar Get-Command y especificar el módulo.
Get-Command -Module AdmPwd.ps
Figura 4: Ver todos los cmdlets disponibles de un módulo.

Configuración GPO LAPS en Active Directory

Se crea una nueva GPO desde el editor de administrador de políticas de grupo (gpmc.msc). Al instalar la plantilla AdmPwd.admx de LAPS se añaden las nuevas políticas para establecer su configuración.
Computer Configuration > Polices > Administrative Templates > LAPS
Se habilita la política y en el caso de que el administrador local no sea el nombre por defecto (Administrator o Administrador) indicamos el nombre de la cuenta local tipo administrador que esté configurada en los equipos del dominio y los parámetros de las passwords como la: complejidad, longitud y expiración.

Figura 5: Crear y configurar GPO LAPS.

Desplegar el MSI de LAPS al resto de equipos del dominio a través de GPO

Es necesario desplegar el mismo instalador al resto de los equipos del dominio donde se necesite implementar LAPS ya sea en su versión de x64 o x86 y solamente con la característica por defecto habilitada.

La operativa de despliegue dependerá de la herramienta que se estemos usando para la gestión de endpoints de la infraestructura ya sea con SCCM System Center, Microsoft Intune, Puppet, etc.
msiexec /i <PATH>\LAPS.x64.msi /quiet
msiexec /i <PATH>\LAPS.x86.msi /quiet
Para este ejemplo al tratarse de un fichero Microsoft Installer .msi se puede desplegar directamente a través de otra GPO.
Computer Configuration > Policies > Software Settings > Software installation
Se indica la ruta donde está el instalador, el tipo de deployment será "Assigned" (sin modificaciones en sus características de instalación) ya que con la configuración por defecto solo instalaría la característica del cliente (GPO CSE).

Figura 6: Desplegar LAPS a los equipos del dominio a través de GPO. 

Funcionamiento y administración de LAPS

En los objetos tipo "Computer" de la unidad organizativa de Active Directory donde se delegaron los permisos para LAPS podemos comprobar que se han añadido dos nuevos atributos: 
  • ms-Mcs-AdmPwd: Almacena la password local del equipo.
  • ms-Mcs-AdmPwdExpirationTime: Tiempo en la que la password expirará y será renovada por otra password aleatoria cumpliendo los requisitos establecidos en la GPO.
A través del Editor de interfaces de servicios de Active Directory ADSI (adsiedit.msc) comprobamos como se han creado estos atributos y ver su valor desde la pestaña de editor de atributos de la propia ficha del objeto en la consola de Active Directory (dsa.msc).

Figura 7: Atributos de objeto donde se almacenará la password local aleatoria y rotatoria.

LAPS UI es la interfaz gráfica para una gestión más sencilla de LAPS donde podemos buscar por hostname un equipo del dominio para visualizar su password de administrador local, fecha de expiración y también restablecer su password (en caso de que tengamos permisos de escritura sobre los atributos comentados anteriormente).

Otra forma de visualizar la passwords es a través de PowerShell haciendo uso de los cmdlets que se importan a través del módulo AdmPwd.ps en la instalación de LAPS.
Get-AdmPwdPassword <HOSTNAME> | fl
Get-AdmPwdPassword <HOSTNAME>.Password
Figura 8: LAPS UI para ver y restablecer las passwords locales de los equipos de AD.

También es posible restablecer la password de administrador local de un equipo del dominio desde PowerShell.
Reset-AdmPwdPassword -ComputerName:<HOSTNAME>

Establecer permisos específicos según roles (RBAC)

Solo los usuarios privilegiados del dominio como los Domain Admins tienen permisos de lectura y escritura sobre los valores de los atributos donde se almacena la password y la fecha de expiración. 

En el caso de que se necesite conceder la capacidad de poder ver o editar estos valores de atributos a usuarios que no sean administradores, sino usuarios menos privilegiados a nivel de dominio que proporcionen mantenimiento y soporte a otros empleados de la compañía, se pueden crear grupos de seguridad en Active Directory de solo lectura (LAPS_R) y otro de lectura y escritura (LAPS_RW) donde se puedan asignar estos usuarios de soporte y de esta manera tener un control de permisos más granular.

En este caso asignamos el permiso de solo lectura sobre estos atributos al grupo "LAPS_R". Es posible conceder estos permisos de dos modos: 

Una forma automática a través del siguiente cmdlet donde se hará referencia el grupo de AD y la OU.
Set-AdmPwdReadPasswordPermission -Identity "<OU_AD>" -AllowedPrincipals "<Grupo_Solo_Lectura>"
Con el siguiente cmdlet verificamos la asignación de permisos asignados a la OU.
Find-AdmPwdExtendedRights -Identity "<OU_AD>"
Figura 9: Establecer un grupo de permiso de lectura para LAPS. 

La otra opción sería hacerlo de manera manual estableciendo las DACLs de la OU. En este caso agregando el grupo LAPS_R a las propiedades de seguridad de la OU y en los permisos avanzados permitir las siguientes ACEs "Read ms-Mcs-AdmPwd" y "Read ms-Mcs-AdmPwdExpirationTime".

Figura 10: LAPS - ACEs de lectura establecida a un grupo en una OU de AD.

En el caso que necesitemos agregar un grupo de usuarios para poder restablecer passwords, es decir otorgar permisos de escritura sobre los valores de los atributos de LAPS, usaremos el siguiente cmdlet.
Set-AdmPwdResetPasswordPermission -Identity "<OU_AD>" -AllowedPrincipals "<Grupo_Lectura_Escritura>"

Comprobar los permisos establecidos

Para comprobar de los permisos asignados anteriormente, el usuario de dominio "user2" no forma parte del grupo "LAPS_R" por lo tanto desde la consola de Active Directory no podrá visualizar los atributos de LAPS del equipo "PC10D2".

Figura 11: Comprobación de lectura sin permisos en el atributo de password de AD.

Para cambiar esto y conceder permisos de lectura. Se asigna el usuario "user2" al grupo "LAPS_R" de AD, se reinicia sesión con este usuario para aplicar los cambios y vemos como es posible visualizar desde el editor de atributos del tipo de objeto equipo el valor de la contraseña y la fecha de expiración. 

Figura 12: Comprobación de lectura correcta en el atributo de password de AD

Auditoría de eventos LAPS

La activación de auditoría de los usuarios que consultan y leen con éxito la contraseña de Administrador local de un equipo se puede activar mediante el siguiente cmdlet parte del módulo de AdmPwd.ps. 
Set-AdmPwdAuditing -OrgUnit: <OU_AD> -AuditedPrincipals: <GRUPO/USUARIO>
Cuando una contraseña se lee con éxito, se registra un ID de evento 4662 en el registro de seguridad del controlador de dominio.

Figura 13: Auditar eventos LAPS de consulta y lectura exitosa de la contraseña.

Saludos!

24 diciembre, 2021

Apache Log4j: Resumen de vulnerabilidades asociadas a Log4Shell

No suelo realizar publicaciones de este tipo pero considero este caso como una excepción.

"Log4j" o "Log4Shell" se han convertido en trending topic no solo en las comunidades de Ciberseguridad sino también en los entornos tecnológicos en general. 

Si hablásemos de un "Top Vulnerabilidades 2021" está claro que se lo llevarían las asociadas a Apache Log4j, el revuelo de esto no está tanto por el número de vulnerabilidades publicadas y su gravedad, sino por el impacto de afectación que está teniendo en una enorme cantidad de productos y servicios. El mayor problema es identificar las aplicaciones que lo usan, en muchos casos las empresas carecen de un inventario actualizado de las aplicaciones que utilizan. El inventario es fundamental en materia seguridad.

Por esta razón he realizado un resumen de las vulnerabilidades asociadas a Log4j publicadas hasta la fecha. (Este post se irá actualizado según se vayan publicando nuevas vulnerabilidades relacionadas)

CVE-2021-44228

  • Base Score: Crítico, 10.0
  • Publicación: 10/12/2021
  • Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
  • Versiones afectadas: Apache Log4j2 2.0-beta9 a 2.12.1 y 2.13.0 a 2.15.0.
  • CWE: Deserialización de datos, validación de entrada incorrecta, consumo incontrolado de recursos.
Vulnerabilidad Log4Shell original es una falla de deserialización. Otorga capacidades de ejecución remota de código (RCE) a atacantes no autenticados. Se encuentra explotable en componentes log4j-core, limitado a las versiones 2.x: desde la 2.0-beta9 hasta la 2.14.1 inclusive.

Se implementó una solución para Log4Shell en la versión 2.15.0, pero actualmente se considera incompleta.

CVE-2021-45046

  • Base Score: Crítico, 9.0
  • Publicación: 14/12/2021
  • Vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H
  • Versiones afectadas: 2.0.1 - 2.12.2 (excluido) y 2.13.0 - 2.16.0 (excluido).
  • CWE: Deserialización de datos.
Tras la explotación provoca una denegación de servicio (DoS). Log4j 2.16.0 soluciona este problema eliminando la compatibilidad con los patrones de búsqueda de mensajes y desactivando la funcionalidad JNDI de forma predeterminada.

CVE-2021-4104

  • Base Score: Alto, 8.1
  • Publicación: 14/12/2021
  • Vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
  • Versiones afectadas: JMSAppender en Log4j 1.2.
  • CWE: Deserialización de datos.
Aunque anteriormente se pensaba que Log4j 1.x era seguro, ya no es así. Esencialmente, la configuración no predeterminada de las instancias de Log4j 1.x que utilizan la clase JMSAppender también se vuelve susceptible a la falla de deserialización que no es de confianza. 

Este CVE afecta todas las versiones de los componentes log4j:log4j y org.apache.log4j:log4j para los cuales solo existen versiones 1.x. Debido a que estas son versiones al final de su vida útil, no existe una solución para la rama 1.x en ninguna parte, y se debe actualizar a Log4j-core 2.16.0 o superior. 

Es posible protegerse eliminando JMSAppender por completo de log4j-1.2.17.jar, con el siguiente comando. https://www.slf4j.org/log4shell.html
zip -d log4j-1.2.17.jar org/apache/log4j/net/JMSAppender.class

 

CVE-2021-42550

  • Base Score: Medio, 6.6
  • Publicación: 16/12/2021
  • Vector: CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:H
  • Versiones afectadas: Versión 1.2.7 de Logback y versiones anteriores.
  • CWE: Deserialización de datos.
Esta es una vulnerabilidad en el framework Logback, un sucesor de la biblioteca Log4j 1.x. Se han publicado versiones más recientes de Logback v1.3.0-alpha11 y v1.2.9 que abordan esta vulnerabilidad menos grave.

CVE-2021-45105

  • Base Score: Alto, 7.5
  • Publicación: 18/12/2021
  • Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
  • Versiones afectadas: Log4j2 2.0-alpha1 hasta 2.16.0 (incluido).
  • CWE: Validación de entrada incorrecta, recurrencia incontrolada.
Se descubrió que Log4j 2.16.0 era vulnerable a una falla DoS calificada como Alta. Apache ha lanzado una versión log4j 2.17.0 que corrige esta vulnerabilidad. 

CVE-2021-44832

  • Base ScoreMedio, 6.6
  • Publicación28/12/2021
  • VectorCVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:H
  • Versiones afectadas: Log4j2 2.0-beta7 hasta la 2.17.0 (excluyendo las versiones de corrección de seguridad 2.3.2 y 2.12.4).
  • CWE: Validación de entrada incorrecta, RCE (Ejecución remota de código).
Vulnerabilidad de ejecución remota de código (RCE) en el que un atacante con permiso para modificar el archivo de configuración de registro puede construir una configuración maliciosa utilizando un JDBC Appender con una fuente de datos que haga referencia a un URI JNDI que pueda ejecutar código remoto. Se trata una vulnerabilidad RCE con un score medio debido a su complejidad de explotación y al nivel de alto privilegio que el atacante debería conseguir previamente para explotar esta vulnerabilidad de forma exitosa.

Este problema se soluciona limitando los nombres de fuentes de datos JNDI al protocolo java en las versiones 2.17.1, 2.12.4 y 2.3.2 de Log4j2.

Saludos!

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.

Hardening o bastionado en servicios de Escritorio remoto RDP

Lista de acceso de usuarios mediante NLA y privilegios

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

Otras medidas de seguridad adicionales para un bastionado en servicios RDP

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!

Entradas Populares