Saltar a contenido

Práctica 2.2: SIEM (ELK) e IDS (Snort)

P2.2 - SIEM (ELK) e IDS (Snort)

1. Introducción

Los SIEM (Security Information and Event Management) son herramientas fundamentales en la gestión de la seguridad de la información. Permiten recopilar, analizar y visualizar datos de seguridad en tiempo real, facilitando la detección de amenazas y la respuesta a incidentes. En esta práctica, nos centraremos en el despliegue de un SIEM utilizando ELK Stack (Elasticsearch, Logstash y Kibana) y la integración de Snort como sistema de detección de intrusiones (IDS).

1.1 SIEM

Hemos visto que un SOC, en definitiva, es el lugar desde donde se controla la seguridad de una serie de sistemas.

Pero hay una pregunta clave: ¿cómo sabemos que todo está yendo bien? ¿cómo sabemos si alguien se ha saltado las restricciones impuestas y está atacando a la empresa? O incluso algo más simple: ¿cómo sabemos si el servidor está respondiendo a la carga de trabajo actual?

Un SIEM (Security Information and Event Management) es un sistema que nos permite recopilar datos, procesarlos, almacenarlos y visualizarlos. Estos datos pueden ser de distinta naturaleza, en función de los agentes de datos empleados.

Uno de los primeros pasos para montar un SIEM de forma adecuada es generar una lista de activos de nuestra infraestructura que queremos monitorizar. A partir de ese listado se decide qué tipo de datos queremos generar y enviar desde cada activo al SIEM.

1.2 Datos e información

Antes de avanzar, conviene distinguir entre datos e información. De los datos recogidos de los distintos sistemas (relacionados con el funcionamiento de los equipos), datos son, por ejemplo: el porcentaje de CPU usada, la cantidad de memoria libre, etc.

Por sí solos, descontextualizados, no nos indican gran cosa. Es en el análisis realizado en el SIEM donde estos datos empiezan a generar valor.

1.3 Infraestructura y PoC

Para poder monitorizar con un SIEM necesitamos una infraestructura. Es decir, el SIEM se ubicará en un entorno (un sistema) y los activos que queremos monitorizar serán sistemas distintos.

Para realizar nuestro PoC (Proof of Concept) tenemos varias alternativas:

  • Emplear distintas máquinas virtuales.
  • Emplear distintas máquinas físicas.
  • Emplear servidores VPS para simular la infraestructura.
  • Emplear contenedores.

El objetivo principal de este trabajo es implementar una solución con herramientas de código abierto en un entorno basado en contenedores, que permita monitorizar tráfico de red no deseado, anomalías de seguridad y la detección de intrusiones.

Para ello, se procesará, recopilará y presentará la información relevante mediante una interfaz gráfica fácilmente comprensible por cualquier usuario con acceso.

Con este fin se utilizará Snort como software de detección de intrusiones y ELK Stack, que será el encargado de recopilar los datos enviados por Snort, analizarlos y visualizarlos en tiempo real. Ambas herramientas funcionarán sobre contenedores Docker para aprovechar las ventajas que ofrece esta tecnología.

No vamos a realizar un curso de Docker en este momento, ni se explicarán las diferencias con respecto a una máquina virtual. Simplemente se expondrán y explicarán los comandos necesarios para generar la infraestructura. El objetivo es montar el SIEM y Docker es una herramienta eficiente para lograrlo.

2. Punto de partida

  • Contenedor 1: simula el agente de datos (endpoint).
  • Contenedor 2: simula el entorno SIEM del SOC, donde estará instalado ELK.

2.1. Elementos

ELK es un conjunto de productos desarrollados por Elastic, diseñado para recibir datos desde cualquier tipo de fuente y formato, transformarlos, realizar búsquedas, analizarlos e incluso visualizarlos de forma gráfica en un dashboard.

Proporciona un marco de trabajo (framework) que permite que, una vez reciba datos, podamos visualizarlos de forma sencilla. En lugar de conectarnos directamente a la base de datos, trabajaremos con el dashboard para ver el estado de la información.

