viernes, enero 12, 2018

Hidden Networks: Una herramienta de @elevenpaths para la detección y análisis de Redes Ocultas en tu empresa

Llevamos algún tiempo trabajando con la detección de Redes Ocultas o Hidden Networks (Chema Alonso ya habló por primera vez de ellas en 2014 en este artículo) ya que nos parece un tema muy interesante que nos permite entender hasta qué punto es posible interconectar diferentes segmentos de red o incluso redes distantes totalmente aisladas sin estar conectadas físicamente entre ellas (incluso pertenecientes a diferentes empresas, organismos o plataformas), utilizando simplemente un dispositivo USB (pendrive).

Figura 1:  Hidden Networks, una tool para la detección y análisis de Redes Ocultas en tu empresa

Hace unos meses publicamos unos scripts en PowerShell capaces de conectar con los diferentes equipos de la red para comprobar los pendrives que habían sido conectados en algún momento en esos ordenadores y crear una lista desde la cual analizar su trazabilidad. Partiendo de esta lista, es posible dibujar un grafo el cual muestra una Hidden Network basándose en dicha información recopilada.

Figura 2: PoC de Hidden Networks publicada en la web de ElevenPaths

Ahora hemos dado un paso más y hemos desarrollado una PoC en Python donde unificamos las opciones más interesantes que ya aplicamos en su día en los scripts pero además hemos añadido nuevas funcionalidades como, por ejemplo, dibujar directamente desde la aplicación (que llamamos también HN) el grafo resultante del análisis. De esta forma tenemos una única PoC capaz de analizar, recopilar e incluso dibujar la red resultante.

Figura 3: Ejemplo de ejecución de HN dibujando la red oculta resultante

Vamos a detallar una a una sus características principales y explicar cómo funcionan internamente:

Project

Para facilitar los análisis y tener toda la información obtenida debidamente organizada, hemos implementado la opción de crear proyectos individualizados donde iremos almacenando los resultados de los análisis que vayamos realizando en diferentes carpetas en nuestro equipo. Por lo tanto, el primer paso antes de empezar será abrir o crear un proyecto (excepto si queremos dibujar solamente un grafo a partir de CSV, lo veremos más adelante). Si elegimos la opción de crear un proyecto, HN nos indicará dónde queremos guardar la información del mismo y le asignaremos un nombre de fichero (Project file). Una vez creado o abierto se mostrará la ruta completa a dichos ficheros.

Figura 4: Opciones del proyecto

Cada proyecto tiene un nombre personalizado para describirlo que podemos introducir en el campo Project name. También se creará una carpeta con el mismo nombre de fichero que hayamos asignado en Project File donde se almacenarán los ficheros CSV y JSON. Los proyectos constan básicamente de tres ficheros, uno con la información del proyecto en sí que será un fichero de texto con la extensión .hn y otros dos, uno CSV y otro JSON (localizados dentro la carpeta con el mismo nombre) donde se almacenará toda la información recopilada. El fichero JSON se genera a partir del fichero CSV utilizando la librería Python json y el comando dump: json.dump(filas, ficheroCSV).

Las filas se han obtenido generando una lista a partir del fichero CSV. Siempre que nos refiramos en el artículo a un cambio en el fichero CSV, este se replicará de forma automática el fichero JSON. Los datos almacenados en el fichero CSV y JSON, cada uno en sus respectivos formatos, son los siguientes: computer_name, computer_ip, usbdevice_name y usbdevice_id. Un posible ejemplo de una recopilación de datos en un fichero CSV podría ser la siguiente:

Figura 5: Ejemplo real de un fichero CSV

Los valores usbdevice_name y usbdevice_id se obtienen desde el Registro de Windows, en cambio computer_name y computer_ip son peticiones al sistema:
• computer_name: nombre del ordenador analizado. Se obtiene utilizando la librería socket de Python (luego lo veremos en detalle). 
• computer_ip: dirección IP del ordenador auditado. Se obtiene también con la librería socket. 
• usbdevice_name: valor de la key “Friendlyname” o nombre “amigable” del dispositivo USB. Se obtiene accediendo, en función de si conectamos en local o en red con el equipo, analizando la rama del registro:
HKLM\SYSTEM\CurrentControlSet\Enum\USBSTOR 
• usbdevice_id: key “ContainerID”, id único del dispositivo. Se obtiene de la misma manera que usbdevice_name.
Local computer options

