Full text
- 1 - 1Equation Chapter 1 Section 1 1 Proyecto Fin de Grado Grado en Ingeniería de las Tecnologías de Telecomunicación redBorder Malware EndPoint Autor: Adrián Rodríguez García Tutor: Pablo Nebrera Herrera Dep. Telemática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2016
14
1 Proyecto Fin de Grado Grado en Ingeniería de las Tecnologías de Telecomunicación redBorder Malware EndPoint Autor: Adrián Rodríguez García Tutor: Pablo Nebrera Herrera Profesor titular Dep. Telemática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2016
2
3 Proyecto Fin de Grado: redBorder Malware EndPoint Autor: Adrián Rodríguez García Tutor: Pablo Nebrera Herrera El tribunal nombrado para juzgar el Proyecto arriba indicado, compuesto por los siguientes miembros: Presidente: Vocales: Secretario: Acuerdan otorgarle la calificación de: El Secretario del Tribunal Fecha:
4
5 Agradecimientos RedBorder Malware EndPoint ha sido mi primer contacto con el mundo laboral. Estoy satisfecho con esta experiencia, que me ha ayudado a aprender y a superarme día a día. En primer lugar, quisiera agradecer a mi padre el sacrificio realizado para poner a mi disposición todos los medios necesarios para estudiar, que me han permitido llegar hasta aquí. Agradecer también a todo el equipo de redBorder. En especial a Carlos Jiménez, por el trato recibido por su parte, por su dedicación y por su esfuerzo para que este proyecto sea a día de hoy una realidad. A Verónica, por hacer todo el trayecto universitario conmigo y hacerlo mucho más fácil y llevadero, por haber estado en los buenos y malos momentos y por ayudarme cada vez que lo he necesitado. Por último, quiero dar las gracias a mis amigos, los que durante toda esta etapa me han tenido que soportar tanto dentro como fuera de la universidad. Por todos ellos, ¡¡Venga a ustedes!! Adrián Rodríguez García Sevilla, 2016
6
7 Resumen Los cibercriminales son una de las principales amenazas a las que se enfrentan las empresas día tras día en el ciberespacio. Incluso compañías con directrices estrictas en materia de ciberseguridad pueden quedar expuestas. Por este motivo, es necesario implantar medidas de seguridad en todos los rincones de las empresas, incluido los equipos de trabajo, herramienta tan necesaria como vulnerable a ciberataques. De esta conclusión nace redBorder Malware EndPoint, incluido dentro del módulo de Malware del portfolio de productos de redBorder. redBorder Malware EndPoint es un sistema de ciberseguridad para equipos finales, con inteligencia artificial, que detecta y evita, en tiempo real, amenazas, malware y ataques dirigidos a empresas. Trabaja en cooperación con la aplicación de redBorder Malware para reportar y bloquear malware en base a un sistema de reputación.
14 Figura 27 - Funcionamiento de los workers ........................................................................................................... 45 Figura 28 - Estado de las tareas ............................................................................................................................... 46 Figura 29 - Tarea de monitorización de particiones lógicas .................................................................................. 48 Figura 30 – Tarea de captura de ficheros ................................................................................................................ 49 Figura 31 - Tarea de captura de procesos creados ................................................................................................. 51 Figura 32 - Tarea de captura de conexiones de red ................................................................................................ 53 Figura 33 - Tarea de envío de eventos .................................................................................................................... 53 Figura 34 - Evento de detección de un fichero desconocido ................................................................................. 54 Figura 35 - Evento de detección de un fichero conocido ...................................................................................... 55 Figura 36 – Evento de detección de un fichero borrado ........................................................................................ 55 Figura 37 - Evento de detección de un fichero renombrado ................................................................................. 56 Figura 38 - Evento de detección de un proceso creado ......................................................................................... 56 Figura 39 - Evento de detección de un proceso destruido ..................................................................................... 57 Figura 40 - Evento de detección de una conexión de red ...................................................................................... 57 Figura 41 - Evento de detección de un fichero desconocido en rb_endpoint ...................................................... 58 Figura 42 – Evento de detección de un fichero conocido en rb_endpoint ........................................................... 59 Figura 43 - Arquitectura de integración de redBorder Malware EndPoint .......................................................... 61 Figura 44 - Descarga de redBorder Malware EndPoint desde la web .................................................................. 62 Figura 45 - Elección de la arquitectura de redBorder Malware EndPoint ........................................................... 62 Figura 46 - Diagrama de instalación ....................................................................................................................... 63 Figura 47 - Lista de EndPoints registrados ............................................................................................................. 63 Figura 48 – Descarga de redBorder Malware EndPoint vía web ......................................................................... 65 Figura 49 - Instalador ............................................................................................................................................... 66 Figura 50 - Servicios instalados .............................................................................................................................. 66 Figura 51 - Registro web de un EndPoint .............................................................................................................. 67 Figura 52 - Parámetro client_id ............................................................................................................................... 67 Figura 53 – Creación de un fichero ......................................................................................................................... 68 Figura 54 - Fichero nuevo en rb_endpoint ............................................................................................................. 69 Figura 55 - Fichero nuevo en rb_ioc ....................................................................................................................... 69
15 Figura 56 - Fichero nuevo en Riak .......................................................................................................................... 69 Figura 57 - Fichero nuevo en la web ....................................................................................................................... 70 Figura 58 - Creación del proceso notepad .............................................................................................................. 70 Figura 59 – Evento de creación del proceso notepad en Kafka ............................................................................ 71 Figura 60 - Creación de una conexión web ............................................................................................................ 71 Figura 61 - Evento de creación del proceso de conexión web .............................................................................. 72 Figura 62 - Creación de una alerta de IOC ............................................................................................................. 72 Figura 63 – Alerta de IOC ....................................................................................................................................... 73 Figura 64 - Descarga del histórico de eventos ........................................................................................................ 74 Figura 65 - Contenido del histórico de eventos ...................................................................................................... 74 Figura 66 - Actividad de un EndPoint durante la instalación del software .......................................................... 75 Figura 67 - Actividad de Disco de un EndPoint durante la instalación del software ........................................... 76 Figura 68 - Actividad de la memoria RAM de un EndPoint durante la instalación del software ....................... 76 Figura 69 – Actividad del tráfico de red de un EndPoint durante la instalación del software ............................ 77 Figura 70 - Actividad del sistema durante la instalación del software .................................................................. 77 Figura 71 - Gráfica de procesado de lotes de ficheros ........................................................................................... 79 Figura 72 - Gráfica de uso de CPU ......................................................................................................................... 80 Figura 73 - Gráfica de uso de memoria RAM ........................................................................................................ 81
16
17 1 INTRODUCCIÓN. “En Internet hay lobos y corderos. Si no eres lobo, te toca ser cordero.” - Chema Alonso – 1.1 Motivación oy en día, la seguridad cibernética es una de las mayores preocupaciones en el sector empresarial, debido a los continuos ataques contra compañías y organizaciones para el propósito de obtener información. Este tipo de situaciones ocurren cada vez con mayor frecuencia e incluso existen organizaciones de cibercriminales que se benefician de estas actividades. Por este motivo, es necesario tener una herramienta anti-malware que además de incluir el análisis del tráfico de red (NAT) y herramientas de seguridad cibernética específicos (IPS, IDS, Mail Gateway, etc.), incluya protección para EndPoints1. Los EndPoints son pilares fundamentales en la detección de amenazas avanzadas, malware o ataques dirigidos y mantener, gracias a la inteligencia artificial, toda la infraestructura de red segura. Este proyecto, cubrirá la necesidad de protección para EndPoints y se integrará dentro de redBorder Malware, donde convivirá con los demás sistemas que este alberga. 1.2 Objetivo El propósito general de este proyecto es el desarrollo de un sistema de ciberseguridad para equipos finales que detecte en tiempo real amenazas avanzadas, malware, ataques dirigidos, y que reporte dicha información al sistema central de redBorder Malware para practicar un análisis, y permitir de este modo tomar decisiones para resolver cualquier incidencia. Una vez realizado el desarrollo y la integración del proyecto dentro de la plataforma de redBorder Malware, se realizarán pruebas de concepto y de rendimiento para demostrar el óptimo funcionamiento del mismo como herramienta anti-malware. 1.3 Metodología Antes de la realización del proyecto se realizará una introducción a redBorder Malware y todos los elementos que intervienen dentro del sistema. Adicionalmente se hablará de Apache Kafka, Riak KV y de Google Rapid Response. Todo ello es tratado en la sección 2. A continuación, se introducirá el sistema en la sección 3 y se explicará la arquitectura del EndPoint y de todos los módulos que lo componen en la sección 4. Seguidamente se comenzará con el diseño, implementación y desarrollo, entrando en detalle en cada uno de los módulos que componen el EndPoint y sus funcionalidades. Todo ello, explicado en la sección 4. Una vez desarrollado, en la sección 5, se integrará dentro de la aplicación de malware de redBorder. Por último, en la sección 6, se realizarán diferentes pruebas de concepto, rendimiento y fiabilidad para demostrar el óptimo funcionamiento del sistema, finalizando con la sección 7 de presupuesto. 1 Equipos finales de una red, haciendo referencia a puestos de trabajo en ámbito empresarial. H
18
19 2 CONTEXTUALIZACIÓN. 2.1 Introducción a redBorder Malware redBorder es un sistema de seguridad y monitorización de redes en tiempo real y altamente escalable que permite disponer de una gran visibilidad de todo lo que ocurre en las redes monitorizadas, procurando un gran control de la infraestructura y de los servicios ofrecidos en ésta. Este sistema ofrece varios módulos de servicios que pueden ser contratados por los clientes. Entre ellos se encuentra redBorder Malware, que es un sistema de ciberseguridad para detectar malware y evitar la propagación del mismo y cualquier daño que pueda causar en la red. Figura 1 Arquitectura de redBorder Malware Tal y como se muestra en la anterior imagen, el escenario está formado por una red conmutada con 5 elementos principales:
20 Manager (clúster): elemento central en el cual se recibe, se trata y se visualiza toda la información emitida por los diferentes elementos. En su interior se incluye el servidor ICAP, que recibe las consultas/peticiones del proxy web. IPS: elemento por el cual pasa el tráfico de red que se va a analizar y, que funcionando como un IDS/IPS, es capaz de detectar patrones de tráfico correspondientes a posibles ataques y/o Malware. Una vez registrado con un manager, le envía los resultados, así como los ficheros detectados mediante los protocolos HTTP, FTP, SMTP y SMB. Mail Gateway: elemento por el cual pasa el tráfico de correo (SMTP). Funciona como un sencillo MTA2, ya que cada vez que recibe un email lo reenvía, por una parte, al servidor de correo correspondiente y, por la otra, una copia de los emails con adjuntos al manager, junto con datos adicionales del email (ej. Dirección remitente/destinatario...). Mail Server: servidor de correo electrónico utilizado únicamente para la recepción del correo interceptado por el Mail Gateway y autorizado para su reenvío. EndPoints: elementos finales de la red, donde se instalará este proyecto con el fin de detectar en tiempo real amenazas, malware y ataques a través del análisis de información del sistema, reportando la misma al manager. Cabe mencionar que en uno de los puentes de red se ha configurado un SPAN3 a nivel local para habilitar la interfaz de red del puente que lo une con el puente más externo para copiar todo el tráfico hacia/desde Internet en otra interfaz. En esa interfaz de destino es donde se conecta la interfaz del IDS/IPS. Destacar que redBorder implementa más servicios de los mencionados aquí, pero se ha optado por introducir sólo redBorder Malware para comprender mejor el ecosistema y funcionamiento de este proyecto. 2.2 Tecnologías utilizadas 2.2.1 Apache Kafka Servicio distribuido, particionado y con posibilidad de replicación que ofrece la funcionalidad de un sistema de colas de mensajes con el paradigma publicador/subscritor. Los eventos generados por los distintos elementos de redBorder Malware pasan por este servicio en el Manager, a fin de desacoplar la obtención de datos de su procesamiento. El EndPoint usa Apache Kafka para enviar información relativa a eventos del sistema al Manager y así informar en tiempo real de lo sucedido en los equipos finales de la red. 2.2.2 Google Rapid Response (GRR) GRR es un framework para dar una respuesta rápida remota a incidente forenses. La estructura es cliente/servidor, donde el servidor se encuentra en el Manager y el cliente en los EndPoints. De esta forma, dentro de redBorder Malware se usa para parar/reiniciar redBorder Malware EndPoint, bloquear los equipos en cuanto se detecte algún tipo de riesgo para red y obtener información de un sistema final para su análisis forense. 2 Mail Transfer Agent (Agente de Transferencia de correo) se encarga de recibir y reenviar correos. 3 Modo de configuración de una interfaz de red en un puente de red que permite enviar copias de paquetes a otra interfaz de red que esta siendo monitorizada.
21 2.2.3 Riak Servicio de almacenamiento de objetos fiable y de alta disponibilidad. Es utilizado sobre todo para el almacenamiento profundo en Druid. Todos los ficheros que se envían desde los EndPoints para ser analizados en busca de malware, usan el servicio de Riak para almacenar en Druid. 2.2.4 Druid Es un sistema de almacenamiento y análisis de información que permite la consulta y exploración de grandes cantidades de datos en tiempo real. 2.2.5 Windows Manager Instrumentation (WMI) Windows Management Instrumentation (WMI) es la infraestructura de datos y operaciones de gestión en sistemas operativos basados en Windows. Permite automatizar tareas administrativas y suministrar datos de gestión a otras partes del sistema operativo y productos. En este proyecto se ha usado para gestionar en tiempo real los cambios producidos en el sistema de fichero de Windows y para poder gestionar los cambios en los procesos. 2.2.6 Python Lenguaje de programación interpretado usado para la realización de este proyecto. La elección de este lenguaje es debido a la enorme potencia que contienen sus librerías para el sistema operativo Windows.
22
23 3 DESCRIPCIÓN DEL SISTEMA. 3.1 Introducción a redBorder Malware EndPoint. redBorder Malware EndPoint es un sistema de ciberseguridad, para equipos finales de una red, con inteligencia artificial que detecta en tiempo real amenazas, malware o ataques. Una vez detectada cualquier anomalía o amenaza se reporta a la unidad central de la plataforma redBorder Malware (manager), con capacidad de análisis, para estudiar y posteriormente determinar la acción a ejercer sobre un EndPoint. Figura 2 Detección de anomalías Este sistema, consigue un alto nivel de seguridad en EndPoints debido a que monitoriza de forma constante los procesos que se crean o destruyen, la red y cualquier cambio en el sistema de ficheros. La información obtenida se envía al manager para un análisis mediante IOC4 a la vez que se almacena de forma local, creando así un histórico de eventos para un posible análisis forense. Adicionalmente, cualquier cambio en el sistema de ficheros es detectado y el fichero implicado subido a Riak para analizarlo mediante cargadores5 en busca de 4 Indicadores de compromisos (Indicators Of Compromises) son un conjunto de reglas que permiten detectar incidencias o anomalías. 5 Conjunto de motores de análisis malware que emiten una valoración sobre una posible infección malware en un fichero.
30 Como se puede observar en la anterior imagen, existen los logs de salida para el histórico de eventos (binnacle) y para los logs de aplicación (application). Adicionalmente, se observa como cada logs lleva asignado su manejador (handler), en los cuales se indica el tipo de logs (INFO, DEBUG o ERROR), el tipo de formato y la clase (class). Destacar, que la clase elegida ha sido la rotativa o cíclica, es decir, que completado cierto tamaño de fichero de logs (50 Mb en este caso), se cree otro fichero de logs para continuar hasta un máximo de ficheros (5 en caso de los logs de aplicación y 10 en caso del histórico de eventos). Una vez completado el máximo de ficheros permitidos, se empezará a sobreescribir el primero. Figura 11 Ficheros cíclicos o rotativos o Parameters: Fichero de configuración en formato YAML que contiene todos los parámetros necesarios para poder configurar de forma correcta el agente EndPoint y que de esta forma pueda comunicarse con el manager de redBorder Manager mediante el sistema de reputación (sección 4.2.1), Riak o Apache Kafka. Figura 12 Fichero parameters Kafka_server: Parámetro que indica un alias del servidor donde enviar los eventos junto al puerto destino. En este caso, la IP correspondiente al alias se encuentra en el fichero “hosts”, que corresponde con un balanceador que se encarga de redirigir los eventos según unos criterios al manager adecuado dentro del clúster. S3_bucket: Indica el bucket, dentro de Riak instalado en el clúster de manager de redBorder Malware, donde se subirán los ficheros. S3_key: Parámetro que indica la ruta dentro del bucket de Riak donde subir los ficheros. Access_key y secret_key: En su conjunto, son dos parámetros que forman el equivalente a “usuario”, “contraseña” para poder acceder a Riak.
31 Reputation_server: Alias del servidor donde se encuentra el servicio de reputación (sección 4.2.1) del cual se realimenta la caché local. La IP que se corresponde a este alias se encuentra en el fichero “hosts”, al igual que las IP de Kafka o Riak. File_size: Parámetro que indica el tamaño máximo en bytes que un fichero puede tener para subirse a Riak. A partir de ese valor (128 Mb) el fichero no se subirá a la plataforma de malware, debido a que VirusTotal no analiza archivos superiores a ese tamaño, por tanto, no se analizarían de forma correcta. Versión_cache: Versión del sistema de reputación (sección 4.2.1), necesario para construir la URL de petición. Domain_id: Identificador de tipo numérico, que identifica de forma unívoca el dominio de red en el cual está un EndPoint. Score: Parámetro que indica una puntuación a partir de la cual los ficheros serán considerados como malware (se explica más detalladamente en la sección 4.2.1 dentro del sistema de reputación). Client_id: Identificador únivoco de un EndPoint asignado mediante GRR. Logs: Directorio en el cual se almacenan los logs de aplicación del agente EndPoint. Como se ha explicado anteriormente, se crearán un máximo de ficheros (cinco en este caso) de un tamaño determinado (50Mb particularmente), a partir de los cuales empezará a sobreescribirse el primero. Un ejemplo de logs de aplicación es la siguiente imagen: Figura 13 Ejemplo de logs de aplicación Events: Directorio que almacena el histórico de eventos que el agente EndPoint produce a lo largo del tiempo. Al igual que los logs de aplicación, estos ficheros son cíclicos, creándose un número determinado de ficheros (diez en este caso) con un tamaño determinado (50Mb en particular). En la sección 4.3 se detallan y explican todos los eventos producidos en el agente EndPoint.
32 EndPoint_agent: Son un conjunto de directorios que forman el agente de carga y contienen todo el código de la aplicación. Cabe destacar dos de los directorios: o db: Directorio donde se almacena la caché local del agente EndPoint. La caché local es una base de datos SQLite con dos campos (hash y score) que se realimenta con el sistema de reputación de redBorder Malware. Todos los ficheros detectados y capturados por el agente EndPoint son comparados contra la caché para dilucidar sin son conocidos o no y en caso de ser conocidos si es malware o está limpio. (La caché será tratada más en profundidad en la sección 4.2.1 de este proyecto). o Riak: Directorio donde se almacena una copia de todos los ficheros que son subidos a Riak. Estos ficheros son temporales, de forma que una vez subidos al manager, se destruyen. o Filter: Directorio donde se almacenan dos ficheros de configuración internos. Type file, es un fichero que describe las cadenas características de ciertos tipos de ficheros en una serie de bytes, permitiendo así conocer el tipo de un fichero. El otro, Type filter, es un fichero YAML que se configura en el manager y permite elegir que tipos de ficheros no se subirán a Riak para su análisis. Esto es debido porque quizás ficheros de tipo MP3, ODT, DOCX, etc, no son de interés para un determinado cliente. 4.1.2 Servicio de reputación de redBorder Malware El servicio de reputación es el encargado de proporcionar una API REST para poder hacer consultas de una URL, IP o hash y obtener una puntuación y también pone a disposición listas de reputación para los elementos de redBorder Malware que lo soliciten. Este servicio ofrece dos funcionalidades: Listas: Pueden ser de dos tipos, por un lado, existe la total, que ofrece el contenido íntegro de la caché remota, y por otro, la incremental, que permite obtener los últimos registros añadidos a la dicha caché. Consulta específica: Permite realizar una consulta por hash, URL o IP específica. Los tipos de consultas que se pueden realizarse al servicio de reputación son las siguientes: Method Path Function GET /reputation/vX/malware/total Permite obtener el contenido íntegro de la caché remota. GET /reputation/vX/malware/total/:filter Obtiene una lista total filtrada por hash, IP o URL. GET /reputation/vX/malware/incremental Devuelve el identificador de la última lista incremental liberada. GET /reputation/vX/malware/incremental/:id Permite obtener la lista incremental especificada a través del ID. GET /reputation/vX/malware/incremental/:id/:filter Permite obtener la lista incremental especificada a través del ID filtrada por hash, IP o URL. POST /reputation/vX/malware/query Permite realizar una consulta de un hash, IP o URL concretos. POST /reputation/vX/malware/status Permite saber en qué estado se encuentra un hash.
33 Tabla 1 Consultas al servicio de reputación Destacar que cuando se inicia una lista incremental, ésta comienza en la revisión 0.0 y cuando se libera el primer incremento pasará a ser la 1.0. Por tanto, por cada incremento que exista, aumentará en una unidad de la versión del sistema de reputación. Mencionar que para usar de forma correcta el servicio de reputación con listas incrementales ha de hacerse en dos pasos reflejados a continuación: Consultar la lista total, obteniendo así todo el contenido de la caché remota. Consultar de forma periódica el incremental, para posteriormente obtener el contenido del incremento. 4.2 Arquitectura del agente EndPoint El agente EndPoint consta de una estructura modularizada en tres partes que permite cumplir las necesidades y funcionalidades para las cuales ha sido diseñado. Figura 14 Arquitectura del agente EndPoint La arquitectura consta de tres módulos: Módulo principal (EndPoint Agent), que ha su vez consta de un submódulo interno para su configuración. Módulo de gestores (Managers), que son un conjunto de submódulos independientes para manejar y utilizar los servicios del entorno de redBorder. Módulo controlador (Monitor Controller), encargado de manejar y gestionar los workers y tareas del agente EndPoint.
34 4.2.1 Módulo principal (EndPoint Agent) Este módulo es el núcleo del agente EndPoint, debido a que se encarga de desplegar y comprobar el entorno mediante el submódulo de configuración. De igual forma, carga las librerías para su posterior uso, inicia y configura el módulo gestor e inicia el módulo controlador junto a las tareas, las cuales posteriormente son delegadas a éste. La secuencia de arranque y configuración es la mostrada en la siguiente imagen: Figura 15 Secuencia de inicio 4.2.1.1 Submódulo de configuración (configuration) Este submódulo del módulo principal se encarga de seis tareas muy definidas y distinguidas para que el agente EndPoint funcione de forma correcta: Carga las librerías (explicadas en la sección 4.1.1) y las deja listas para ser utilizadas. Carga el archivo de configuración de logs de aplicación e histórico de eventos (explicados en la sección 4.1.1). Carga el fichero con los parámetros necesarios para hacer uso de los servicios de reputación, Kafka y Riak. Inicia los logs de aplicación con la configuración obtenida anteriormente. Inicia el histórico de eventos de la misma forma que los logs de aplicación. Configura los submódulos de riak, kafka, caché y filtrado de parámetros del módulo gestor.
35 Figura 16 Tareas del submódulo de configuración Esta son las seis acciones que realiza el submódulo de configuración del módulo principal del agente EndPoint. Una vez realizadas, se inicia el módulo controlador, se inician las tareas y se asignan las tareas al controlador para que se encargue de su realización y gestión. 4.2.2 Módulo de gestores (Managers) Es un módulo compuesto por submódulos totalmente independientes entre ellos, que implementan una pequeña lógica, las cuales permiten obtener una serie de funcionalidades a las diferentes tareas del agente EndPoint de una forma transparente. Los submódulos que componen este módulo son los siguientes: Figura 17 Gestores
36 4.2.2.1 Gestor de la caché (Cache) El gestor de la caché se encarga de todas las gestiones relacionadas con la caché local y con el servicio de reputación, siendo el único elemento del agente EndPoint que entra en contacto con dichos servicios. Este gestor se puede subdividir en dos bloques que ofrecen diferentes funcionalidades: Sistema de reputación: Uno de los bloques es el que realiza las consultas a la caché remota del sistema de reputación de redBorder Malware. Para ello, crea un worker (sección 4.2.3.1) Dicho bloque dispone de una única funcionalidad, la cual se inicia desde el submódulo de configuración del agente EndPoint y estará activa hasta que el redBorder Malware EndPoint se apague. Esta funcionalidad, realiza las siguientes acciones: o Carga los parámetros de configuración (versión de la caché remota y el alias del sistema de reputación), construye la URL y envía una petición para obtener todo el contenido de la caché remota (sección 4.1.2), guardando dicho contenido en la caché local. o A continuación, entra en bucle, y cada cierto periodo de tiempo, lanza una petición para obtener la incremental de la caché remota y posteriormente su contenido. Con dicho contenido actualiza la caché local. Figura 18 Consulta caché remota Todas las respuestas de la caché remota relacionadas con el contenido son de tipo JSON, de los cuales, el gestor selecciona el hash y su score y los guarda en la caché local. Caché Local: El otro bloque del que se compone este gestor, se encarga de proporcionar una serie de funcionalidades al agente EndPoint para que haga consultas a través del gestor a la caché local cuando lo requiera. Esas funcionalidades se explican a continuación: o Comprobar si un hash existe en la caché local. o Obtener el score de un hash dado y existente en la caché local.
37 4.2.2.2 Gestor de Kafka (Kafka) El gestor de Kafka se encarga de gestionar las conexiones y envios de eventos hacia el manager de redBorder Malware, los cuales tienen un broker6. Para lograr dicho fin, crea un productor y pone a disposición del agente EndPoint las funcionalidades de conexión y envío de mensajes. Figura 19 Productor y consumidor de Kafka Se observa que un productor, como es el caso que aquí se describe, puede enviar eventos a distintos topics7, en este caso los topics son dos: rb_ioc rb_endpoint Adicionalmente, cada topic está compuesto de diferentes particiones donde enviar estos eventos. En este caso, la partición a la cual se envían los eventos viene dada por el identificador unívoco dado a cada EndPoint por GRR. Con esta información, el gestor pone a disposición del agente EndPoint las funcionalidades de conexión y envío de mensajes: Conexión con el clúster de broker: Cuando el submódulo de configuración inicia este gestor, le pasa el alias de los brokers a los cuales el cliente de Kafka debe conectarse y se realizan las siguientes acciones: o Se crea un cliente y se conecta al clúster. o Se crea un productor y se deja preparado para su uso. Envío de eventos a los brokers: Funcionalidad que está disponible para el agente EndPoint cuando este solicite su uso. Para ello, el agente EndPoint decide el topic y evento, y usa gestor para el envío del mensaje al manager. De forma adicional y transparente, esta funcionalidad cuenta con un sistema de gestión de errores, de forma que, si el error no es grave, reintenta envíar el mensaje una cantidad determinada de veces, en cambio, si el error resulta ser grave, se reinicia la conexión con el clúster de 6 Proceso que se encarga de publicar la información recibida de los productores y que está dividida en topics. 7 Estructura de un conjunto determinado de datos, cada topic tendrá unos datos y estructura distintos.
38 brokers. Figura 20 Secuencia del funcionamiento del gestor de Kafka En la imagen anterior se muestran las funcionalidades que el gestor de Kafka ofrece al agente EndPoint y se detalla el caso en el cual se produce un error grave o de conexión al enviar un mensaje. 4.2.2.3 Gestor de Riak (Riak) El gestor de Riak se encarga de proporcionar al agente EndPoint el mecanismo para enviar un fichero a Riak, dentro del manager de redBorder Malware. Para proporcionar este mecanismo es necesario configurar el gestor. El submódulo de configuración se encarga de iniciar el gestor y configurarlo enviándole los siguientes parámetros: Bucket 8al que subir las muestras. Ruta dentro del bucket donde subir los ficheros. Las claves secretas y de acceso configuradas por el manager para Riak. Una vez configurado, el gestor se queda a la espera de recibir peticiones para subir muestras. Una vez recibida la petición por parte del agente EndPoint, el gestor realiza las siguientes acciones para subir la muestra: Hace una copia del fichero recibido a la carpeta temporal de riak. Esta acción se realiza porque la subida del fichero puede tardar unos segundos, por tanto, el fichero estará siendo usado por el proceso de redBorder Malware EndPoint, dejándolo indisponible para otros procesos. De esta forma, el fichero que se sube a Riak es una copia del original, cuyo contenido, que es lo que realmente importa, es invariable repecto al original. Sube a Riak el fichero. Borra el fichero de la carpeta temporal. 8 Sistema de almacenamiento de objetos, del cual se pueden añadir o extraer datos.
39 Una vez realizadas las acciones anteriores, el fichero se encontrará en Riak dentro del manager de redBorder Malware para ser analizado en busca de malware. Tras el análisis, se generará un score que, junto al hash del fichero, se introducirá mediante una incremental en el sistema de reputación y que el gestor de la caché obtendrá en la siguiente consulta a la caché remota. Figura 21 Funcionamiento del gestor de Riak 4.2.2.4 Gestor de filtrado (Filter) El gestor de filtrado se encarga, mediante una pequeña lógica, de proporcionar funcionalidades al agente EndPoint de filtrado de ficheros. Es decir, se encarga de comprobar características de un fichero, de forma que el agente EndPoint decida si subir a Riak o no un fichero. Las funcionalidades de filtrado proporcionadas son las siguientes: Filtrado por ruta: Previamente a este proyecto, se ha hecho un estudio acerca de los diferentes ficheros de logs internos del sistema operativo Windows. Debido a que los mencionados archivos cambian de forma constante, provocarían constantes procesamientos innecesarios que ocuparían recursos en el proceso del agente EndPoint. Por este motivo, existe este filtrado, para cuando se detecten estos ficheros, ignorarlos. Filtrado por tamaño: Tal como se explico en la sección 4.1.1, en el fichero “parameters.yaml” existe un parámetro llamado “file_size”, que indica el tamaño máximo que puede tener un archivo para subirlo a Riak. Este filtrado se encarga, dado un fichero, de extraer su tamaño y compararlo con el máximo para indicar si es mayor, menor o igual y que de esta forma el agente EndPoint lo suba o no a Riak. Filtrado por tipo: Como se explico en la sección 4.1.1, dentro del directorio “Filter” existe un fichero llamado “Type filter” que contiene una lista de tipos de ficheros que deben ser ignorados, por tanto, no deben subirse a Riak. Figura 22 Fichero type filter
46 Figura 27 Funcionamiento de los workers Como se puede observar en la imagen, el proceso A ejecuta la acción 1, que a su vez lanza una serie de workers, los cuales no son mas que procesos hijos que preparan y ejecutan tareas. Una vez creados los workers y asignadas las tareas, el proceso A continua con las acciones 2 y 3 mientras los procesos B, C y D están activos. Una vez finalizados, los workers lanzan un método interno call back para anunciar al proceso padre que han finalizado. Adicionalmente, como se ha explicado anteriormente, las facilidades de gestión de los workers, los hacen ideales para realizar cualquier tipo de tarea. 4.2.3.2 Tareas (Tasks) Las tareas están directamente relacionadas con las principales funcionalidades que ofrece este proyecto. Por tanto, cada una de ellas requiere de una o varias tareas para poder llevarse a cabo. En la siguiente tabla se aprecian las funcionalidades del proyecto junto a una relación con las tareas necesarias asociadas.
47 Funcionalidades Workers Tareas relacionadas Detección en tiempo real de modificaciones en el sistema de ficheros 4 Monitorización de las particiones físicas Captura de ficheros Captura lenta de ficheros Captura de ficheros no existentes Captura de procesos en tiempo real 2 Captura de procesos creados Captura de procesos destruidos Detección de las conexiones de red en tiempo real 1 Detección de las conexiones de red Generación de un histórico de eventos 0 Todas las anteriores tareas se encargan, cada vez que generan un evento, de añadirlo al histórico de eventos Tabla 2 Relación Funcionalidades – Tareas Como se observa en la tabla anterior, cada tarea esta gestionada por un worker, el cual está encargado de preprararla, ejecutarla y, a través de los estados, gestionarla. Por tanto, los estados de las tareas son imprescindibles para la gestión de las mismas, y son los siguientes: Figura 28 Estado de las tareas Se observa, que cuando a un worker se le asigna una tarea, está parada, y que es el worker el encargado de prepararla. Para ello, la inicia/configura y la deja lista para su ejecución, notificándole este hecho al agente EndPoint. Posteriormente, se ejecuta, pudiendo ocurrir que falle, o bien que la ejecución sea correcta y finalice, o que mientras se ejecuta, el agente EndPoint ordene al módulo controlador finalizar la tarea. En todos estos casos, la tarea se para y vuelve al estado inicial. Destacar que, en este proyecto, debido a su carácter y a las funcionalidades que cubre, las tareas no finalizan
48 hasta que el agente EndPoint se para, dando la orden de finalización de todas las tareas de forma previa. 4.2.3.2.1 Tarea de monitorización de las particiones físicas del disco del equipo Esta tarea se encarga de detectar los cambios en el sistema de ficheros, por tanto, fundamental para la lucha contra el malware. Para ello, realiza una serie de acciones. En primer lugar, monitoriza todas las particiones lógicas y físicas existentes en el disco del equipo. En el caso de Windows, son “C:\”, “D:\”, “E:\”, etc. De esta forma, monitoriza todo el sistema de ficheros y además todas las unidades de almacenamiento extraíbles que se inserten en el equipo. La monitorización finalizará cuando la unidad desaparezca, es decir, en caso de un USB, cuando sea extraido del sistema. Esta acción, es fundamental dentro del proyecto, ya que monitoriza uno de los puntos calientes de entrada de malware en un entorno empresarial. En segundo lugar, por cada partición física detectada, se monitoriza, a través de WMI, su sistema de ficheros hasta que la partición desaparezca del disco. La monitorización implica que a partir de ese instante se empiecen a detectar y capturar diferentes eventos en el sistema de fichero. Los eventos son: Fichero creado Fichero borrado Fichero modificado Destacar que para considerar que un fichero ha sido modificado, debe cumplirse algunos de los siguientes requisitos: Cambio en el contenido Cambio de permisos Cambio del nombre Cambio en los metadatos Cambio del tamaño Estos requisitos no aparecen reflejados cuando se captura un evento a excepción del cambio de nombre, por tanto, sólo se sabe que el fichero ha sido modificado o renombrado. Es decir, se sabe que un fichero ha sido modificado, pero no se sabe si es porque han cambiado los permisos, el contenido, los metadatos o el tamaño. Por último, una vez detectado y capturado un evento, esta tarea extraen los datos y se introducen en la correspondiente cola. Los datos extraidos son: Ruta del fichero: ruta completa del fichero afectado. Tipo de evento: se refiere a si el evento es de un fichero creado, borrado o modificado. En caso de ser el evento de tipo modificado y el fichero es renombrado, se pueden extraer de forma adicional el nombre anterior y el actual. Al tener un nuevo nombre, el sistema también genera un evento de tipo fichero creado. En función del tipo del evento, se usa la una cola u otra. En caso de que un fichero sea de tipo creado o modificado, el evnto se añade a la cola “cola de ficheros”, pero en caso de ser de tipo borrado o renombrado, el evento se introduce en la “cola de ficheros inexistentes”. De esta forma, las correspondientes tareas que leen de cada cola tratan de una forma distinta cada evento. Mencionar, que la tarea, antes de encolar el evento, comprueba a través del gestor de filtrado si la ruta del fichero debe ser ignorada o no. En caso que haya que filtrar, la muestra no se añade a ninguna cola, por tanto, se ignora.
49 Figura 29 Tarea de monitorización de particiones lógicas En la imagen se observa, como de cada partición lógica, se monitoriza el sistema de ficheros, detectando así cada cambio producido en el mismo. Por cada cambio producido, la tarea extrae los datos del evento y en función del tipo del evento y de si de la ruta esta filtrada o no, a través del gestor de cola, lo añade a una cola u otra. 4.2.3.2.2 Tarea de captura de ficheros Tarea encargada de leer de forma continua de la cola de ficheros, comprobar si un fichero está filtrado por tipo o tamaño, extraer todos los datos necesarios de un fichero, subir los archivos no filtrados a Riak a través del gestor, enviar a Kafka a través del gestor el evento, y añadir el evento al histórico de eventos. Para empezar, la tarea obtiene de la cola de ficheros un evento, extrae la ruta y tipo del evento y, a través del gestor de parámetros de ficheros, obtiene el tipo del fichero y su tamaño. A continuación, comprueba si está filtrado por tipo o por tamaño usando el gestor de filtrado. En caso de estar filtrado el fichero, se vuelve a introducir el evento extraido de la cola de ficheros en la cola lenta de ficheros, incluyendo el tipo del fichero. De esta forma, se priorizan los ficheros que deban tratarse y subirse a Riak para que no sufran retardo y se puedan analizar los antes posible. A continuación, a los ficheros no filtrados se le extraen, a través del gestor de parámetros de ficheros, los siguientes parámetros para construir un nuevo evento que enviar al manager a través de Kafka: Hash SHA256 Hash MD5 Permisos Extensión Nombre A estos parámetros, hay que añadir el tamaño y tipo del fichero, que con anterioridad se obtuvieron.
50 Una vez se tienen los parámetros, se comprueba usando el gestor de la caché, si el hash SHA256 es conocido o desconocido en la caché local. En caso de ser conocido, no hay que subir el fichero a Riak, por tanto, se obtiene el score asociado al hash, y se añade al nuevo evento construido anteriormente. Si el fichero resulta ser desconocido en la caché local, se añade una nueva entrada en la misma, introduciendo el SHA256 correspondiente al fichero y el score a menos uno (-1) que indica desconocido. A continuación, usando el gestor de Riak, se envía al manager el fichero para su análisis, y se completa el evento construido con anterioridad con el score. Destacar que la nueva entrada añadida a la caché local, se actualizará en el siguiente incremento que el gestor de la caché descargue del sistema de reputación, debido a que el manager analizará el fichero y le asignará un score, actualizando así la caché remota. Por último, haciendo uso del gestor de parámetros de Windows, se obtienen los parámetros, se completa el evento, y se encola para su envío al manager mediante Kafka. Figura 30 Tarea de captura de ficheros
51 4.2.3.2.3 Tarea de captura lenta de ficheros Esta tarea se encarga de leer de la cola lenta de ficheros los eventos para extraer todos los parámetros de los mismos, crear un nuevo evento con dichos parámetros e introducirlo en la cola de Kafka. De manera adicional, añade al histórico de eventos una entrada para enriquecerlo de cara a un posible análisis forense posterior. Se trata de una tarea parecida a la anterior, pero mucho más simple, ya que los eventos que extre de la cola son de ficheros filtrados, que no deben subirse a Riak. El objetivo de esta tarea, a parte de informar al manager y enriquecer el histórico de eventos, es reducir el consumo de recursos al máximo al extraer los datos de estos ficheros que han sido filtrados, ya que los recursos deben destinarse a los ficheros que deben ser analizados. Destacar que los eventos que se extraen de la cola, pueden referirse a eventos relacionados con ficheros de tipo ISO, AVI, MOV, etc. Por tanto, ficheros de un gran tamaño, los cuales pueden hacer que el gestor de parámetros de ficheros tarde un tiempo amplio en obtener ciertos parámetros, como el SHA256 o MD5, ya que se debe cargar antes todo el contenido del correspondiente fichero. Por este motivo, esta tarea no es prioritaria, ni importa el tiempo que tarde en realizarse, lo único que importa es que termine, se informe al manager del evento sucedido, y como ya se ha explicado, que ocupe los mínimos recursos posibles. La tarea se encarga de leer la cola lenta de ficheros hasta que el agente de EndPoint envía la orden al worker que la controla de finalizar la misma. De cada elemento que se lee de la cola, se extrae la ruta del fichero, el tipo del evento y el tipo del fichero. Una vez obtenidos, se recurre al gestor de los parámetros de ficheros para obtener los siguientes parámetros: Hash SHA256 Hash MD5 Permisos Tamaño Una vez obtenidos, se crea un nuevo evento para enviarlo al manager usando Kafka. Adicionalmente, se añaden los siguientes parámetros al evento: Extensión del fichero Nombre del fichero Ruta del fichero Tipo del evento extraido de la cola Por último, se recurre al gestor de los parámetros de Windows para conseguir los parámetros referentes al sistema operativo y usuario que lo usa. Tras el último paso, se envía al manager el evento haciendo uso del gestor de Kafka y se añade al histórico de eventos. 4.2.3.2.4 Tarea de captura de ficheros inexistentes Esta tarea se encarga de leer de forma continua de la “cola de ficheros inexistentes” y extraer los elementos para construir un nuevo evento que enviar al manager mediante el uso del gestor de Kafka. Se debe tener en cuenta, que en esta cola sólo se introducen eventos relacionados con archivos que ya no existen en el sistema de ficheros, es decir, ficheros borrados o ficheros renombrados. Por tanto, no se puede trabajar con esos ficheros, solo se puede anunciar lo ocurrido al manager y añadirlo al histórico de eventos. Se generan dos tipos de eventos en función del tipo de evento extraido de la cola: Ficheros borrados: Se genera un nuevo evento en el que se informa que un fichero ha sido borrado. Ficheros renombrados: Se crea un nuevo evento informando que un fichero llamado de una forma, ha pasado a llamarse de otra.
52 4.2.3.2.5 Tarea de captura de procesos creados Tarea creada a partir de la necesidad de detectar en tiempo real la ejecución de un programa posiblemente infectado con malware, y a través del manager de redBorder Malware, actuar en consecuencia. Para llevar a cabo esta necesidad, esta tarea monitoriza la creación de procesos. Todo ello gracias a WMI, que permite capturar los procesos y obtener una serie de parámetros de los mismos, imprescindibles para el posterior análisis mediante IOC. Por cada proceso creado y capturado, esta tarea, extrae la siguiente información con el fin de construir un evento que enviar al manager: Nombre: Nombre del proceso PID: Identificador numérico del proceso que lo identifica de forma unívoca. El ejecutable: Fichero que se ha ejecutado provocando la creación del proceso. La priorida: Prioridad numérica (1-13) que un tiene para ejecutarse dentro del sistema. El identificado del perfil de usuario: Identificador numérico del perfil de usuario que ha ejecutado el proceso. El ejecutable padre: Este parámetro hace referencia al fichero que ha provocado el proceso padre del proceso capturado, en caso de su existencia. Una vez obtenidos todos los parámetros, se construye un nuevo evento, se introducen los parámetros y, por un lado, se añade una nueva entrada en el histórico de eventos y, por otro, se encola el evento para enviarse al manager. El manager de redBorder Malware, recoge el evento y lo ejecuta contra IOC para detectar si el proceso ejecutado entraña algún peligro para la seguridad del equipo y de la red en la que se encuentra. En caso de encontrar algún peligro, mediante GRR, actúa de inmediato y paraliza la amenaza. Figura 31 Tarea de captura de procesos creados
53 4.2.3.2.6 Tarea de captura de procesos destruidos Al igual que la tarea de captura de procesos creados, tan necesaria para detectar en tiempo real posibles ficheros infectados de malware, es necesario tener una tarea que se encargue de detectar, en tiempo real, la destrucción de un proceso. Este necesario control de los procesos tiene dos motivos principales: El primer motivo, es que cuando en el manager se reciba la alerta y se compare contra IOC, se debe tener un control en tiempo real, de la ejecución de un archivo infectado y del equipo implicado para poder actuar en consecuecia. El segundo motivo tiene que ver con el análisis forense. Si se tiene un exhaustivo control acerca de la ejecución de un malware, se puede determinar desde los ficheros que ha podido ejecutar de forma adicional ese archivo, hasta los daños causados en el periodo de tiempo que ha estado activo. Por estos motivos, existe esta tarea, que se encarga de enviar al manager, a través del gestor Kafka, eventos relacionados con la destrucción de un proceso, para que una vez comparada contra IOC, gestione las alertas de la forma oportuna, y para añadir una nueva entrada en el histórico de eventos. Para ello, se monitoriza en tiempo real la destrucción de procesos y se obtienen datos acerca del mismo, todo ello gracias a WMI. Los procesos que se destruyen guardan sus datos en memoria durante un tiempo determinado antes de ser eliminados por completo. Estos datos, al no existir el proceso, son escasos y son los siguientes: Nombre PID Con estos datos, se construye un nuevo evento, se introducen en la cola de Kafka para enviarlo al manager, y se añade una nueva entrada al histórico de eventos. Los datos extraidos en esta tarea pueden parecer escasos, pero una vez llegado el evento al manager, se correlacionan los datos con el del proceso creado y se extraen los demás datos. 4.2.3.2.7 Tarea de detección de las conexiones de red La detección en tiempo real de conexiones de red es de vital importancia en la lucha contra ataques o amenazas avanzadas, ya que un temprano conocimiento de las conexiones, puede evitar un gran perjuicio tanto en un equipo como en una red. Hoy día, muchos EndPoint son tomados de forma remota por botnets 10 que establecen el control a partir de un fichero instalado en el equipo, el cual duerme hasta que recibe la orden de actuar. También existen organizaciones de cibercriminales que se dedican a extraer información de empresas mediante ataques. Todo lo anterior, se puede evitar si se detectan a tiempo las conexiones, ya que permite actuar de forma inmediata y evitar cualquier daño que pueda llegar a producirse. Por este motivo, el agente EndPoint implementa esta tarea, que monitoriza, a partir de los procesos creados (encargados de generar conexiones), las conexiones que se establecen en un EndPoint. Una vez extraidas las conexiones, se establece la ruta completa de direcciones IP y puertos hasta el destino, se crea un nuevo evento, se encola para enviarse a Kafka y se añade una nueva entrada al fichero de histórico de eventos. 10 Conjunto de servidores (clúster) que de forma remota controlan un equipo mediante la instalación en el mismo de un malware, convirtiendo ese equipo en un zombie.
54 Figura 32 Tarea de captura de conexiones de red Tal como se observa en la imagen anterior, el manager, una vez recibido el evento y ejecutado contra los IOC, si se detecta una amenaza, ejecuta mediante GRR la acción pertinente para evitar cualquier daño o robo de información que los EndPoints puedan sufrir. 4.2.3.2.8 Tarea de envío de eventos En todas las tareas anteriores se necesita enviar eventos al manager mediante Kafka. Para evitar envíos masivos de eventos simultaneos al manager y agilizar todo el proceso de envío, se ha creado esta tarea. Es una tarea simple, ya que se encarga de extraer eventos, de forma constante, de la cola de Kafka mediante el gestor de colas y mediante el uso del gestor, enviarlos al manager de redBorder Malware usando el topic y partición adecuada. Figura 33 Tarea de envío de eventos
55 4.3 Eventos En este proyecto, tal como se ha mencionado en puntos anteriores, genera de forma continua eventos con información referente a la detección de posibles ficheros infectados, o bien, a procesos o conexiones de red. Estos eventos, son siempre enviados al manager de redBorder Malware a través de Apache Kafka haciendo uso de la tarea de envío de eventos. Tal como se explico en la sección 4.2.2.2, Kafka está dividida en topics, que no son más que diferentes estructuras de datos, en este caso JSON. Además, también se explicó que en este proyecto se usan dos topics: rb_ioc rb_endpoint Por tanto, se tienen dos estructuras de datos distintas, la primera, usada en todas las tareas, y la segunda usada sólo en las tareas relacionadas con la captura de ficheros. 4.3.1 rb_ioc Este topics lo usa el agente EndPoint en cualquier tarea, ya sean las de captura de ficheros, detección de procesos o detección de conexiones web. En caso de ser nuevo el fichero el evento es del siguiente tipo: Figura 34 Evento de detección de un fichero desconocido Se observa como en el evento se encuentran una serie de parámetros que se explican a continuación: cache_score: Score, que el hash SHA256 asociado al fichero, tiene en la caché local. action: Tipo de evento asociado al fichero detectado. En este caso el fichero ha sido modificado. namespace_uuid: Identificador del EndPoint emisor del evento. Este parámetro se usa para la parte de Big Data en el manager de redBorder Malware. filename: Nombre del fichero capturado. type: Tipo del evento. En este caso, fichero capturado. file_type: Tipo del fichero. acl: Permisos del fichero en decimal. timestamp: Marca de tiempo del instante de detección del fichero.
62 Una vez se tiene el instalador MSI, se introduce en la web de redBorder Malware para poder ser instalado en diferentes EndPoints. Figura 44 Descarga de redBorder Malware EndPoint desde la web Para descargarlo, se hace desde la web, que una vez pulsado el botón mostrado en la imagen anterior, pide la arquitectura sobre la cual va a ser instalado. Figura 45 Elección de la arquitectura de redBorder Malware EndPoint Una vez descargado en un EndPoint, se ejecuta el instalador, que instalará el cliente de GRR y redBorder Malware EndPoint. Una vez instalado, el cliente GRR lanza un call back al servidor para decirle que ha sido instalado, dando los datos del EndPoint. Esos datos, son cogidos por parte del manager de redBorder para reconocer como un elemento más de la red al EndPoint donde se ha instalado. Una vez realizado el paso anterior, el manager extrae del servidor el identificador que el servidor GRR le ha dado al cliente y mediante el propio servidor, se le comunica a redBorder Malware EndPoint cual es su identificador, añadiéndolo al fichero “parameters.yaml” que se explico en la sección 4.2. Posteriormente, inicia el servicio “EndPoint service”, que es el servicio generado por este proyecto.
63 Figura 46 Diagrama de instalación Con el servicio arrancado, se tiene el EndPoint registrado en el manager y funcionando como se muestra en la siguiente imagen, por tanto, la integración con redBorder Malware se ha completado. Figura 47 Lista de EndPoints registrados
64
65 6 PRUEBAS DE CONCEPTO. 6.1 Pruebas de concepto y funcionalidad En este apartado se van a realizar diferentes pruebas con el fin de demostrar que el proyecto funciona de forma correcta y cumple cada una de las funcionalidades para las cuales ha sido diseñado. 6.1.1 Instalación y registro La primera prueba va a consistir en descargarse vía web redBorder Malware EndPoint, instalarlo en un EndPoint y verificar que el registro en la web de redBorder Malware se ha realizado de forma correcta. De forma adicional, se va a comprobar como los servicios, tanto de GRR como del EndPoint, están activos y como el software se inicia de forma correcta. Para comenzar la prueba se abre la web del manager de redBorder Malware, cuya dirección es https://<IP>/endpoints y se descarga el software. Figura 48 Descarga de redBorder Malware EndPoint vía web Una vez descargado, se abre en el EndPoint el directorio, y se ejecuta el instalador, que instala el cliente GRR y el software, ambos como servicios de Windows. Una vez instalado, el cliente de GRR lanza un callback al servidor para registrarse, a lo que este último responde con un identificador. Los datos, tanto los de registro como los del identificador, son extraidos por el manager para hacer el registro del EndPoint en la web y para que, vía GRR, se lance un flow 11 al cliente para introducir el identificador en el fichero del entorno de configuración e iniciar el servicio EndPoint. En la siguiente imagen se aprecia el instalador que se debe ejecutar: 11 Flujo de comunicación servidor – cliente de GRR, por el cual el servidor hace una petición al cliente.
66 Figura 49 Instalador Una vez instalado, se puede apreciar como crea tanto el servicio de GRR (GRR Monitor) como el servicio de EndPoint (EndPoint Service): Figura 50 Servicios instalados
67 El siguiente paso, como se explico con anterioridad, es demostrar que el EndPoint se registra de forma correcta en la web de redBorder Malware. Para ello, el servicio “GRR Monitor” manda un callback al servidor para registrarse, asignándole este un identificador. Tanto el callback como el identificador son extraidos por parte del manager para registrarlo en la web, momento en el cual ya aparece como un nuevo EndPoint, como puede apreciarse a continuación: Figura 51 Registro web de un EndPoint Por último, el manager usa la comunicación entre servidor-cliente de GRR para lanzar un flow a través de esta tecnología para introducir el identificador en el fichero “parameters.yaml” , dentro del directorio “config” del entorno de redBorder Malware EndPoint. A continuación, se inicia el servicio “EndPoint service”. Figura 52 Parámetro client_id Para comprobar el correcto inicio del servicio, se pueden consultar los servicios y comprobar que está en estado “Iniciado”, como se demuestra en la imagen mostrada anteriormente de los servicios. En caso de algún tipo de error, el servicio estaría parado. Realizadas las pruebas, se observa que todo ha transcurrido de forma correcta, por tanto, la instalación y registro de redBorder Malware EndPoint, ha sido un éxito.
68 6.1.2 Detección y captura de ficheros La realización de esta prueba, va a estar orientada a la demostración de la detección y captura de ficheros, la subida de los mismos a Riak, el envío del evento mediante Kafka y su posterior aparición en la web de redBorder Malware. El primer paso a dar es crear un fichero, al cual se da un nombre cualquier, en este caso, “FILE.exe”. Figura 53 Creación de un fichero A continuación, para comprobar si se ha detectado el fichero, se abre desde el manager de redBorder Malware una consola y se inicia el consumidor de Kafka en el topic “rb_endpoint”: Figura 54 Fichero nuevo en rb_endpoint
69 Se observa que los eventos llegan de forma correcta, por tanto, se comprueba el topic “rb_ioc” para asegurar que llegan los eventos y que, en caso de amenazas, sean analizados por los IOC: Figura 55 Fichero nuevo en rb_ioc Al igual que antes, los eventos llegan sin problemas al manager, por tanto, sólo quedan dos pasos pendientes. El primero de ellos es, a partir del hash del fichero, cerciorarse que el fichero se encuentra en Riak. Por tanto, desde la misma consola donde se han comprobado los eventos, se comprueba si el fichero se encuentra en Riak, hecho que podemos observar en la siguiente imagen: Figura 56 Fichero nuevo en Riak El segundo paso es demostrar vía web que los ficheros están siendo analizados. Para ello, es necesario observar si existe algún fichero lanzado desde el equipo “adrian-HP-Notebook.redborder.lan” en la vista Top de Malware de la web. Figura 57 Fichero nuevo en la web
70 El equipo anteriormente mencionado, ha lanzado varias muestras, entre las que se encuentra el fichero FILE.exe, por tanto, esta prueba ha sido realizada con éxito. 6.1.3 Detección de procesos Esta prueba es bastante más sencilla que la anterior, ya que la comprobación acerca de la detección de un proceso sólo se comprueba a través del consumidor de Kafka en el topic “rb_ioc”. Para ello, primero se abre un proceso cualquiera, como puede ser “notepad”: Figura 58 Creación del proceso notepad Acto seguido, se cierra el proceso y se comprueba, mediante la consola del manager, si en el consumidor existen eventos de Kafka relacionados con dicho proceso. Para ello, se miran los eventos llegados al topic “rb_ioc”. Como puede comprobarse a continuación, la prueba resulta satisfactoria: Figura 59 Evento de creación del proceso notepad en Kafka 6.1.4 Detección de conexiones web En esta prueba se va a realizar una conexión a una página web y se va a comprobar mediante un consumidor de Kafka que el evento llega al manager de forma correcta. Por tanto, lo primero que se debe hacer es abrir una conexión web, desde Google Chrome, por ejemplo:
71 Figura 60 Creación de una conexión web Posteriormente, desde el consumidor Kafka del manager de redBorder Malware, se comprueba el topic de “rb_ioc” para asegurar si ha llegado o no el evento referente a la conexión anterior: Figura 61 Evento de creación del proceso de conexión web Se puede observar como la detección y el envío del evento a Kafka ha sido un éxito, ya que se muestra que el proceso es “chrome.exe” y todos los saltos que ha dado hasta llegar al servidor web destino. 6.1.5 Detección mediante IOC de amenazas Para llevar a cabo esta prueba, sólo es necesario saber que proceso o fichero, puede activar una alerta por IOC. En este caso, se ha diseñado una alerta, a modo de ejemplo, que salta al detectar la creación de un fichero llamado “server.exe” en un EndPoint. Por tanto, el primer paso es crear un fichero con ese nombre, como se hace a continuación:
78 6.2.2 Procesamiento de lotes de ficheros La siguiente prueba pretende probar la capacidad de procesar lotes de ficheros en un EndPoint. Para ello se va a descargar y descomprimir un conjunto de ficheros que serán procesados por redBorder Malware EndPoint. Se describen a continuación las características del EndPoint de prueba: Máquina virtual con Windows 7 de 64 bits de arquitectura. Gb de RAM. CPU con 2 cores. El procedimiento a seguir es el siguiente: Se dispone de varios ficheros comprimidos con lotes de 5, 50, 100, 500 y 10000 archivos de diversos tipos y tamaños. El servicio de endPoint estará apagado al inicio de cada prueba para limpiar la caché y las colas de proceso, de esa forma priorizamos los archivos descomprimidos. En la siguiente tabla, se da información referente a los ficheros, sus tipos y tamaños. LOTE TAMAÑO TIPOS 5 992 KB JPEG, MP3, TXT, HTML 50 20,4 MB JPEG, MP3, TXT, HTML, AVI, DOCX 100 145 MB JPEG, MP3, TXT, HTML, AVI, DOCX, GIF 500 438 MB JPEG, MP3, TXT, HTML, AVI, DOCX, GIF, ZIP, XLS, BIN, PNG, MOV, MKV, PDF, PPT 1000 637 MB JPEG, MP3, TXT, HTML, AVI, DOCX, GIF, ZIP, XLS, BIN, PNG, MOV, MKV, PDF, PPT Tabla 3 Información de los ficheros de pruebas Una vez se han procesado todos los lotes de ficheros por parte de redBorder Malware EndPoint, se obtienen los siguientes resultados:
79 LOTES 5 50 100 500 1000 UNIDADES CPU 8 7,67 7,6 23,6 6,342 % RAM 0,82 0,81 0,9 1,5 0,4 % HDD 0,5 2,14 2,33 55,36 2,240 % RED 8996,61 35766,5 15547,487 9789,543 15154,19 Bytes/seg TIEMPO 138 1086 2371 19010 35665 Seg Tabla 4 Resultados del análisis de los lotes de ficheros Figura 71 Gráfica de procesado de lotes de ficheros La gráfica muestra el tiempo requerido para el procesado de cada uno de los lotes. Destacar cierta tendencia lineal en el comportamiento del servicio de EndPoint. Observando los recursos utilizados en los distintos procesamientos de lotes, se puede comprobar que el consumo de RAM se mantiene en unos valores en torno al 0,5% del incremento sobre el utilizado.
80 Destaca de forma notable el lote de 500 archivos, donde se aprecia un consumo de CPU y disco duro mayores; estos valores son penalizaciones debido a que el equipo está virtualizado y comparte los recursos del anfitrión con otras máquinas virtuales. También puede impactar el uso de red para los lotes más grandes, dado que su uso es menor que por ejemplo el lote de 50 archivos. Estos valores más “relajados” están calculados como media en el espacio de tiempo que dura el procesamiento de los lotes de archivos. 6.2.3 Uso de un EndPoint Esta prueba consiste en simular el uso por parte de un usuario de un EndPoint. Para automatizar el comportamiento se ha usado un bot, combinando acciones de edición y renombrado de de ficheros, descarga de malware y acceso a enlaces multimedia. El objetivo de la prueba es tener el conocimiento necesario acerca del uso de CPU y memoria RAM: CPU: La actividad ha sido constante, como el patrón de funcionamiento del bot durante más de 12 horas. Se aprecian picos de procesamiento debido a la acumulación de procesos en los accesos a contenidos multimedia. Durante la prueba, el promedio de uso de CPU ha sido del 34,51%. Figura 72 Gráfica de uso de CPU Memoria RAM: La actividad durante periodos de actividad intensa ha sido del 48,8%, pero una vez finalizada la actividad del bot, se observa un descenso hasta llegar al promedio del 30%.
81 Figura 73 Gráfica de uso de memoria RAM
82
83 7 PRESUPUESTO. En esta sección estiramos el coste del proyecto, basándonos en las horas dedicadas y el equipamento necesario para instalar todo el sistema: Periodo de Trabajo Número de Horas Precio/Hora Total Agosto 2015 – Mayo 2016 1035 30€/hora 31.020€ Equipamiento Cantidad Precio/Hora Total Anfitrión para VMware ESXi (Entorno de virtualización de servidores). 1 5.000€ 5.000€ Tabla 5 Presupuestos En el anfitrión se virtualizan servidores que soportan los distintos servicios involucrados en el proyecto, estos servidores tienen las siguientes características: Memoria: 15 Gigas de RAM. CPUs: modelo “Intel(R) Xeon(R) CPU E5-1650 0 @ 3.20GHz”. Sistema redBorder, basado en CentOs. Para llevar acabo este proyecto se ha calculado un presupuesto total de 36.020 €.
84
85 8 CONCLUSIONES. 8.1 Conclusiones La herramienta desarrollada en este proyecto, es estable, funcional y está incluida en la aplicación de malware de redBorder dentro del portfolio de productos. Este presente documento, recoge el resultado de un largo y complejo proceso de investigación, aprendizaje, superación personal y principalmente trabajo. El trabajado se ha desarrollado con tecnologías código abierto para el desarrollo de este proyecto como son Python o GRR. De forma adicional, al haber trabajo con el equipo de Eneo Tecnología SL, he adquirido enormes capacidades de trabajo en equipo y de comunicación. Por último, atendiendo al resultado del proyecto y de sus pertinentes pruebas, se han conseguido cubrir todos los hitos propuestos. Se ha conseguido un sistema de seguridad para EndPoints que detecte en tiempo real eventos del sistema y los notifique a la plataforma de análisis de redBorder Malware, detectando así ataques, malware o amenazas avanzadas. 8.2 Futuras mejoras El siguiente paso de este proyecto tratará sobre la adición al agente EndPoint un nuevo módulo de IOC, de modo que, cualquier evento del sistema sea analizado en el propio EndPoint para dilucidar si existe algún riesgo para el equipo. De esta manera, el agente EndPoint quedaría exento de la necesidad de un análisis remoto contra IOC y además podría pasar a actuar de manera inmediate ante cualquier riesgo sin necesidad de esperar la respuesta del servidor GRR.
86
87 9 REFERENCIAS. [1] Mike Halsey, Mayo 2011. Windows 7 Power User Guide [En línea] [Documento PDF] Disponible: https://blogs.technet.microsoft.com/uktechnet/2011/05/10/download-the-windows-7-power-usersguide-ebook-free/ [2] Python.org. Documentación [En línea] [Web] Disponible: https://docs.python.org/3/ [3] Apache Software Foundation – Apache Kafka [En línea] [Web] Disponible: http://kafka.apache.org [4] ENEO Tecnología – redBorder Malware [En línea] [Web] Disponible: https://redborder.com/ [5] Basho – Riak CS Documentation [En línea] [Web] Disponible: http://docs.basho.com/riakcs/latest/ [6] Basho – Riak Documentation [En línea] [Web] Disponible: http://docs.basho.com/riak/latest/ [7] Microsoft – WMI API Documentation [En línea] [Web] Disponible: https://msdn.microsoft.com/en-us/library/windows/desktop/ff818516(v=vs.85).aspx [8] Malware Analyst’s Cookbook. Autores: Michael Hale Ligh, Steven Adair, Blake Hartstein and Matthew Richard [En línea] [Documento PDF] Disponible: https://docs.google.com/file/d/0B7d_gqEI7itKTm44RC1Hczh3LUU/edit?pli=1 [9] Github – GRR Documentation [En línea] [Web] Disponible: https://github.com/google/grr-doc [10] Raul Gonzalez Duque, Octubre 2014. Python para todos [En línea] [Documento PDF] Disponible: https://launchpadlibrarian.net/18980633/Python%20para%20todos.pdf [11] Mark Kedrich, Marzo 2007. EndPoint Security
94 51. # connection to sqlite 52. dbConnection = None 53. 54. # lastest incremental list query 55. last_incremental_query = 'r.i' 56. 57. # init local cache with config file 58. @staticmethod 59. def initCache(config): 60. if CacheManager.status is Status.STOPPED: 61. 62. CacheManager.status = Status.PREPARING 63. 64. logging.getLogger('application').info("Init cache client") 65. # load configuration 66. CacheManager.url_server = config['reputation_server'] 67. CacheManager.version = config['version_cache'] 68. databasePath = os.path.join(config['rootpath'], 'endpoint_agent', 'db', Cac heManager.DATABASE_NAME) 69. CacheManager.dbConnection = sqlite3.connect(databasePath, check_same_thread =False) 70. CacheManager.semaphore = threading.Semaphore() 71. 72. # define table in BBDD 73. if CacheManager.dbConnection: 74. logging.getLogger('application').debug("Connected to local cache databa se: {0}".format(CacheManager.DATABASE_NAME)) 75. CacheManager.dbConnection.cursor().execute('CREATE TABLE IF NOT EXISTS cache (hash text PRIMARY KEY, score integer)') 76. CacheManager.dbConnection.commit() 77. 78. CacheManager.status = Status.READY 79. 80. 81. @staticmethod 82. def start(): 83. 84. if CacheManager.status is Status.READY: 85. 86. CacheManager.status = Status.RUNNING 87. 88. # Init cache task 89. executor = ThreadPoolExecutor(max_workers=1) 90. executor.submit(CacheManager.doPetitions) 91. 92. logging.getLogger('application').info("Started cache client!") 93. 94. 95. @staticmethod 96. def doPetitions(): 97. if (CacheManager.status is Status.RUNNING): 98. 99. logging.getLogger('application').debug("Doing total petition to remote cach e") 100. # first time get total with filter hash 101. CacheManager.getTotal("hash") 102. 103. logging.getLogger('application').debug("Doing incremental petition t o remote cache") 104. # first time get incremental 105. CacheManager.getIncremental("hash") 106. 107. # every X seconds get incremental with filter hash 108. while CacheManager.status is Status.RUNNING and not time.sleep(900): 109. CacheManager.getIncremental("hash")
95 110. logging.getLogger('application').debug("Do incremental petition to remote cache") 111. 112. 113. 114. @staticmethod 115. def getTotal(filter): 116. # build url 117. url = CacheManager.URL_REPUTATION_TOTAL.format(CacheManager.url_server, CacheManager.version, "/%s" % filter if filter else "") 118. 119. try: 120. 121. CacheManager.semaphore.acquire() 122. 123. if (CacheManager.status is Status.RUNNING): 124. 125. # get remote response 126. response = requests.get(url, verify=False) 127. 128. logging.getLogger('application').debug("Response in total petiti on : {0}".format(response.status_code)) 129. 130. if response.status_code is 200: 131. 132. # if status reponse is correct put in BBDD all data 133. data = response.json()['data'] 134. 135. for entry in data: 136. if 'hash' in entry and 'score' in entry: 137. hashValue = entry['hash'] 138. scoreValue = entry['score'] 139. CacheManager.dbConnection.cursor().execute('INSERT O R REPLACE INTO cache VALUES (\'{0}\',{1})'.format(hashValue, scoreValue)) 140. logging.getLogger('application').debug("Updated data base : hash {0} with score {1}.".format(repr(hashValue), scoreValue)) 141. 142. CacheManager.dbConnection.commit() 143. logging.getLogger('application').info("Updated local cache d atabase with total list") 144. else: 145. logging.getLogger('application').warning("Unable to get tota l list from : {1}".format(CacheManager.url_server)) 146. 147. except Exception as e: 148. logging.getLogger('application').error('Error to get total list from remote cache') 149. logging.getLogger('application').error(e) 150. finally: 151. CacheManager.semaphore.release() 152. 153. 154. 155. @staticmethod 156. def getIncremental(filter): 157. 158. try: 159. CacheManager.semaphore.acquire() 160. 161. if (CacheManager.status is Status.RUNNING): 162. 163. # get incremental cache 164. url = CacheManager.URL_REPUTATION_INCREMENTAL.format(CacheManage r.url_server, CacheManager.version) 165. 166. # get remote response 167. response = requests.get(url, verify=False)
96 168. 169. logging.getLogger('application').debug("Response in last increme ntal petition : {0}".format(response.status_code)) 170. 171. # if status reponse is correct checked latest updates 172. if response.status_code is 200: 173. 174. lastRelease = response.json()['last_release'] 175. 176. if not lastRelease == CacheManager.last_incremental_query an d not CacheManager.last_incremental_query == '0.0': 177. 178. CacheManager.last_incremental_query = lastRelease 179. 180. url = CacheManager.URL_REPUTATION_INCREMENTAL_LIST.forma t(CacheManager.url_server, CacheManager.version, lastRelease, "/%s" % filter if filter else "") 181. 182. response = requests.get(url, verify=False) 183. 184. logging.getLogger('application').debug("Response in incr emental list petition : {0}".format(response.status_code)) 185. 186. if response.status_code is 200: 187. 188. #If remote cache changed, updated endpoint local cac he 189. data = response.json()['data'] 190. 191. for entry in data: 192. if 'hash' in entry and 'score' in entry: 193. hashValue = entry['hash'] 194. scoreValue = entry['score'] 195. 196. CacheManager.dbConnection.cursor().execute(' INSERT OR REPLACE INTO cache VALUES (\'{0}\',{1})'.format(hashValue, scoreValue)) 197. logging.getLogger('application').debug("Upda ted database incremental list {0} : hash {1} with score {2}".format(repr(lastRelease), repr(hashValue), str(scoreValue))) 198. 199. CacheManager.dbConnection.commit() 200. 201. logging.getLogger('application').info("Updated local cache database with incremental list : {0}".format(repr(lastRelease))) 202. else: 203. logging.getLogger('application').info("Not incremental l ist available!") 204. 205. except Exception as e: 206. logging.getLogger('application').error('Error to get incremental lis t from remote cache') 207. logging.getLogger('application').error(e) 208. finally: 209. CacheManager.semaphore.release() 210. 211. 212. @staticmethod 213. def get_score(hashvalue): 214. 215. # get score from hash value 216. try: 217. CacheManager.semaphore.acquire() 218. 219. if (CacheManager.status is Status.RUNNING): 220. 221. cursor = CacheManager.dbConnection.cursor()
97 222. 223. cursor.execute('SELECT score FROM cache WHERE hash=?', (hash value,)) 224. 225. result = cursor.fetchone() 226. 227. if result: 228. logging.getLogger('application').debug("Result for hash {0} : {1}".format(hashvalue, result[0])) 229. return result[0] 230. else: 231. logging.getLogger('application').debug("Results not foun d for hash %s" % hashvalue) 232. else: 233. return 1000 234. 235. except Exception as e: 236. logging.getLogger('application').error("Error to query hash {0}" .format(hashvalue)) 237. logging.getLogger('application').error(e) 238. return 1000 239. finally: 240. CacheManager.semaphore.release() 241. 242. 243. @staticmethod 244. def checkIfExists(hashvalue): 245. 246. try: 247. CacheManager.semaphore.acquire() 248. 249. if(CacheManager.status is Status.RUNNING): 250. #checked if hash value exists in local cache 251. cursor = CacheManager.dbConnection.cursor() 252. 253. cursor.execute("SELECT EXISTS(SELECT 1 FROM cache WHERE hash=\'{ 0}\' LIMIT 1)".format(hashvalue)) 254. 255. result = cursor.fetchone()[0] 256. 257. if bool(result): 258. logging.getLogger('application').debug("Value {0} exists in database".format(hashvalue)) 259. else: 260. logging.getLogger('application').debug("Value {0} doesn't ex ists in database".format(hashvalue)) 261. CacheManager.dbConnection.cursor().execute('INSERT OR REPLAC E INTO cache VALUES (\'{0}\',{1})'.format(hashvalue, -1)) 262. CacheManager.dbConnection.commit() 263. logging.getLogger('application').info("Added new entry {0} i n local cache".format(hashvalue)) 264. 265. return bool(result) 266. 267. return None 268. 269. except Exception as e: 270. logging.getLogger('application').error("Error to add new entry for h ash {0}".format(hashvalue)) 271. logging.getLogger('application').error(e) 272. finally: 273. CacheManager.semaphore.release() 274. 275. 276. 277. @staticmethod 278. def shutdown():
98 279. if CacheManager.status is Status.RUNNING: 280. 281. CacheManager.status = Status.STOPPING 282. 283. # Wait a minute, if not close connection 284. try: 285. CacheManager.semaphore.acquire() 286. CacheManager.dbConnection.close() 287. CacheManager.status = Status.STOPPED 288. logging.getLogger('application').info('Local Cache Manager stopp ed') 289. except Exception as e: 290. logging.getLogger('application').error("Error to close database" ) 291. logging.getLogger('application').error(e) 292. finally: 293. CacheManager.semaphore.release()
99 managers/KafkaManager.py 1. from kafka import KafkaClient 2. from kafka import KeyedProducer 3. from kafka import HashedPartitioner 4. from kafka.common import (RequestTimedOutError, MessageSizeTooLargeError) 5. 6. import time 7. import logging 8. 9. 10. class KafkaManager(): 11. 12. 13. # constructor 14. def __init__(self, kafkaConfig): 15. self.kafkaConfig = kafkaConfig 16. self.init() 17. self.running = True 18. logging.getLogger('application').info("Init new kafka manager") 19. 20. # init kafka producer 21. def init(self): 22. running = False 23. try: 24. self.kafkaClient = KafkaClient(self.kafkaConfig['kafka_server']) 25. logging.getLogger('application').info("Connect with broker: {0}".format(sel f.kafkaConfig['kafka_server'])) 26. 27. logging.getLogger('application').info("Init new producer") 28. self.kafkaProducer = KeyedProducer(self.kafkaClient, partitioner=HashedPart itioner, async=False) 29. 30. running = True 31. 32. except Exception as e: 33. logging.getLogger('application').error("Kafka server connect failed.") 34. logging.getLogger('application').error(e) 35. 36. 37. def reconnect(self, topic, key, msg): 38. trying = 1 39. running = False 40. while trying <= 5 and not running: 41. try: 42. self.kafkaClient = KafkaClient(self.kafkaConfig['kafka_server']) 43. logging.getLogger('application').info("Reconnect with broker: {0}".form at(self.kafkaConfig['kafka_server'])) 44. 45. logging.getLogger('application').info("Updated producer") 46. self.kafkaProducer = KeyedProducer(self.kafkaClient, partitioner=Hashed Partitioner, async=False) 47. 48. self.kafkaProducer.send_messages(topic, bytes(key, 'utf8'), bytes(msg, 'utf-8')) 49. 50. running = True 51. 52. except Exception as e: 53. logging.getLogger('application').error("Kafka server reconnect failed. {0} of 5 retries".format(trying)) 54. logging.getLogger('application').error(e) 55. time.sleep(5) 56. trying += 1 57. continue 58.
100 59. def kafkaRequestTimedOutError(self, topic, key, msg): 60. trying = 1 61. running = False 62. while trying <= 5 and not running: 63. try: 64. self.kafkaProducer.send_messages(topic, bytes(key, 'utf8'), bytes(msg, 'utf-8')) 65. running = True 66. except RequestTimedOutError: 67. logging.getLogger('application').error("Kafka request time out error. { 0} of 5 retries".format(trying)) 68. time.sleep(5) 69. trying += 1 70. 71. # send message with or without key to topic 72. def sendMessage(self, msg, topic, key=None): 73. logging.getLogger('application').debug("Sending message to topic: %s", repr(top ic)) 74. try: 75. self.kafkaProducer.send_messages(topic, bytes(key, 'utf8'), bytes(msg, 'utf-8')) 76. except RequestTimedOutError: 77. self.kafkaRequestTimedOutError(topic, key, msg) 78. return 79. except MessageSizeTooLargeError: 80. logging.getLogger('application').error("Message size is too large to send k afka server {0}".format(msg)) 81. return 82. except Exception as e: 83. logging.getLogger('application').error(e) 84. self.reconnect(topic, key, msg)
101 managers/RiakManager.py 1. import time 2. import os 3. import shutil 4. import logging 5. import boto3 6. from botocore.utils import fix_s3_host 7. 8. class S3Manager(): 9. 10. configuration = None 11. path = None 12. 13. # Method to load configuration for S3 14. @staticmethod 15. def load_config(config, path): 16. S3Manager.configuration = config.getConfigProperties() 17. S3Manager.path = path 18. 19. 20. # Method to get file from S3 21. @staticmethod 22. def get_s3_file(sha256): 23. s3_path = os.path.join(S3Manager.path, 's3', sha256) 24. return s3_path 25. 26. 27. # Method to create temporal file. 28. @staticmethod 29. def create_temp_file(old_file, new_file): 30. flag = False 31. count = 0 32. while flag == False: 33. try: 34. if count < 5: 35. shutil.copyfile(old_file, new_file) 36. flag = True 37. logging.getLogger('application').info('Created temporal file : {0}' .format(new_file)) 38. return "ok" 39. else: 40. flag = True 41. logging.getLogger('application').error('Error to create temporal fi le : {0}'.format(new_file)) 42. return "error" 43. except: 44. time.sleep(15) 45. count += 1 46. 47. 48. # Method to upload files to S3 49. @staticmethod 50. def upload_s3(filename, sha256): 51. s3_file_upload = S3Manager.get_s3_file(sha256) 52. aws_access = S3Manager.configuration['access_key'] 53. aws_secret = S3Manager.configuration['secret_key'] 54. bucket_name = S3Manager.configuration['s3_bucket'] 55. bucket_key = S3Manager.configuration['s3_key'] 56. 57. copy_status = S3Manager.create_temp_file(filename, s3_file_upload) 58. 59. if copy_status == "ok": 60. count = 0 61. flag = True 62. while flag == True:
102 63. try: 64. # connect to hosts. 65. s3resource = boto3.resource(service_name="s3", 66. verify=False, 67. endpoint_url="https://s3.redborder.cluster", 68. aws_access_key_id=aws_access, 69. aws_secret_access_key=aws_secret) 70. 71. # Avoid redirect 72. s3resource.meta.client.meta.events.unregister('beforesign.s3', fix_s3_host) 73. 74. # Get bucket 75. bucket = s3resource.Bucket(bucket_name) 76. data = open(s3_file_upload, 'rb') 77. 78. # upload file to s3 79. bucket.put_object(Key=bucket_key + sha256, Body=data) 80. # close file 81. data.close() 82. time.sleep(1) 83. flag = False 84. if os.path.exists(s3_file_upload): 85. os.remove(s3_file_upload) 86. logging.getLogger('application').info('Uploaded file {0}'.format(re pr(filename))) 87. return "upload" 88. except Exception as e: 89. logging.getLogger('application').error('Failed to upload file {0} t o S3'.format(repr(filename))) 90. logging.getLogger('application').error(str(e)) 91. #Max intents. 92. if count < 2: 93. count += 1 94. time.sleep(2) 95. else: 96. return "error_upload" 97. else: 98. return "error_copy"
103 managers/QueueManager.py 1. from queue import Queue 2. from queue import Full 3. 4. import logging 5. 6. # Static class for queue management 7. class QueueManager(): 8. 9. # created queues 10. queues = dict() 11. 12. 13. # method to create a new queue with key 14. @staticmethod 15. def createQueue(key): 16. if not key in QueueManager.queues: 17. QueueManager.queues[key] = Queue() 18. logging.getLogger('application').info("Created new queue with key: \'%s\'", key) 19. 20. 21. # method to put data in queue with key 22. @staticmethod 23. def putOnQueue(key, data): 24. if key in QueueManager.queues: 25. QueueManager.queues[key].put(data) 26. logging.getLogger('application').debug("Added new data to queue with key: \ '%s\'", key) 27. logging.getLogger('application').debug("Data value : %s", data) 28. else: 29. logging.error("key \'%s\' not found!", key) 30. raise QueueManagerException(key) 31. 32. 33. # method to know if a queue with key is empty 34. @staticmethod 35. def isEmpty(key): 36. if key in QueueManager.queues: 37. logging.getLogger('application').debug("Checking size of queue with key: \' %s\'", key) 38. return QueueManager.queues[key].empty() 39. else: 40. logging.getLogger('application').error("key \'%s\' not found!", key) 41. raise QueueManagerException(key) 42. 43. 44. # method to get data from a queue with key 45. @staticmethod 46. def getItemFromQueue(key, timeout=None): 47. try: 48. if key in QueueManager.queues: 49. logging.getLogger('application').debug("Get item from queue with key: \ '%s\'", key) 50. return QueueManager.queues[key].get(timeout=timeout) 51. else: 52. logging.getLogger('application').error("key \'%s\' not found!", key) 53. raise QueueManagerException(key) 54. except Exception as e: 55. logging.getLogger('application').debug('Queue timeout for key {0}'.format(r epr(key))) 56. 57. 58. # method to get queue with key 59. @staticmethod
110 65. sha_normal = hashlib.sha256(content) 66. sha_256 = sha_normal.hexdigest() 67. file_open.close() 68. flag = False 69. except: 70. if count < 20: 71. time.sleep(timer) 72. count += 1 73. else: 74. flag = False 75. return sha_256 76. 77. @staticmethod 78. def get_file_type(filename): 79. # get a type from file 80. try: 81. message = open(filename, 'rb').read() 82. dir_file = os.path.join(FileParameters.path,'filters', 'snort_file.txt') 83. snort_file = open(dir_file, 'r').readlines() 84. tam = 0 85. tabla_uno = [] 86. #read line by line a snort file 87. while tam < len(snort_file): 88. words = snort_file[tam].split() 89. # if last word is 1 meaning that next line must be satisfy 90. if words[-1] == "1": 91. while words[-1] == "1": 92. #variable final is a last byte to check 93. final = int(words[1]) + int(words[0]) 94. #the content of a file limited by an initial byte and other en d is caught in hexadecimal 95. content = binascii.hexlify(message[int(words[0]):final]) 96. match = str(content.decode('ascii')).upper() 97. #If the content matches the typical of a file of that type com ing in our snort put 1 in the table file. In other case put 0 98. if match == words[2]: 99. tabla_uno.append(1) 100. else: 101. tabla_uno.append(0) 102. tam += 1 103. words = snort_file[tam].split() 104. words = snort_file[tam].split() 105. final = int(words[1]) + int(words[0]) 106. content = binascii.hexlify(message[int(words[0]):final]) 107. match = str(content.decode('ascii')).upper() 108. if match == words[2]: 109. tabla_uno.append(1) 110. else: 111. tabla_uno.append(0) 112. flag = 1 113. for attribute in tabla_uno: 114. if attribute == 0: 115. flag = 0 116. if flag == 1: 117. return words[3] 118. tabla_uno = [] 119. tam += 1 120. else: 121. final = int(words[1]) + int(words[0]) 122. content = binascii.hexlify(message[int(words[0]):final]) 123. match = str(content.decode('ascii')).upper() 124. if match == words[2]: 125. return words[3] 126. tam += 1 127. except: 128. type_file_none = 'None'
111 129. return type_file_none 130. 131. @staticmethod 132. def get_acl(filename): 133. try: 134. #Get dacl(data access control list) 135. security_file = win32security.GetFileSecurity(filename, win32securit y.DACL_SECURITY_INFORMATION) 136. #Get DACL Descriptor. 137. dacl = security_file.GetSecurityDescriptorDacl() 138. if dacl != None and dacl != '': 139. string_dacl = str(dacl) 140. parameter_dacl = string_dacl.split()[-1] 141. # Take permits and turn them into hexadecimal 142. permission = parameter_dacl[0:-1] 143. permission_decimal_format = int(permission, 16) 144. return permission_decimal_format 145. else: 146. return None 147. except: 148. return None 149. pass
112 managers/WindowsValueManager.py 1. import socket 2. import wmi 3. import logging 4. 5. class WindowsValueManager(): 6. 7. #windows values dictionary 8. values = dict() 9. 10. # Method to get MAC, dns domain, IP, IP subnet, profile and hostname in PC. 11. @staticmethod 12. def set_values(): 13. try: 14. WindowsValueManager.values['sensor_name'] = socket.gethostbyname_ex (socket .gethostname ())[0] 15. for parameters_network in wmi.WMI ().Win32_NetworkAdapterConfiguration (): 16. if parameters_network.MACAddress != None: 17. WindowsValueManager.values['client_mac'] = parameters_network.MACAd dress.lower() 18. 19. if parameters_network.DNSDomain != None: 20. WindowsValueManager.values['domain_name'] = parameters_network.DNSD omain 21. 22. if parameters_network.IPAddress != None: 23. WindowsValueManager.values['endpoint_agent'] = parameters_network.I PAddress[0] 24. WindowsValueManager.values['dst'] = parameters_network.IPAddress[0] 25. WindowsValueManager.values['sensor_ip'] = parameters_network.IPAddr ess[0] 26. 27. if parameters_network.IPSubnet != None: 28. WindowsValueManager.values['src_net_name'] = parameters_network.IPS ubnet[0] 29. 30. for parameters_profile in wmi.WMI ().Win32_NetworkLoginProfile (): 31. if parameters_profile.Name != None: 32. WindowsValueManager.values['client_id'] = parameters_profile.Name 33. except Exception as e: 34. logging.getLogger('application').error(e) 35. 36. 37. 38. @staticmethod 39. def get_values(): 40. return WindowsValueManager.values
113 Anexo C: Código del módulo controlador Controllers/MonitorController.py 1. from concurrent.futures import ThreadPoolExecutor 2. import logging 3. 4. class MonitorController(): 5. 6. # Constructor 7. def __init__(self): 8. self.executorService = ThreadPoolExecutor(max_workers=100) 9. self.workerList = list() 10. self.taskList = list() 11. logging.getLogger('application').info("Created new monitor controller") 12. 13. 14. # Reload monitor controller 15. def reload(self): 16. pass 17. 18. 19. # Run single task 20. def runTask(self, task, replication=1): 21. # Deploy task with replication 22. 23. self.taskList.append(task) 24. 25. task.prepare() 26. 27. for replica in range(replication): 28. self.workerList.append(self.executorService.submit(task.doTask)) 29. logging.getLogger('application').info("Worker for task {0} created".format( repr(task.getTaskName()))) 30. 31. 32. # Get result from every worker and return a list 33. def getWorkersResults(self): 34. results = list() 35. for worker in self.workerList: 36. results.append(worker.result()) 37. 38. return results 39. 40. 41. def stopWorkers(self): 42. 43. logging.getLogger('application').info("Stopping workers") 44. 45. up_workers = len(self.workerList) 46. down_workers = 0 47. 48. for task in self.taskList: 49. task.shutdown() 50. 51. for worker in self.workerList: 52. worker.cancel()
114 53. down_workers += 1 54. 55. logging.getLogger('application').info("Stopped %d of %d workers", down_workers, up_workers) 56. 57. 58. def getWorkersStatus(self): 59. pass
115 controllers/tasks/Task.py 1. from enum import Enum 2. import logging 3. 4. class Task(): 5. 6. class Status(Enum): 7. # task is starting (config for example) 8. STARTING = 0 9. 10. # task ready for begin 11. READY = 1 12. 13. # task is running 14. RUNNING = 2 15. 16. # task is trying stop 17. STOPPING = 3 18. 19. # task is stopped 20. STOPPED = 4 21. 22. # task fail 23. FAILED = 5 24. 25. # task cancelled 26. CANCELLED = 6 27. 28. # task finished 29. FINISHED = 7 30. 31. # task aborted 32. ABORTED = 8 33. 34. client_id = None 35. 36. def __init__(self, taskName=None, config=None): 37. if taskName: 38. self.taskName = taskName 39. 40. self.configuration = config 41. 42. if config: 43. Task.client_id = config['client_id'] 44. 45. 46. if not self.configuration and self.taskName: 47. logging.getLogger('application').warning('Configuration is not set for task {0}'.format(repr(self.taskName))) 48. 49. self.status = Task.Status.STOPPED 50. 51. # Method to prepare the task 52. def prepare(self): 53. # If task is stopped pass to starting 54. if self.status is Task.Status.STOPPED: self.status = Task.Status.STARTING 55. 56. # Call to prepare implementation 57. self.prepareImpl() 58. 59. # If task is starting pass to ready 60. if self.status is Task.Status.STARTING: self.status = Task.Status.READY 61. 62. if self.taskName: logging.getLogger('application').info("Task %s prepared" % re pr(self.taskName))
116 63. 64. # Method to run the task 65. def doTask(self): 66. # If task is ready then pass to starting 67. if self.status is Task.Status.READY: self.status = Task.Status.RUNNING 68. # Call to do task implementation and get the result 69. 70. if self.taskName: logging.getLogger('application').info("Running task %s" % rep r(self.taskName)) 71. 72. lastResult = self.doTaskImpl() 73. 74. if self.status is Task.Status.RUNNING: self.status = Task.Status.FINISHED 75. 76. if self.taskName: logging.getLogger('application').info("Task %s done" % repr(s elf.taskName)) 77. 78. return lastResult 79. 80. 81. # Method to shutdown the task 82. def shutdown(self): 83. if self.status is Task.Status.RUNNING: self.status = Task.Status.STOPPING 84. 85. if self.taskName: logging.getLogger('application').info("Stopping task %s" % re pr(self.taskName)) 86. 87. # Call to shutdown implementation 88. self.shutdownImpl() 89. 90. if self.status is Task.Status.STOPPED: self.status = Task.Status.ABORTED 91. 92. if self.status is Task.Status.STOPPING: self.status = Task.Status.STOPPED 93. 94. if self.taskName: logging.getLogger('application').info("Task %s stopped" % rep r(self.taskName)) 95. 96. 97. # Get task name 98. def getTaskName(self): 99. return self.taskName 100. 101. # Set task name 102. def setTaskName(self, taskName): 103. self.taskName = taskName 104. 105. # Get task status 106. def getTaskStatus(self): 107. return self.status 108. 109. # Get last result 110. def getLastResult(self): 111. return self.lastResult 112. 113. # Shutdown user implementation 114. def shutdownImpl(self): 115. pass 116. 117. # DoTask user implementation 118. def doTaskImpl(self): 119. pass 120. 121. # Preprare task implementation 122. def prepareImpl(self): 123. pass
117 controllers/tasks/windows/MonitorDiskPartition.py 1. import os 2. import threading 3. import time 4. import win32con 5. import win32file 6. import wmi 7. import logging 8. import pythoncom 9. from controllers.tasks.Task import Task 10. from managers.FilterManager import FilterManager 11. from managers.WindowsValueManager import WindowsValueManager 12. from managers.QueueManager import QueueManager 13. 14. 15. class MonitorDiskPartition(Task): 16. 17. def prepareImpl(self, config=None): 18. pass 19. 20. def doTaskImpl(self): 21. try: 22. pythoncom.CoInitialize() 23. connection = wmi.WMI() 24. # lista que controla que particion logica del disco existe y esta activo en el sistema y cual no 25. threads_state = [] 26. # lista de particion logica del disco. 27. threads_list = [] 28. # lista con las posibles particiones logicas del discos del sistema 29. partitions_logical_disk = [] 30. 31. while self.status is Task.Status.RUNNING: 32. try: 33. WindowsValueManager.set_values() 34. # si es una particion logica del disco fisico se incluira a la list a para monitorizarlo y evitar asi los discos de red 35. for physical_disk in connection.Win32_DiskDrive(): 36. for partition in physical_disk.associators("Win32_DiskDriveToDi skPartition"): 37. for logical_disk in partition.associators("Win32_LogicalDis kToPartition"): 38. if len(partitions_logical_disk) == 0: 39. # si la lista de discos esta vacia es porque acabam os de empezar 40. # se introducen en la lista el disco encontrado 41. disk_partition = logical_disk.Caption + "\\" 42. partitions_logical_disk.append(disk_partition) 43. threads_state.append(0) 44. else: 45. # si existe en la lista algun disco se compara 46. # si coincide no se introduce en la lista (match = 1) 47. # si no existe ningun disco en la lista igual que e se, se introduce y se mete en la lista de estados en su misma posicion un 0. 48. match = 0 49. for element in partitions_logical_disk: 50. disk_partition = logical_disk.Caption + "\\" 51. if element == disk_partition: 52. match = 1 53. 54. if match == 0: 55. disk_partition = logical_disk.Caption + "\\" 56. partitions_logical_disk.append(disk_partition)
118 57. threads_state.append(0) 58. threads_position = 0 59. # se recorre la lista de discos y comprobamos que su ruta existe 60. for path_logical_disk in partitions_logical_disk: 61. if os.path.exists(path_logical_disk): 62. # si existe la ruta y el disco estaba inactivo se crea hilo 63. # como estaba inactivo ponemos un uno en referencia a que a hora si lo esta en su posicion correspondiente 64. if threads_state[threads_position] == 0: 65. thread_monitor_disk = threading.Thread(target=self.chan ges_files, args=(path_logical_disk,),name=path_logical_disk) 66. threads_list.append(thread_monitor_disk) 67. thread_monitor_disk.start() 68. threads_state[threads_position] = 1 69. # Si no existe la ruta pero el disco estaba activo en la ultima comprobacion se pone como inactivo en su posicion correspondiente. 70. else: 71. if threads_state[threads_position] == 1: 72. threads_state[threads_position] = 0 73. threads_position = threads_position + 1 74. 75. time.sleep(2) 76. except: 77. continue 78. except Exception as e: 79. logging.getLogger('application').error(e) 80. finally: 81. pythoncom.CoUninitialize() 82. 83. def shutdownImpl(self): 84. pass 85. 86. def changes_files(self, logical_disk): 87. # Acciones que pueden ocurrir para que salte un evento 88. ACTIONS = { 89. 1: "Created", 90. 2: "Deleted", 91. 3: "Updated", 92. 4: "Renamed from something", 93. 5: "Renamed to something" 94. } 95. 96. FILE_LIST_DIRECTORY = 0x0001 97. 98. # Ruta a monitorizar 99. path_to_watch = logical_disk 100. # configuracion de la monitorizacion 101. hDir = win32file.CreateFile( 102. path_to_watch, 103. FILE_LIST_DIRECTORY, 104. win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE | win32file.F ILE_SHARE_DELETE, 105. None, 106. win32file.OPEN_EXISTING, 107. win32con.FILE_FLAG_OVERLAPPED | win32con.FILE_FLAG_BACKUP_SEMANTICS, 108. None 109. ) 110. exit_thread = 1 111. while exit_thread == 1: 112. 113. try: 114. 115. # Monitorizacion (ruta, buffer, subcarpetas a monitorizar (si o no), cambios con los que salta el evento)
119 116. results = win32file.ReadDirectoryChangesW( 117. hDir, 118. 5012, 119. True, 120. win32con.FILE_NOTIFY_CHANGE_FILE_NAME | 121. win32con.FILE_NOTIFY_CHANGE_DIR_NAME | 122. win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES | 123. win32con.FILE_NOTIFY_CHANGE_SIZE | 124. win32con.FILE_NOTIFY_CHANGE_LAST_WRITE | 125. win32con.FILE_NOTIFY_CHANGE_SECURITY, 126. None, 127. None 128. ) 129. # mientras exista resultados asociados a un evento 130. send_list = [] 131. 132. # Del evento capturado se coge la accion y el archivo implicado en el evento. 133. for action, file in results: 134. # completamos el nombre del fichero implicado en un evento. 135. full_filename = os.path.join(path_to_watch, file) 136. temporal_list = [action, full_filename] 137. # introducimos en la lista de eventos la accion y el fichero implicado. 138. match = FilterManager.get_filter_path(full_filename) 139. send_list.append(temporal_list) 140. # si la ruta existe y es un fichero. 141. if os.path.exists(full_filename) and os.path.isfile(full_fil ename): 142. # si el fichero no esta filtrado por ruta y la accion no es borrar, es decir, el fichero existe para trabajar con el mismo. 143. if match == False and action != 2: 144. file_action = dict() 145. file_action['filename'] = full_filename 146. # se asigna una accion dependiendo del numero relaci onado a la accion 147. if action == 1: 148. file_action['action'] = 'created' 149. else: 150. file_action['action'] = 'updated' 151. # se encola el evento en la cola normal 152. QueueManager.putOnQueue('file_capture',file_action) 153. 154. # en caso de ser un directorio solo se mandara el evento si es nuevo. 155. if os.path.isdir(full_filename) and action == 1 and match == False: 156. #si no esta filtrado el directorio por ruta se trabajara con el siempre y cuando se haya creado, ya que si es actualizado sera un fichero de de ntro. 157. file_action = dict() 158. file_action['filename'] = full_filename 159. file_action['action'] = 'created' 160. QueueManager.putOnQueue('file_capture',file_action) 161. # se mete toda la informacion asociado a un evento en la cola de informacion 162. QueueManager.putOnQueue('file_event',send_list) 163. # si la ruta raiz (en este caso la particion de windows, ya que puede ser un usb y desaparecer) deja de existir se acaba el bucle. 164. if not os.path.exists(path_to_watch): 165. exit_thread = 0 166. 167. except Exception: 168. continue
126 controllers/tasks/general/FileCaptureEventTask.py 1. import logging 2. import os 3. import json 4. from controllers.tasks.Task import Task 5. from managers.FilterManager import FilterManager 6. from managers.QueueManager import QueueManager 7. 8. 9. class FileCaptureEventTask(Task): 10. 11. def prepareImpl(self, config=None): 12. pass 13. 14. 15. def doTaskImpl(self): 16. 17. while self.status is Task.Status.RUNNING: 18. 19. try: 20. # Get item from file_event queue 21. event = QueueManager.getItemFromQueue('file_event', timeout=15) 22. 23. if event: 24. logging.getLogger('application').debug('Getted new file {0}'.format (event)) 25. 26. for elements in event: 27. 28. # Is file filtered? 29. filtered = FilterManager.get_filter_path(elements[1]) 30. 31. # Create new event 32. event = dict() 33. 34. # Client id 35. event['namespace_uuid'] = Task.client_id 36. event['endpoint_uuid'] = Task.client_id 37. 38. # Event type 39. event['type'] = 'file_information' 40. 41. if elements[0] == 2: # File is deleted 42. 43. if not filtered: 44. 45. event['filename'] = elements[1] 46. event['action'] = 'deleted' 47. 48. if elements[0] == 4: # File is renamed 49. 50. if not filtered: 51. action = 'file ' + elements[1] +' renamed to ' 52. 53. if elements[0] == 5: 54. 55. if not filtered and (os.path.isfile(elements[1]) or os.path .isdir(elements[1])): 56. 57. action += elements[1] 58. 59. event['filename'] = elements[1] 60. event['action'] = action 61. 62. if 'action' in event:
127 63. 64. # Dict to JSON 65. json_event = json.dumps(event) 66. 67. # Create new kafka message 68. kafka_message = dict() 69. kafka_message['topic'] = 'rb_ioc' 70. kafka_message['content'] = json_event 71. 72. # Logging in binnacle 73. logging.getLogger('binnacle').info(json_event) 74. 75. # And put message in kafka queue 76. QueueManager.putOnQueue('kafka', kafka_message) 77. 78. except Exception as e: 79. if self.status is Task.Status.RUNNING: 80. logging.getLogger('application').error(e) 81. 82. continue 83. 84. def shutdownImpl(self): 85. pass
128 controllers/tasks/windows/CreationProcessTask.py 1. import json 2. import logging 3. import os 4. import time 5. import psutil 6. import wmi 7. import pythoncom 8. import win32api 9. import win32con 10. import win32process 11. from controllers.tasks.Task import Task 12. from managers.QueueManager import QueueManager 13. 14. class CreationProcessTask(Task): 15. 16. def prepareImpl(self, config=None): 17. pass 18. 19. 20. def doTaskImpl(self): 21. try: 22. 23. pythoncom.CoInitialize() 24. connection = wmi.WMI() 25. 26. watcher = connection.Win32_Process.watch_for("creation") 27. while self.status is Task.Status.RUNNING: 28. try: 29. 30. new_process = watcher() 31. event = dict() 32. 33. # Get executable 34. executable = self.get_executable(new_process, new_process.ProcessID , new_process.Caption, new_process.CommandLine, new_process.Description) 35. 36. event['namespace_uuid'] = Task.client_id 37. event['endpoint_uuid'] = Task.client_id 38. event['type'] = 'process' 39. event['action'] = 'creation' 40. 41. # Get PID if exists 42. if new_process.ProcessID is not None: 43. event['pid'] = new_process.ProcessID 44. 45. # Get caption if exists 46. if new_process.Caption is not None: 47. event['name'] = new_process.Caption 48. 49. # Get executable if exists 50. if executable is not None: 51. event['application'] = executable 52. 53. self.characterist(new_process, event) 54. 55. try: 56. 57. # Get parent process 58. parent_process = psutil.Process(new_process.ParentProcessId) 59. 60. if parent_process: 61. try: 62. # Get executable parent
129 63. executable_parent = self.get_parent_executable(new_proc ess.ParentProcessId, parent_process.name()) 64. event['parent_application'] = executable_parent 65. except Exception as e: 66. logging.getLogger('application').error(e) 67. except Exception as e: 68. logging.getLogger('application').error(e) 69. continue 70. 71. event['timestamp'] = int(time.time()) 72. 73. json_event = json.dumps(event) 74. logging.getLogger('binnacle').info(json_event) 75. 76. # Create new kafka message 77. kafka_message = dict() 78. kafka_message['topic'] = 'rb_ioc' 79. kafka_message['content'] = json_event 80. 81. # Put message in kafka queue 82. QueueManager.putOnQueue('kafka', kafka_message) 83. 84. except Exception as e: 85. logging.getLogger('application').error(e) 86. continue 87. 88. except Exception as e: 89. logging.getLogger('application').error(e) 90. finally: 91. pythoncom.CoUninitialize() 92. 93. 94. def shutdownImpl(self): 95. pass 96. 97. def characterist(self, process, bitacora_string): 98. try: 99. 100. # Get priority 101. if process.Priority: 102. bitacora_string['priority'] = process.Priority 103. 104. #Get session ID 105. if process.SessionId: 106. bitacora_string['sessionId'] = process.SessionId 107. except Exception as e: 108. logging.getLogger('application').error(e) 109. pass 110. 111. # Allow to get parent executable 112. def get_parent_executable(self, pid, name): 113. 114. try: 115. # Get process by PID 116. executable = self.get_process_executable(pid) 117. 118. # If not, get executable by name 119. if not executable: 120. executable = self.get_process_executable_by_name(name) 121. 122. if not executable: 123. return None 124. 125. else: 126. return executable 127. 128. except Exception as e:
130 129. logging.getLogger('application').error(e) 130. return None 131. pass 132. 133. 134. # Allow to get executable by name 135. def get_process_executable_by_name(self, filename, cmdline=None): 136. try: 137. 138. if not cmdline: 139. system_path = os.environ['WINDIR'] + "\\System32\\" 140. full_path = system_path + filename 141. if os.path.exists(full_path): 142. executable = full_path 143. else: 144. executable = None 145. 146. else: 147. if "\\SystemRoot\\".lower() in cmdline.lower(): 148. os_path = os.environ['WINDIR'] 149. system_path = cmdline.replace("\\SystemRoot",os_path)[:cmdli ne.rfind("\\")] 150. full_path = system_path + filename 151. if os.path.exists(full_path): 152. executable = full_path 153. else: 154. executable = None 155. else: 156. executable = None 157. except Exception as e: 158. logging.getLogger('application').error(e) 159. executable = None 160. return executable 161. 162. 163. #Allow to get process by PID 164. def get_process_executable(self, pid): 165. 166. if not pid or pid == 4: 167. return None 168. 169. try: 170. handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, False, pi d) 171. executable = win32process.GetModuleFileNameEx(handle,0) 172. win32api.CloseHandle(handle) 173. if "\\SystemRoot\\".lower() in executable.lower(): 174. executable = None 175. if "\\??\\" in executable: 176. executable = executable.replace("\\??\\", "") 177. 178. except Exception as e: 179. logging.getLogger('application').error(e) 180. executable = None 181. return executable 182. 183. 184. # Allow to get executable 185. def get_executable(self, process, pid, name, commandLine, description): 186. 187. try: 188. 189. executable = process.ExecutablePath 190. 191. if not executable: 192.
131 193. # Try to get executable by PID 194. executable = self.get_process_executable(pid) 195. 196. if not executable: 197. executable = process.ExecutablePath 198. 199. if not executable: 200. # Try to get executable by name 201. executable = self.get_process_executable_by_name(name) 202. 203. if not executable: 204. 205. if commandLine: 206. # Try to get executable by name and CLI 207. executable = self.get_process_executable_by_name(name, com mandLine) 208. 209. if not executable: 210. 211. if commandLine: 212. # Try to get executable by description and CLI 213. executable = self.get_process_executable_by_name(descripti on, commandLine) 214. 215. if not executable: 216. return None 217. else: 218. return executable 219. except: 220. pass
132 controllers/tasks/windows/DeletionProcessTask.py 1. import json 2. import logging 3. import time 4. import wmi 5. import pythoncom 6. from controllers.tasks.Task import Task 7. from managers.QueueManager import QueueManager 8. 9. 10. class DeletionProcessTask(Task): 11. 12. def prepareImpl(self, config=None): 13. pass 14. 15. def doTaskImpl(self): 16. 17. try: 18. pythoncom.CoInitialize() 19. 20. connection = wmi.WMI() 21. 22. watcher = connection.Win32_Process.watch_for("deletion") 23. 24. while self.status is Task.Status.RUNNING: 25. try: 26. deletedProcess = watcher() 27. 28. event = dict() 29. event['namespace_uuid'] = Task.client_id 30. event['endpoint_uuid'] = Task.client_id 31. event['type'] = 'process' 32. event['action'] = 'deletion' 33. event['name'] = deletedProcess.Caption 34. event['pid'] = deletedProcess.ProcessID 35. event['timestamp'] = int(time.time()) 36. 37. json_event = json.dumps(event) 38. 39. logging.getLogger('binnacle').info(json_event) 40. 41. # Create new kafka message 42. kafka_message = dict() 43. kafka_message['topic'] = 'rb_ioc' 44. kafka_message['content'] = json_event 45. 46. # Put message in kafka queue 47. QueueManager.putOnQueue('kafka', kafka_message) 48. 49. except Exception as e: 50. logging.getLogger('application').error(e) 51. continue 52. 53. except Exception as e: 54. logging.getLogger('application').error(e) 55. 56. finally: 57. pythoncom.CoUninitialize() 58. 59. def shutdownImpl(self): 60. pass controllers/tasks/windows/WebProcessTask.py
133 1. import json 2. import logging 3. import time 4. import psutil 5. import wmi 6. import pythoncom 7. from controllers.tasks.Task import Task 8. from managers.QueueManager import QueueManager 9. 10. 11. class WebProcessTask(Task): 12. 13. def prepareImpl(self, config=None): 14. pass 15. 16. def doTaskImpl(self): 17. 18. try: 19. pythoncom.CoInitialize() 20. connection = wmi.WMI() 21. #Se inicia la monitoricion de procesos en tiempo real, en este caso se moni torizaran los procesos que se crean. 22. watcher = connection.Win32_Process.watch_for("creation") 23. 24. while self.status is Task.Status.RUNNING: 25. try: 26. #Cuando un nuevo proceso se cree en el sistema entra en el try ante rior y capturamos el proceso creado. 27. new_process = watcher() 28. 29. try: 30. parent_process = psutil.Process(new_process.ParentProcessId) 31. soon_process = psutil.Process(new_process.ProcessID) 32. connections_opened = '' 33. try: 34. if parent_process: 35. #si el proceso padre existe, capturamos sus conexiones d e red. 36. #Si el proceso es de red tendra conexiones, sino dara un error y se ignorara el proceso. 37. connections_opened = parent_process.connections(kind="al l") 38. else: 39. if soon_process: 40. connections_opened = soon_process.connections(kind=" all") 41. 42. ip_list = [] 43. ip_non_repeat_list = [] 44. 45. for tupla in connections_opened: 46. #recorremos conexion a conexion, se coge la IP y puerto y se mete en la lista de IP. 47. try: 48. conexion = tupla[4] 49. ip = conexion[0] 50. puerto = conexion[1] 51. if ip != None and ip !='' and puerto != None and pue rto !='': 52. ip_string = str(ip) + ":" + str(puerto) 53. ip_list.append(ip_string) 54. except: 55. continue 56. #Recorremos la lista de conexiones para coger las no repetid as, ya que hay algunas redundantes. 57. #Para saber si esta repetida usaremos una bandera a 0 o 1. 58. for element in ip_list:
134 59. flag = 0 60. for element_ip in ip_non_repeat_list: 61. if element == element_ip: 62. flag = 1 63. #Si la conexion no se repite se guarda en la lista de la s no repetidas. 64. if flag == 0: 65. ip_non_repeat_list.append(element) 66. new_string = '' 67. #Como cada proceso puede tener varias conexiones pues enviam os a la bitacora todas. 68. for ip_connection in ip_non_repeat_list: 69. new_string += str(ip_connection) + '; ' 70. 71. if len(ip_non_repeat_list) > 0: 72. 73. event = dict() 74. event['namespace_uuid'] = str(Task.client_id) 75. event['endpoint_uuid'] = str(Task.client_id) 76. event['type'] = 'connection' 77. event['application'] = str(new_process.Caption) 78. event['pid'] = new_process.ProcessID 79. event['connections'] = new_string 80. event['timestamp'] = int(time.time()) 81. 82. json_event = json.dumps(event) 83. 84. logging.getLogger('binnacle').info(json_event) 85. logging.getLogger('application').debug('data for kafka : {0}'.format(json_event)) 86. 87. # Create new kafka message 88. kafka_message = dict() 89. kafka_message['topic'] = 'rb_ioc' 90. kafka_message['content'] = json_event 91. 92. # Put message in kafka queue 93. QueueManager.putOnQueue('kafka', kafka_message) 94. 95. except Exception as e: 96. logging.getLogger('application').error(e) 97. continue 98. 99. except Exception as e: 100. logging.getLogger('application').error(e) 101. continue 102. 103. except Exception as e: 104. logging.getLogger('application').error(e) 105. continue 106. 107. except Exception as e: 108. logging.getLogger('application').error(e) 109. 110. finally: 111. pythoncom.CoUninitialize() 112. 113. 114. def shutdownImpl(self): 115. pass controllers/tasks/general/KafkaSenderTask.py 1. import logging
135 2. import sys 3. 4. from controllers.tasks.Task import Task 5. from managers.KafkaManager import KafkaManager 6. 7. from managers.QueueManager import QueueManager 8. 9. class KafkaSenderTask(Task): 10. 11. 12. def prepareImpl(self): 13. 14. # create new kafka manager 15. try: 16. self.kafkaManager = KafkaManager(self.configuration) 17. except Exception as e: 18. logging.getLogger('application').error(e) 19. 20. def doTaskImpl(self): 21. try: 22. while self.status is Task.Status.RUNNING: 23. message = QueueManager.getItemFromQueue('kafka') 24. logging.getLogger('application').debug('Received message : {0}'.format( message)) 25. self.kafkaManager.sendMessage(key=Task.client_id, msg=message['content' ], topic=message['topic']) 26. except Exception as e: 27. logging.getLogger('application').error(e) 28. 29. 30. def shutdownImpl(self): 31. pass