ELK es un conjunto de productos (no una única aplicación), es decir, son componentes configurables por separado. El Stack está formado por Elasticsearch, Kibana y Logstash.

Además, se emplean distintos Beats (agentes que envían datos). El proceso natural de captura de datos es:

  • Generador de logs (datos).
  • Recolector de datos (Beats).
  • Procesador de datos (Logstash).
  • Almacén de datos (Elasticsearch).
  • Visualizador de información (Kibana).
2.1.1. Elasticsearch

Fundamentalmente, Elasticsearch es un motor de búsqueda en un entorno NoSQL, pero sin perder su naturaleza de base de datos distribuida.

Es compatible con datos generados desde multitud de fuentes, inyectándolos en los diferentes nodos que componen su cluster de almacenaje. De ahí que permita alta disponibilidad y una mayor tolerancia a fallos.

2.1.2. Logstash

Es la herramienta encargada de realizar la recolección de eventos en tiempo real, el análisis de formato y la selección del tipo de almacenamiento, justo antes de su procesamiento por parte de Elasticsearch.

Logstash actúa como agregador: extrae datos de varias fuentes antes de enviarlos (generalmente a Elasticsearch). En soluciones IDS suele ser necesario enriquecer/mejorar los registros, por ejemplo: agregando contexto a los mensajes originales, analizando campos por separado, o filtrando datos no deseados. Logstash es una de las opciones más potentes para esto.

Sin embargo, también añade complejidad. Por eso, ELK permite usar otros agregadores más simples, como Filebeat.

Precisamente, como buscamos una demostración sencilla de integración de elementos (más que una solución IDS robusta), en este trabajo se ha decidido partir de Filebeat para enviar los registros desde la fuente hasta Elasticsearch.

2.1.3. Beats

Son una familia de agentes:

Filebeat, y los otros miembros de la familia Beats, actúan como un agente ligero implementado en el host, que envía registros a Elasticsearch.

Filebeat es uno de los remitentes de logs más utilizados: es liviano, admite cifrado SSL/TLS, incorpora un buen mecanismo de recuperación y es altamente confiable. En nuestro caso se ha seleccionado para el envío de logs por ser ligero y robusto, y porque el sistema se implementará en entornos pequeños o medianos.

2.1.3. Kibana

Es una interfaz de usuario que permite la visualización de datos para su análisis posterior. En Kibana se puede consultar información desde índices de Elasticsearch para realizar búsquedas, visualizar y analizar datos, además de navegar por el Stack.

Pone a disposición del usuario diversas herramientas para visualizar datos (diagramas, histogramas, tablas, etc.). Entre las principales funciones están:

  • Análisis de datos aplicando distintas métricas.
  • Exposición de datos con diferentes tipos de gráficas o diagramas.
  • Comprobación del estado de ELK.
  • Generación de reportes.
  • Exploración y descubrimiento de datos.
  • Creación de alertas.
  • Implementación de TLS como cifrado de comunicaciones.

2.2. Instalación del entorno ELK

El primer paso es instalar Docker:

Para probar que Docker funciona, puedes ejecutar algún comando de ayuda, por ejemplo docker help, o bien:

docker run hello-world

Debería aparecer por consola una serie de mensajes que indican que Docker se ha instalado con éxito.

Por defecto, si estás en Linux, Docker suele necesitar permisos de sudo. Esta configuración se puede mantener o cambiar siguiendo el tutorial: https://docs.docker.com/engine/install/linux-postinstall/

Además, en estas cheatsheets se puede encontrar ayuda para los comandos básicos:

La documentación de la imagen Docker que vamos a usar es la siguiente:

Para descargar la imagen en nuestro equipo, ejecutamos el siguiente comando:

cuidado con las versiones

A la fecha de la redacción de esta guía, la última versión disponible es la 9.3.0. La práctica se inició con la versión 7.16.3. Puedes probar con la última versión o cualquier otra, pero asegúrate de descargar la misma versión en todos los componentes para evitar problemas de compatibilidad con Filebeat.

