
Artículo · Pentesting
[Native Graph Database] ¿Que es Neo4j? + DevOP Python para crear un "Graph" Automaticámente
Introducción
Dentro de mi labor en el mundo de la ciberseguridad, he buscado varias formas de darle un sentido distinto a los análisis Threat Intelligence que realizabamos. Quería una herramienta que me permitiera mostrar la informacion contenida en un muchos archivos LOGs, de una forma mucho más dinámica y de fácil acceso. Es asi, como llegue al término "Base de Datos de Grafos.".
¿Qué es un Grafo?
Según Wikipedia, tenemos que:
"En matemáticas y ciencias de la computación, un grafo es un conjunto de objetos llamados vértices o nodos unidos por enlaces, llamados aristas o arcos, que permiten representar relaciones binarias entre elementos de un conjunto, que son objeto de estudio de la teoría de grafos.
Típicamente, un 'grafo' se representa gráficamente como un conjunto de puntos (vértices o nodos) unidos por líneas (aristas).
Desde un punto de vista práctico, los grafos permiten estudiar las interrelaciones entre unidades que interactúan unas con otras. Por ejemplo, una red de computadoras puede representarse y estudiarse mediante un grafo, en el cual los vértices representan terminales y las aristas representan conexiones (las cuales, a su vez, pueden ser cables o conexiones inalámbricas).
Prácticamente cualquier problema puede representarse mediante un grafo, y su estudio trasciende a las diversas áreas de las ciencias exactas y las ciencias sociales."
Wikipedia: https://es.wikipedia.org/wiki/Grafo
Gráficamente, su representación la podemos dibujar de la siguiente forma:
Mediante los grafos podemos describir una multitud de procesos, situaciones, relaciones, estructuras, etc. Por ejemplo, un grafo nos puede servir para representar los enlaces moleculares de un elemento químico.
Otro ejemplo lo podemos verificar en una red de carreteras. Como vemos, cada arista contiene la distancia en kilómetros que separa cada ciudad. Esto es útil para, por ejemplo, poder calcular la ruta más corta entre dos puntos del grafo.
Con esta evidencia, mi búsqueda exhaustiva habia resultado, quería mostrar la información contenida en archivos LOGS, pero a modo de Grafo. Vamos a ser sinceros, buscar datos en LOGS no estan sencillo. Ahora, si nos apoyamos en una herramienta como un SIEM, es bien dificil buscar aplicaciones o contenidos dentro de estas, que permitan realizar un gráfico en formato grafos. (Watson/IBM realiza un modelado de grafos, pero es de paga $$).
Fue asi como me puse a buscar aplicaciones y herramientas que me permitieran explotar esta idea, de lograr pasar un conjunto de archivos LOGS a un grafo. Así llegue a varias herramientas que permitian modelar datos, graficarlos, hacer consultas, etc. Pero una en particular llamo mi atención, tanto por la facilidad de uso, herramientas asociadas, lenguaje cercano a SQL y con el respaldo de una comunidad, que integran desarrolloas como también por la cantidad de plugins que tiene hacia plataformas varias. Esta herramienta, se llama NEO4J.
NEO4J
Citaré un artículo de internet, que permite detallar de una forma excelente, la herramienta Neo4j:
"Está diseñada para almacenar entidades y relaciones de datos con un nivel alto de optimización para realizar operaciones gráficas como recorridos, agrupamiento de datos y cálculos de rutas óptimas por solo mencionar algunas de sus funciones.
El poder de Neo4j se expresa a toda potencia en la analítica gráfica de los datos y los algoritmos gráficos que ayudan a comprender los conjuntos de datos sin importar el nivel de profundidad y complejidad.
Con Neo4J podemos moldear y entender el comportamiento de sistemas complejos de datos en tiempo real, haciendo un especial énfasis en las relaciones entre puntos específicos.
Esta importante bases de datos cuenta con cinco algoritmos de búsqueda de trayectoria y elementos transversales que nos permiten ejecutar búsquedas paralelas de profundidad y amplitud.
Entre los casos de usos más importantes que pueden darse a Neo4j es el establecimiento de patrones sospechosos de fraude y el análisis en tiempo real de los datos para evitar el blanqueo de capitales.
Adicionalmente esta base de datos ha adquirido renombre fuera del mundo tecnológico debido a que fue el motor de bases de datos principales que se usó en la famosa investigación de los Panama Papers."
https://www.grapheverywhere.com/comparativa-bases-de-datos-de-grafos-cual-es-mejor-cual-elegir/
Neo4j tiene varias formas de implementación, entre las que destacan un aplicativo local, un servidor central o una solución en la nube. Pero aquí detallaremos más la instalación local, la cuál permite crear y mantener bases de datos de forma similar a una implementación central y hacer uso del poder de esta excelente herramienta.
Otra de las caracteristicas destacables de Neo4j, es el uso de un lenguaje propio para realizar consultas sobre la base de datos, con consultas similares a SQL. Este lenguaje, llamado CYPHER, cuenta con toda la documentacion en el sitio oficial de Neo4j y su uso es muy intuitivo, de rápido aprendizaje.
Instalación Local
Para instalar el aplicativo local de NEO4J, realizaremos los siguientes pasos:
1. Acceder a la siguiente URL: https://neo4j.com/download/
2. Descargar aplicativo
3. Copiar la licencia de activacion que nos generará la descarga, la cual nos permitirá activar nuestro aplicativo.
3. Ejecutar e Instalar.
Opciones Extras
1. Sandbox: La opcion de Sandbox, permite emular la instalación local, ejecutando una instancia a la vez, de las bases de datos creadas. Permite la implementación de modelos de consulta que ya vienen pre creadas.
2. Central: Existe la posibilidad de instalar una instancia DOCKER, la cual permite conexiones remotas a las bases de datos, siempre y cuando se accedan con las credenciales validas. Asi tambien, como soluciones de servidor central, que implementan mejoras en relacion a la instalacion local, enfocadas en trabajos dinamicos, paralelos y con acceso rapido a la informacion contenida.
NEO4J - Primeros Pasos
Cuando instalemos y activemos nuestro aplicativo, generalmente se carga con informacion precargada, a modo de prueba.
Como primera interaccion, crearemos nuestra primera base de datos, la cual guardará las relaciones y nodos de nuestro grafo. Con esto, podremos realizar consultas y empezar a graficar la información solicitada.
NEO4J: Creación Base de Datos
1. Crearemos una base de datos local.
2. Le daremos los datos básicos de acceso, los cuales guardaremos para, en el siguiente paso, importar la información desde nuestros LOGS.
3. Le daremos crear.
4. Con la base de datos creada, la iniciaremos para que se encuentre activa.
5. Ahora, le daremos clic al botón Open, lo cual nos abrirá el Browser de Neo4j, encargado de ser la interfaz entre el usuario y nuestra base de datos.
OJO: Para este tipo de soluciones, solamente permite mantener una base de datos activa, a la vez.
Archivos de Registro Previo
Hemos definido el siguiente formato, para los archivos LOGS, para lograr la extraccion e importacion de informacion a Neo4j. El formato, contempla las siguientes columnas:
- SOURCE_IP
- SOURCE_PORT
- SOURCE_USER
- DESTINATION_IP
- DESTINATION_PORT
- PROTOCOL
- EVENT SUBTYPE
Integracion NEO4J + Archivos LOGS
Gracias a un poco de desarrollo y horas de plasmar ideas, hemos concretado el siguiente desarrollo en PYTHON, el cual permite crear los nodos y sus relaciones, en base a la informacion contenida en los archivos formateados.
El programa tiene la gracia de no guardar ninguna credencial, dado que las solicita en el momento de su ejecución.
Los parámetros necesarios para su correcta ejecución, se describen a continuación:
- -f, --file
- -s, --server
- -u, --user
El programa desarrollado, interpreta un conjunto de protocolos, reemplazandolo en el grafica permitiendo una identificación más certera. Además, considera las conexiones LOOPBACK, para lograr una relacion hacia el mismo nodo.
Líbrerias Necesarias
Función Acceso BD
Interacción CYPHER - Creación de Nodos y relaciones
I+D: EJECUCIÓN Y PRUEBAS
Ante lo anterior, ejecutaremos el script, cargandole un archivo de ejemplo y veremos resultados de su ejecucion.
1. Ejecucion SCRIPT
2. Primeros resultados NEO4J
A medida que se realiza la carga de datos, iremos viendo en el costado izquierdo de Neo4j Browser, la carga de las siguientes propiedades básicas:
- Nodos
- Tipos de Relaciones
- Propiedades Claves
Si elegimos alguna de las propiedades ya cargadas, NEO4J cargará una consulta predefinida, limitando los resultados a 25. Desde aqui, tenemos la opción de realizar una navegación sobre los nodos cargados, permitiendo cargar en vivo, las relaciones de los nodos de mi interés.
Drilldown
El aplicativo permite la personalizacion de las relaciones creadas, pudiendo cambiar el color tanto de los nodos como de las relaciones, así como definir que información, de la importada, mostrar como dato definitorio para ambos items.
Opciones de Personalización
Una de las últimas características a mostrar, en este corto artículo, es la posibilidad de generar una vista más "generalizada" del tráfico contenido en archivos LOG. Así, obtenemos una visión más general ámbito de red extraído.
Con esto, finalizamos este corto resumen del poderío de NEO4J y de paso, comparto un desarrollo pequeño, pero totalmente útil, dado que facilita aún más la carga de datos, para el pronto análisis a efectuar.
Código PYTHON
neo4j_import.py#!/usr/bin/env python3import argparseimport csvimport datetimeimport uuidimport getpassfrom py2neo import Graph, Node, Relationshipimport sysdef initConnect(uri, user):try:password = getpass.getpass("Ingrese la contraseña de conexión a la Base de datos# ")driver = Graph(bolt=uri, user=user, password=password)except:print("Error de conexion a la base de datos. Verifique la información ingresada.")return driverdef closeConnect(driver):Graph.close(driver)def replaceCaracter(data):data = data.rstrip("\n")return data.replace("\"","")def createNode(connect,key,label):node = Node(label, ip=key)connect.create(node)return nodedef mergeNode(connect,key,label):node = Node(label, ip=key)merge = connect.merge(node,label,"ip")return nodedef createRelations(keysrc,label,keydst,srcPort,Prot,dstPort,eventSubtype,connect):relation = Relationship(keysrc, label, keydst, SourcePort=srcPort, Protocol=Prot,DestinationPort=dstPort, EventSubtype=eventSubtype)connect.create(relation)def searchValue(dictionary,value):if dictionary:valor = dictionary.values()if str(value) in valor:return Falseelse:return Trueelse:return Truedef getIndex(dictionary,value):for clv,vls in dictionary.items():if str(value) == vls:return clvdef getLabel(port,evtSbt):try:protocolPort = {80 : "HTTP", 443: "HTTPS", 20: "FTP/DATA", 21: "FTP/CONTROL",23: "TELNET", 22: "SSH/SCP/SFTP", 25: "SMTP", 8: "ICMP", 110: "POP3",53: "DNS", 143: "IMAP", 995: "POP3/SSL", 587: "SMTP/SSL", 3306: "MySQL",3389: "RDP", 1433: "SQL/Server", 7680:"DeliveryOpt/Microsoft", 3768: "VRRP",514:"Syslog",1701:"VPN/L2TP",67:"DHCP/Server",68:"DHCP/Client",88:"Kerberos",137:"Netbios/NameServices",138:"Netbios/DatagramSend",139:"Netbios/Sessions",161:"SNMP",162:"SNMP/TRAP",389:"LDAP",445:"Microsoft/DS",1080:"SOCKS/PROXY",1434:"SQL/Monitor",1512:"WINS",5400:"VNC",5500:"VNC",5600:"VNC",5700:"VNC",5800:"VNC",5900:"VNC",6667:"IRC",9050:"TOR"}label = protocolPort[int(port)]except:label = "TRAFFIC"return str(label+"/"+evtSbt.upper())def initWorks(connect):connect.run("MATCH (m) DETACH DELETE m")for constraint in connect.run("CALL db.constraints()"):connect.run("DROP CONSTRAINT " + constraint[0])connect.run("CREATE CONSTRAINT SourceIP ON (s:SOURCE) ASSERT s.ip IS UNIQUE")connect.run("CREATE CONSTRAINT DestnIP ON (d:DESTINATION) ASSERT d.ip IS UNIQUE")def neo4jGraph(file, server, user):nodes = {}nd = 0graphic = initConnect(server,user)initWorks(graphic)with open(file, mode='r') as infile:next(infile)for rows in infile:srcIP,srcPort,srcUser,dstIP,dstPort,protocol,eventSubt = rows.split(",")srcIP = replaceCaracter(srcIP)srcPort = replaceCaracter(srcPort)srcUser = replaceCaracter(srcUser)dstIP = replaceCaracter(dstIP)dstPort = replaceCaracter(dstPort)protocol = replaceCaracter(protocol)eventSubt = replaceCaracter(eventSubt)# NODEAsearchingA = searchValue(nodes,srcIP)if searchingA:nodeA = mergeNode(graphic,srcIP,"SOURCE")nodes[nd] = str(srcIP)nd += 1else:nodeA = mergeNode(graphic,srcIP,"SOURCE")# NODEBsearchingB = searchValue(nodes,dstIP)if searchingB:nodeB = mergeNode(graphic,dstIP,"DESTINATION")nodes[nd] = str(dstIP)nd += 1else:nodeB = mergeNode(graphic,dstIP,"DESTINATION")# RELATIONif str(srcIP) == "::" or str(srcIP) == "127.0.0.1":nodeA = nodeBif str(dstIP) == "::" or str(dstIP) == "127.0.0.1":nodeB = nodeAcreateRelations(nodeA,getLabel(dstPort,eventSubt),nodeB,srcPort,protocol,dstPort,eventSubt,graphic)def main():parser = parser = argparse.ArgumentParser(description='Programa que genera los archivos para su importación en la plataforma neo4j.')parser.add_argument('-f', '--file', help='Nombre del archivo generado en la plataforma SIEM, con el contenido analizar. Formato [CSV]: srcIP,srcPort,dstIP,dstPort,Protocol', required=True)parser.add_argument('-s', '--server', help='Conexion a la base de datos, provista por la plataforma en cuestion. FORMATO: bolt://localhost:7687 ', required=True)parser.add_argument('-u', '--user', help='Nombre de usuario utilizado para la conexíon hacia la base de datos.', required=True)args = parser.parse_args()if args.file:neo4jGraph(args.file, args.server, args.user)if __name__ == '__main__':sys.tracebacklimit=0main()
Saludos!
¿Quieres aprender estas técnicas en profundidad?
Continúa tu ruta con Penetration Testing Student.

Escrito por
Javier CandiaAnalista Ciberseguridad
Ingeniero en Redes, caminando por los caminos de la ciberseguridad.