Esta funcionalidad permite recopilar la información local de los dispositivos USB conectados, es decir, de la máquina desde la cual se está ejecutando HN. Esta opción es útil para ir recopilando la información de los equipos a los cuales no tengamos forma de auditar de forma remota o simplemente queramos ir equipo por equipo de forma manual. Cada vez que pulsamos el botón "Get Local registry info", la información que hemos detallado en el apartado Project será recuperada. En principio sólo se mostrará por pantalla, para almacenarla en el proyecto hay que marcar la opción "Save output to CSV file".

Figura 6: Opciones para recuperar la información de posibles pendrives en el equipo local

Para obtener el valor computer_name se utiliza la librería socket de Python con el comando socket.gethostname() y para obtener el valor computer_IP se utiliza la función socket.gethostbyname(computername), la cual tiene como parámetro el nombre de equipo que ya hemos comentado que se puede recuperar con el comando socket.gethostname().

Para recuperar la información de los dispositivos USB se accede directamente al Registro de Windows. HN sólo recupera los datos usbdevice_name y usbdevice_id, pero una vez se accede a la rama del registro correspondiente, es posible recuperar más información si fuera necesario. En principio esta es suficiente para poder dibujar el grafo y definir una red oculta.

El primer paso es almacenar en una lista los dispositivos detectados en la siguiente rama del registro del equipo auditado:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USBSTOR
Esto se hace utilizando las funciones readSubKeys y readValues. La primera, readSubKeys devuelve una lista con las sub-ramas de una rama específica y readValues devuleee una lista tipo diccionario con el par nombre de la key y su valor correspondiente. Para poder construir la rama completa que contiene toda la información referente al dispositivo USB se utilizan varios bucles que van montando la ruta completa a partir de los datos obtenidos desde la lista que contiene las sub-ramas y los datos del diccionario.

Los dos valores principales que definen la información almacenada de un dispositivo USB y que tenemos que añadir a la rama del registro principal que hemos mencionado son, en primero lugar la cadena de texto que se construye con diferentes datos como el VendorID o el ProductID y luego el número de serie:
• Vendor,ID+ProductID+Revisión: cadena de texto que incluye los datos del fabricante, el ID del producto y la versión. Por ejemplo, esta sería una rama real utilizada en la PoC: 
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USBSTOR\Disk&Ven_&Prod_USB_DISK_2.0&Rev_PMAP\
• Serial No: número de serie del dispositivo. 
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USBSTOR\Disk&Ven_&Prod_USB_DISK_2.0&Rev_PMAP\560057DFAE277552&0
Una vez hemos obtenido la ruta completa de la rama, ya es posible acceder a los datos que buscamos del dispositivo USB. Por ejemplo, esta sería la ruta completa del registro donde reside el valor de la key llamada “Friendlyname” (usbdevice_name):

Figura 7: Ejemplo de la rama del Registro de Windows que almacena la información

Networking options (WMI)

Si somos administradores de un dominio, es posible utilizar la opción de auditar todos los equipos que queramos y que sean accesibles desde el servidor desde el cual estemos ejecutando la aplicación. HN utiliza WMI (Windows Management Instrumentation), para acceder a los equipos de la red, utilizando la librería Python con ese mismo nombre.

Figura 8: Opciones para recuperar la información de pendrives de equipos remotos

Esta función de red necesita en primer lugar, un fichero de texto en el cual aparezcan las direcciones IP o los FQDN de cada equipo (pueden utilizarse ambas indistintamente). Por ejemplo, este sería el contenido de un fichero con el listado de equipos llamado "servers_examples.txt":

Figura 9: Ejemplo de fichero con el listado de equipos a auditar

En segundo lugar, será necesario introducir el nombre de usuario administrador del dominio, así como su contraseña en la sección Domain credentials. Finalmente, al pulsar el botón Retrieve remote info comenzará la recolección de información de los equipos listados en el fichero de ordenadores uno por uno. Todo el proceso se irá mostrando paso a paso en la ventana Output. En el siguiente vídeo se muestra el proceso de recolección de varios equipos en un dominio en dos redes distintas (diferentes subnets) pero accesibles desde el ordenador desde el cual se ejecuta la aplicación:

Figura 10: Vídeo de ejemplo de Hidden Networks

La técnica de recopilación remota de información difiere respecto a la forma de recopilarlos a nivel local. El paso principal será crear una conexión con el equipo pasando como parámetros la dirección IP (o FQDN), el nombre del usuario administrador y su contraseña de la siguiente manera:

Figura 11: Llamada WMI desde Python para conectar con un equipo remoto

El objeto devuelto nos permitirá recuperar información del equipo remoto. En nuestro caso utilizamos la siguiente función para obtener los valores de las sub-ramas que cuelgan de la ruta principal:

Figura 12: Acceso a las subramas de la rama principal que contiene los datos del USB

Basándonos en el número de elementos devueltos (número de elementos en la lista “names” utilizando una función len) creamos un bucle que va recorriendo todas las ramas cuyo nombre hemos obtenido desde la lista “names”. Por ejemplo, dentro del bucle (variable i, la cual referencia a cada uno de los elementos de “name”) vamos recorriendo las diferentes ramas (basándonos en el número de elementos que hemos detectado numdevices), las cuales pertenecen cada una a un dispositivo USB, de la siguiente manera:

Figura 13: Acceso a la rama final que corresponde con los datos específicos de un USB en concreto

Una vez recuperado el valor del nombre de la rama en remotereg1 (hay que hacer algunas operaciones adicionales para extraer el valor exacto del nombre de la rama, se pueden observar en el código fuente publicado), almacenamos dicho valor en la variable llamada finalbranch. Para obtener los valores “Friendlyname” y “ContainerID” utilizamos el siguiente código:

Figura 14: Parte del código encargada de recuperar los valores de "Friendlyname" y "ContainerID"

Esta información se almacena en los ficheros CSV y JSON. Esta operación se podría optimizar utilizando diferentes hilos de proceso, intentaremos implementarlo en una nueva versión de esta PoC.

Plot options

Esta funcionalidad permite visualizar mediante un grafo no dirigido, la red oculta indicando sus nodos e interconexiones. El botón Plot Project dibujará directamente la información recopilada en el proyecto que tengamos abierto. El botón Plot single CSV permite importar un fichero con formato CSV, el cual cumpla con el formato de HD, y luego dibujar la red oculta. También se crea por defecto una copia de dicho fichero CSV en formato JSON en la misma ubicación.

Figura 15: Opciones para dibujar Hidden Networks usando un grafo

Estos son algunos ejemplos reales de redes dibujadas por HN después obtener la información sobre los USB que hayan sido instalados en de cada equipo. Cada ventana corresponde a un dispositivo USB y se observa perfectamente la trazabilidad mostrando cada nodo (el cual corresponderá con un equipo en la red) con el nombre de máquina y la dirección IP correspondiente. Los arcos son los conectores que indicarán la ruta o conexión establecida entre dichos nodos creada por el USB:

Figura 16: Ejemplos reales de grafos obtenidos de dispositivos USB remotos y locales

Para dibujar los grafos hemos utilizado la librería de Python llamada pyplot. En este caso los datos utilizados para dibujar son los obtenidos directamente del fichero JSON. La ope comparando uno a uno los ID de los USB con el resto de elementos recopilados. Si el ID del USB que se está analizando coincide con el ID localizado en otra máquina, entonces se crean los dos nodos, el origen y el destino además de dibujar arco el cual indica una conexión entre ellos. Para crear los nodos y el arco se utiliza la función:

Figura 17: Porción de código que dibuja los arcos de los grafos

Donde nuevonodo es el nodo origen donde se detectó por primera vez el USB que se está analizando y nuevonodo2 es el nodo donde ha aparecido una coincidencia de ID, indicando que también ha pasado por ese equipo/nodo. Finalmente dibuja el arco de unión.

Figura 18: Código utilizado para configurar los parámetros generales del grafo

HN es una prueba de concepto, por lo tanto, el código fuente (escrito en Pyhton 3.3) es mejorable, ya que nuestra prioridad ha sido poder plasmar todas las ideas en una sola aplicación práctica de la manera más rápida posible y así poder demostrar el potencial de esta idea. De todas formas, estamos trabajando en la depuración y mejora del código además de añadir nuevas funcionalidades como hilos para la conexión con los equipos remotos, grafos con arcos dirigidos, inclusión de fechas y alguna que otra sorpresa que ya contaremos. Por lo tanto, atento porque esta no será la última vez que hablemos de las Hidden Networks ni de la PoC HN ;)
- Puedes descargar el paper que ya publicamos sobre el concepto de Hidden Network desde el siguiente enlace: Hidden Networks WhitePaper 
- Y el código fuente desde el GitHub de ElevenPaths: Hidden Networks en GitHub 
- Puedes poner cualquier comentario o realizar cualquier pregunta (sobre este o cualquier otro proyecto) en la Comunidad de Eleven Paths:
Autor: Fran Ramírez (@cyberhadesblog) escritor de libro "Microhistorias: anécdotas y curiosidades de la historia de la informática" e investigador en ElevenPaths

No hay comentarios:

Publicar un comentario