docker pull sebp/elk:7.16.3

Este comando descarga la imagen al equipo. Una imagen de Docker puede entenderse como un conjunto de instrucciones para generar un contenedor: una imagen en funcionamiento es un contenedor. Es decir, la imagen es el plano, el contenedor es la casa construida a partir de ese plano.

2.3. Configuración

Todas las piezas juntas

Una buena práctica con Docker es crear redes propias (externas al resto de contenedores) si se van a emplear contenedores trabajando de forma conjunta, para así poder asignarles IPs fijas y evitar problemas de comunicación entre ellos. Para crear una red con Docker:

docker network create -d bridge --subnet 172.20.0.0/24 elk-red

Creamos una red de tipo bridge por si necesitáramos conectarnos a Internet a través del NAT del PC (algo habitual, por ejemplo, en un servidor web).

Una vez descargada la imagen, ejecutamos el contenedor:

cuidado con las versiones

Recuerda la versión que has descargado. En el ejemplo se muestra la versión 7.16.3, pero si has descargado otra, asegúrate de usar la misma versión en el comando.

docker run -p 5601:5601 -p 9200:9200 -p 5044:5044 -it \
  --name elk --net elk-red --ip 172.20.0.10 -d sebp/elk:7.16.3

Este comando ejecuta la imagen y genera el contenedor. El objetivo de esta práctica no es explicar Docker en profundidad, pero sí comprender aspectos importantes.

Algunas opciones del comando:

  • -p: mapea puertos internos del contenedor a puertos del host (5601, 9200 y 5044).
  • --name: asigna un nombre al contenedor para poder arrancarlo, pararlo, ver logs, etc.
  • -it: combina:
    • -i (interactive), para mantener stdin abierto.
    • -t, para crear un pseudo-terminal y comunicarnos con el contenedor.

Cuando el contenedor esté arrancado, puedes acceder a:

Se exponen estos tres puertos porque la imagen ejecuta tres servicios:

  • Kibana (5601): interfaz web (frontend) que consulta a Elasticsearch. Ejemplo: http://<tu-host>:5601.
  • Elasticsearch (9200): permite consultas a su API.
  • Logstash (5044): puerto donde Logstash espera inserciones para actuar como middleware.

Si todo ha ido bien, ahora puedes acceder a localhost:5601 y ver la interfaz gráfica de Kibana.

Si ha ocurrido un error, la forma más fácil de verlo es revisar los logs:

sudo docker logs --follow NOMBRE_CONTENEDOR

Con --follow, el comando se queda esperando y mostrando nuevos logs. Si solo quieres ver los logs actuales, omite ese parámetro.

Un problema habitual es quedarse sin memoria. En ese caso, puede aparecer algo como:

max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

Esto indica que la configuración del kernel vm.max_map_count debe establecerse al menos en 262144.

Para cambiarlo:

sudo sysctl -w vm.max_map_count=262145

Para persistir la corrección, añade esa configuración al final del archivo /etc/sysctl.conf (a veces también se usa /etc/sysctl.d/99-sysctl.conf), por ejemplo:

sudo pico /etc/sysctl.d/99-sysctl.conf

Después, vuelve a ejecutar el contenedor.

2.4. Otros comandos que puedes necesitar

  • Listar contenedores que están corriendo:

    docker container ls
    
  • Listar todos los contenedores (incluyendo parados):

    docker container ls -a
    
  • Listar redes:

    docker network ls
    
  • Parar y arrancar el contenedor elk:

    docker stop elk
    docker start elk
    
  • Borrar el contenedor elk:

    docker container rm elk
    
  • Ver logs del contenedor elk:

    docker logs elk --follow
    
  • Lanzar un bash interactivo dentro del contenedor elk:

    docker exec -it elk bash
    
  • Inspeccionar el contenedor elk:

    docker container inspect elk
    

2.5. Mas sobre ELK Docker

Es posible que ahora mismo consideres suficiente lo que ya sabes, pero existen muchos aspectos de configuración que no hemos visto en esta unidad:

Lógicamente se puede instalar el Stack sin hacer uso de Docker, pero la experiencia no ha sido buena en cuanto a la dedicación que necesita. Aquí puedes ver algunos enlaces que indican como hacerlo:

2.6. Agente de datos

Para simular una infraestructura, vamos a desplegar un contenedor que hará las funciones de servidor web. En este contenedor tendremos instalado Nginx (servidor web) y, como agente de datos, Filebeat.

Vamos a comenzar por el primero de los Beats. Para instalar Filebeat podemos hacerlo de forma nativa o a a través de Docker. Documentación oficial:

En nuestro caso, para esta prueba de concepto, podemos emplear el proyecto proporcionado por el repositorio:

Ese repositorio es del creador de la imagen usada anteriormente. La imagen que vamos a usar es un servidor Nginx monitorizado con Filebeat.

Para instalar la imagen:

  • Descarga el ZIP del proyecto, extráelo:

    tar -xvzf elk-docker-master.tar.gz
    

    O clónalo:

    git clone https://github.com/spujadas/elk-docker
    

  • Accede por línea de comandos a la carpeta nginx-filebeat, que hay dentro del repositorio:

    cd elk-docker-master/nginx-filebeat
    

Antes de continuar, modifica el archivo filebeat.yml. Donde pone elk:5044, sustituye elk por la IP del SIEM de ELK. En el caso de nuestro ejemplo: 172.20.0.10.

Por defecto, Filebeat está configurado para escuchar los archivos syslog y auth.log y enviarlos. En la recepción (en el SIEM) se recibirán como parte de filebeat-syslog.

Además, está escuchando los logs de Nginx. Estos se enviarán como nginx-access.

Como vemos en la parte superior, estamos enviando esta información directamente a Logstash. Es importante que cambiemos donde pone elk por la IP de nuestro SOC.

El siguiente paso es generar la imagen a partir del Dockerfile:

cuidado con las versiones y erratas

Recuerda la versión que has descargado de ELK. En el ejemplo se muestra la versión 7.16.3, pero si has descargado otra, asegúrate de usar la misma versión. Edita antes el fichero dockerfile y modifica la versión de filebeat, para que quede la versión que has descargado de ELK. Por otra parte, es posible que te encuentres con alguna errata en el fichero, derivada de un conflicto al realizar un merge entre ramas. Si es así, corrige el error para que el fichero quede con el formato correcto.

docker build . -t nginx-filebeat

Donde:

  • .: indica que el contexto de construcción es el directorio actual.
  • -t: asigna un nombre a la imagen generada.
  • nginx-filebeat: nombre de la imagen que se va a crear.

Seguidamente, ejecuta el contenedor:

docker run -p 80:80 -it --name filebeat --net elk-red -d nginx-filebeat

Donde:

  • -p: expone el puerto 80 al exterior.
  • -it: necesitamos que el contenedor se cree interactivo.
  • -d: lo inicia en segundo plano y nos deja la consola libre.
  • --name: asigna un nombre al contenedor, en este caso filebeat.
  • --net: indica la red a emplear, en este caso elk-red (la red que creamos anteriormente).

Podríamos haberlo creado dándole una IP fija, pero al ser un contenedor que se va a comunicar con otro (ELK) que sí tiene IP fija, no es necesario. De hecho, lo habitual es que los agentes de datos no tengan IP fija, sino que se comuniquen con el SIEM a través de la red.

2.7. Otras consideraciones sobre el agente de datos

Recuerda que, si queremos ver los logs que va generando el contenedor, podemos salir de la shell del contenedor y escribir:

docker logs --follow filebeat

Recuerda que en el archivo de configuración de Filebeat (/etc/filebeat/filebeat.yml) debes indicar la IP del SIEM. Si no la recuerdas, usa:

docker network inspect elk-red

En la imagen de nuestra infraestructura, Logstash está configurado con SSL por defecto. Dado que al principio puede dar algún problema, y será una tarea opcional, vamos a deshabilitarlo para facilitar que todo funcione correctamente.

Modificamos el archivo:

docker exec -it elk bash
vim /etc/logstash/conf.d/02-beats-input.conf
Para que quede asi:

input {
    beats {
        port => 5044
        ssl => false
        ssl_certificate => "/etc/pki/tls/certs/logstash-beats.crt"
        ssl_key => "/etc/pki/tls/private/logstash-beats.key"
    }
}

Recuerda que cualquier cambio en la configuración necesitará parar y reiniciar el contenedor.

Por último, podrás acceder a Kibana (por ejemplo http://172.19.0.3:5601) y crear tu propio cuadro de mandos.

Pregunta: ¿qué modificaciones serán necesarias para crear un pequeño cuadro de mando para la visualización de la información?

3. Introducir Snort (IDS) en el contenedor 1

Una vez que tenemos el SIEM funcionando, podríamos decidir monitorizar el tráfico de red, en lugar de los logs del sistema o aplicaciones. Para ello, necesitamos un IDS (Intrusion Detection System) que capture el tráfico de red y lo envíe al SIEM.

3.1. Objetivo

Se pretende partir de la situación anterior y modificar el contenedor 1 para que contenga snort.

Posteriormente se configurará snort para detectar posibles eventos dentro del contenedor y lanzar las alarmas oportunas, de forma que Filebeat las recoja y las envíe al Stack ELK.

3.2. Snort

Para la realización del proyecto se ha decidido utilizar Snort como IDS de referencia. Snort es uno de los IDS/IPS de código abierto más utilizados. Usa reglas que ayudan a definir actividad de red maliciosa y las emplea para encontrar paquetes que coincidan con ellas y generar alertas.

Snort, además, es un sistema de detección de intrusiones en red (NIDS) basado en la detección de usos no permitidos en la red, analizando tráfico y capturando paquetes en tiempo real. Puede analizar protocolos, buscar contenidos y detectar multitud de ataques, por ejemplo: desbordamientos de búfer, escaneo de puertos e intentos de OS Fingerprinting (recopilación de datos para obtener información del sistema operativo u otro software), entre muchos otros.

3.3. Escenario de ataque

Una vez tenemos el SIEM funcionando, y planteado el objetivo, es hora de ver como podemos generar tráfico de red malicioso para que Snort lo detecte y lo envíe al SIEM. Para ello, se plantea el siguiente escenario de ataque:

  • La máquina atacante será un Kali Linux, en el que se utilizarán ping e hydra.

    • Se generan diccionarios con base en la información obtenida con ingeniería social: se sabe que el usuario está compuesto por dos letras minúsculas, y su clave por una letra minúscula y un número.
    • Con base en esas pistas, se generarán diccionarios de usuarios y claves necesarios para el ataque. En caso de no aportar diccionarios, el ataque se efectuaría por fuerza bruta y se podría eternizar. Los scripts crear_usuarios y crear_claves se han creado para generar los diccionarios necesarios con la herramienta crunch:

      kali@kali:~/PRACTICA_HYDRAS$ cat crear_usuarios
      crunch 2 2 -t @@ -o usuarios.txt
      
      kali@kali:~/PRACTICA_HYDRAS$ cat crear_claves
      crunch 2 2 -t @@ -o claves.txt
      
    • Se lanza una batería de ataques con diferentes herramientas y diccionarios.

      • Se conoce la dirección de la máquina a atacar: primero se comprobará con ping (ICMP) si la máquina está operativa.
      • Después, se atacará mediante Hydra usando el protocolo SSH.
      kali@kali:~/PRACTICA_HYDRA$ cat lanzar_hydra
      hydra -L usuarios.txt -P claves.txt -o resultados.txt -t 4 -I ssh://192.168.1.29:22
      

3.4. Instalación y configuración de Snort

  • La máquina objetivo será el contenedor 1, en el que instalaremos y configuraremos Snort para detectar y registrar los ataques.

    • Instalar:
    sudo apt install snort
    
    • Comprobar el estado y arrancar el servicio:
    sudo systemctl status snort
    sudo systemctl start snort
    
    • Logs de Snort: /var/log/snort_alerts.log
    • Configuración de reglas: un ejemplo para detectar ICMP.
3.4.1. Reglas de Snort

Las reglas de Snort se generan definiendo instrucciones específicas basadas en texto que Snort utiliza para analizar el tráfico de red, buscando patrones, cabeceras o comportamientos maliciosos. Estas reglas se escriben utilizando un formato propio, el cual consta de dos partes principales: un encabezado y opciones de regla.

El formato de una regla de Snort se divide en:

  • Encabezado (Rule Header): Define la acción (alert, log, pass, drop), el protocolo (ip, tcp, udp, icmp), direcciones IP de origen/destino y puertos.
  • Opciones (Rule Options): Van entre paréntesis y definen los detalles del ataque, como el mensaje a mostrar (msg), el contenido a buscar (content), la firma de identificación (sid) y la revisión (rev).

Un ejemplo para la siguiente regla: "Generar una alerta para tráfico TCP desde la dirección IP

alert tcp 192.168.1.200 any -> $HOME_NET any (msg:"Trafico TCP desde Claudia"; sid:666003;)

Header

Campo Valor
Acción de la regla Alerta
Protocolo TCP
Dirección IP origen 192.168.1.200
Puerto IP origen Cualquiera
Dirección de la operación De 200 a nuestra red local (cualquier dirección de esta red)
Dirección IP destino 192.168.1.0
Puerto IP destino Cualquiera

Trailer

Campo Valor
Mensaje Tráfico TCP desde Claudia
Opciones Identificador de la regla (666003)
3.4.1.1. Regla ICMP

Grabaremos una regla para detectar tráfico ICMP (ping) en el fichero de configuración de Snort: /etc/snort/rules/local.rules

# Regla para detectar un ping (ICMP)
alert icmp any any -> $HOME_NET any (msg:"Trafico ICMP!"; sid:3000001;)

Tras reiniciar Snort, podremos comprobar la generación de alertas en tiempo real con el siguiente comando:

tail -f /var/log/snort_alerts.log
3.4.1.1. Regla SSH

Lo siguiente paso es configurar una regla para detectar tráfico SSH. Ya que estamos intentando detectar un ataque de fuerza bruta, lo ideal es configurar la regla para que solo se active cuando se detecte tráfico SSH con una conexión establecida (es decir, no queremos que se active por un simple escaneo de puertos, sino por un intento de conexión real). Además, para evitar que se generen demasiadas alertas (por ejemplo, si el atacante lanza un ataque de fuerza bruta con muchas conexiones), podemos configurar la regla para que solo se active una vez cada cierto tiempo por cada IP de origen.

Para ello, añadiremos la siguiente regla al mismo fichero de configuración:

# --------------------
# LOCAL RULES
# --------------------
# This file intentionally does not come with signatures.  Put your local
# additions here.

# PRACTICAS - Regla para detectar un ping (ICMP)
# NOTA: Comentamos esta regla para efectuar la prueba unitaria de detección de SSH
#alert icmp any any -> $HOME_NET any (msg:"¡Trafico ICMP!"; sid:3000001;)

# PRACTICAS - Regla para detectar un ssh (TCP y puerto 22)
alert tcp any any -> any 22 (msg:"Acceso SSH"; sid:3000002; flow:established; threshold: type limit, track by_src, count 1, seconds 30;)

Tras grabar la regla, limpiaremos el fichero de log, reiniciaremos Snort, abriremos dos terminales y lanzaremos el ataque con Hydra.

Pregunta: ¿qué modificaciones serán necesarias para conectar Filebeat?

¿Qué modificaciones serán necesarias para crear un pequeño cuadro de mando para la visualización de la información?

¿Qué información llega al SIEM? ¿Qué información se muestra en el cuadro de mando? ¿Qué información se muestra en los logs de Snort?