Compare commits
No commits in common. "5aa4d720c787572486ab48afd6014495598c1380" and "7cf5253c708cf1500e819d052837ed094388a4cc" have entirely different histories.
5aa4d720c7
...
7cf5253c70
4 changed files with 27 additions and 138 deletions
|
@ -61,8 +61,6 @@ IOC_TIPOS_OMITIR = [
|
||||||
|
|
||||||
WORKERS_THR = 4
|
WORKERS_THR = 4
|
||||||
|
|
||||||
# Organizaciones que no se guardaran en BD (No se refleja conteo de IoC)
|
|
||||||
ORG_OMITIR = []
|
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -73,8 +71,6 @@ config.py se divide en :
|
||||||
- KTIP_CONFIG: Configuración de conexión a servicio de Lookup de Kaspersky. Se debe solo configurar "api_key".
|
- KTIP_CONFIG: Configuración de conexión a servicio de Lookup de Kaspersky. Se debe solo configurar "api_key".
|
||||||
- IOC_TIPOS_OMITIR: Tipos de Atributos que se omiten en el conteo, generalmente metadata.
|
- IOC_TIPOS_OMITIR: Tipos de Atributos que se omiten en el conteo, generalmente metadata.
|
||||||
- WORKERS_THR: Cantidad de hilos que utiliza la aplicación en ejecución. Por defecto utiliza 4. Este parametro depende de la capacidad de CPU en relación a la cantidad de núcleos (4 Cores => 4 Hilos)
|
- WORKERS_THR: Cantidad de hilos que utiliza la aplicación en ejecución. Por defecto utiliza 4. Este parametro depende de la capacidad de CPU en relación a la cantidad de núcleos (4 Cores => 4 Hilos)
|
||||||
- ORG_OMITIR: Organizaciones que no se contemplaran para mostrar conteo de IOC (datos). De igual forma se realiza limpieza de IoC asociados a esas organizaciones
|
|
||||||
|
|
||||||
|
|
||||||
En el archivo run_daily.py se puede configurar por ejemplo, el maximo de ioc a comtemplar por evento en la variable "max_ioc":
|
En el archivo run_daily.py se puede configurar por ejemplo, el maximo de ioc a comtemplar por evento en la variable "max_ioc":
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,3 @@ IOC_TIPOS_OMITIR = [
|
||||||
|
|
||||||
WORKERS_THR = 4
|
WORKERS_THR = 4
|
||||||
|
|
||||||
# Organizaciones que no se guardaran en BD (No se refleja conteo de IoC)
|
|
||||||
ORG_OMITIR = []
|
|
||||||
|
|
||||||
|
|
120
defs.py
120
defs.py
|
@ -8,14 +8,13 @@ import ipaddress
|
||||||
import json
|
import json
|
||||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
from sqlalchemy.orm import sessionmaker
|
||||||
from pymisp import PyMISP
|
from pymisp import PyMISP
|
||||||
from pymisp.exceptions import PyMISPError
|
from pymisp.exceptions import PyMISPError
|
||||||
import config
|
import config
|
||||||
import urllib3
|
import urllib3
|
||||||
import requests
|
import requests
|
||||||
from models import Base, Registro, Usuario, ModificadosEv
|
from models import Base, Registro, Usuario
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
@ -289,10 +288,6 @@ class MISPProcessorTop:
|
||||||
if int(evento['Event']['attribute_count']) > 0:
|
if int(evento['Event']['attribute_count']) > 0:
|
||||||
self.misp.publish(int(e['Event']['id']))
|
self.misp.publish(int(e['Event']['id']))
|
||||||
logging.info("Publicando cambios de evento #" + e['Event']['id'])
|
logging.info("Publicando cambios de evento #" + e['Event']['id'])
|
||||||
|
|
||||||
# Se guarda uuid de evento que fue modificado en BD, manteniendo el timestamp de publish
|
|
||||||
self.guardar_bd_procesados_mod(e['Event']['uuid'], int(e['Event']['publish_timestamp']), datetime.now())
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Se elimina evento con cero atributo...
|
# Se elimina evento con cero atributo...
|
||||||
self.misp.delete_event(int(e['Event']['id']))
|
self.misp.delete_event(int(e['Event']['id']))
|
||||||
|
@ -301,20 +296,6 @@ class MISPProcessorTop:
|
||||||
logging.error(str(err))
|
logging.error(str(err))
|
||||||
return resultados
|
return resultados
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def convert_value(value):
|
|
||||||
try:
|
|
||||||
# Intentar convertir a fecha y retornar un número representativo
|
|
||||||
return int(datetime.strptime(value, "%Y%m%d").strftime("%Y%m%d"))
|
|
||||||
except ValueError:
|
|
||||||
# Si falla, convertir a entero directamente
|
|
||||||
return int(value)
|
|
||||||
|
|
||||||
def find_max(self, data):
|
|
||||||
# Llamar al método estático directamente
|
|
||||||
max_value = max(data, key=self.convert_value)
|
|
||||||
return max_value
|
|
||||||
|
|
||||||
def calcula_calidad_iocs(self, desde: str, hasta: str, a_por_evento=None):
|
def calcula_calidad_iocs(self, desde: str, hasta: str, a_por_evento=None):
|
||||||
try:
|
try:
|
||||||
# Para salida
|
# Para salida
|
||||||
|
@ -325,6 +306,10 @@ class MISPProcessorTop:
|
||||||
|
|
||||||
puntos = {k: 0 for k in self.creators_accounts.keys()}
|
puntos = {k: 0 for k in self.creators_accounts.keys()}
|
||||||
|
|
||||||
|
# Para filtrar versión de Warninglist donde buscar, se toma desde fecha "desde"
|
||||||
|
actual = datetime.now().year
|
||||||
|
version = str(actual)
|
||||||
|
|
||||||
# Fechas para sacar la mayor...
|
# Fechas para sacar la mayor...
|
||||||
fechas = []
|
fechas = []
|
||||||
|
|
||||||
|
@ -343,11 +328,12 @@ class MISPProcessorTop:
|
||||||
wl = self.misp.warninglists()
|
wl = self.misp.warninglists()
|
||||||
|
|
||||||
for l in wl:
|
for l in wl:
|
||||||
fechas.append(str(l['Warninglist']['version']))
|
if str(l['Warninglist']['version']).startswith(str(actual)):
|
||||||
|
fechas.append(l['Warninglist']['version'])
|
||||||
|
|
||||||
|
if fechas:
|
||||||
# Saca la versión más alta...
|
# Saca la versión más alta...
|
||||||
#version = max(fechas, key=self.convert_value)
|
version = max(fechas)
|
||||||
version = self.find_max(fechas)
|
|
||||||
|
|
||||||
for l in wl:
|
for l in wl:
|
||||||
if str(l['Warninglist']['version']) == version:
|
if str(l['Warninglist']['version']) == version:
|
||||||
|
@ -356,11 +342,11 @@ class MISPProcessorTop:
|
||||||
lista.append(self.misp.get_warninglist(int(l['Warninglist']['id'])))
|
lista.append(self.misp.get_warninglist(int(l['Warninglist']['id'])))
|
||||||
|
|
||||||
if lista:
|
if lista:
|
||||||
|
|
||||||
# Promedio por defecto
|
# Promedio por defecto
|
||||||
prom = 0
|
prom = 0
|
||||||
|
|
||||||
logging.info("Warninglist de MISP Cargadas : " + str(len(lista)))
|
logging.info("Warninglist de MISP Cargadas : " + str(len(lista)))
|
||||||
logging.info("Versión de Warninglists a utilizar: " + str(version))
|
|
||||||
|
|
||||||
# Rango completo de fechas....
|
# Rango completo de fechas....
|
||||||
logging.info("Buscando IoC Desde :" + desde + " Hasta :" + hasta)
|
logging.info("Buscando IoC Desde :" + desde + " Hasta :" + hasta)
|
||||||
|
@ -378,22 +364,10 @@ class MISPProcessorTop:
|
||||||
# Se seleccionan eventos para establecer limite de fechas
|
# Se seleccionan eventos para establecer limite de fechas
|
||||||
for e in eventos_tmp:
|
for e in eventos_tmp:
|
||||||
if datetime.fromtimestamp(int(e['publish_timestamp'])).date() <= datetime.strptime(hasta, '%Y-%m-%d').date():
|
if datetime.fromtimestamp(int(e['publish_timestamp'])).date() <= datetime.strptime(hasta, '%Y-%m-%d').date():
|
||||||
try:
|
|
||||||
# se verifica que evento no haya sido procesado en periodo anterior
|
|
||||||
prev_evento = self.obtener_evento_mod_db(e['uuid'])
|
|
||||||
|
|
||||||
# Si no existe en modificados....
|
|
||||||
if not prev_evento:
|
|
||||||
# Event get
|
# Event get
|
||||||
ev = self.misp.get_event(int(e['id']))
|
ev = self.misp.get_event(int(e['id']))
|
||||||
eventos.append(ev)
|
eventos.append(ev)
|
||||||
elif datetime.fromtimestamp(int(e['publish_timestamp'])) > prev_evento['pub_mod_fecha'] + timedelta(hours=1):
|
|
||||||
# Verificar fecha con desfase, si es mayor se agrega para procesar
|
|
||||||
# Event get
|
|
||||||
ev = self.misp.get_event(int(e['id']))
|
|
||||||
eventos.append(ev)
|
|
||||||
except Exception as err_c:
|
|
||||||
logging.error("Error al tratar de agregar evento +"+e['id']+" :"+str(err_c))
|
|
||||||
|
|
||||||
# Atributos por evento es None, se calcula promedio...
|
# Atributos por evento es None, se calcula promedio...
|
||||||
if a_por_evento is None:
|
if a_por_evento is None:
|
||||||
|
@ -454,80 +428,6 @@ class MISPProcessorTop:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.info("Error al escribir JSON :" + str(e))
|
logging.info("Error al escribir JSON :" + str(e))
|
||||||
|
|
||||||
|
|
||||||
def obtener_evento_mod_db(self, e_uuid: str):
|
|
||||||
# Output inicial vacío
|
|
||||||
output = {}
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Ruta de la base de datos
|
|
||||||
ruta_base_datos = os.path.join(self.dir_data, "registros.db")
|
|
||||||
engine = create_engine(f'sqlite:///{ruta_base_datos}')
|
|
||||||
Base.metadata.create_all(engine)
|
|
||||||
Session = sessionmaker(bind=engine)
|
|
||||||
session = Session()
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Consultar el último registro con el evento_uuid específico
|
|
||||||
ultimo_procesado = (
|
|
||||||
session.query(ModificadosEv)
|
|
||||||
.filter_by(evento_uuid=e_uuid)
|
|
||||||
.order_by(ModificadosEv.pub_mod_fecha.desc()) # Ordenar por fecha descendente
|
|
||||||
.first() # Obtener solo el primer registro
|
|
||||||
)
|
|
||||||
|
|
||||||
# Si se encuentra un registro, convertirlo a dict
|
|
||||||
if ultimo_procesado:
|
|
||||||
output = ultimo_procesado.to_dict()
|
|
||||||
|
|
||||||
except SQLAlchemyError as db_error:
|
|
||||||
logging.error(f"Error en la consulta a la base de datos: {db_error}")
|
|
||||||
session.rollback()
|
|
||||||
finally:
|
|
||||||
session.close()
|
|
||||||
|
|
||||||
except Exception as err:
|
|
||||||
logging.error(f"Error general al conectarse a la base de datos: {err}")
|
|
||||||
|
|
||||||
# Retornar el resultado
|
|
||||||
return output
|
|
||||||
|
|
||||||
|
|
||||||
def guardar_bd_procesados_mod(self, e_uuid: str, timestamp_fecha: int, mod_fecha: datetime):
|
|
||||||
try:
|
|
||||||
# Ruta de la base de datos
|
|
||||||
ruta_base_datos = os.path.join(self.dir_data, "registros.db")
|
|
||||||
engine = create_engine(f'sqlite:///{ruta_base_datos}')
|
|
||||||
Base.metadata.create_all(engine)
|
|
||||||
Session = sessionmaker(bind=engine)
|
|
||||||
session = Session()
|
|
||||||
|
|
||||||
# Crear el nuevo registro
|
|
||||||
nuevo_registro = ModificadosEv(
|
|
||||||
evento_uuid=e_uuid,
|
|
||||||
publicado_fecha=datetime.fromtimestamp(timestamp_fecha),
|
|
||||||
pub_mod_fecha=mod_fecha
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
session.add(nuevo_registro)
|
|
||||||
session.commit()
|
|
||||||
logging.info(f"Registrado UUID de evento modificado: {e_uuid}")
|
|
||||||
except IntegrityError:
|
|
||||||
# Manejo específico para violación de restricciones
|
|
||||||
logging.warning(f"Registro duplicado: {e_uuid} ya existe con la misma fecha.")
|
|
||||||
session.rollback()
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Error al guardar los datos en la base de datos: {e}")
|
|
||||||
session.rollback()
|
|
||||||
finally:
|
|
||||||
session.close()
|
|
||||||
|
|
||||||
except Exception as err:
|
|
||||||
logging.error(f"Error general al guardar en la base de datos: {err}")
|
|
||||||
|
|
||||||
|
|
||||||
def guardar_bd(self, data: list, fecha: str):
|
def guardar_bd(self, data: list, fecha: str):
|
||||||
try:
|
try:
|
||||||
ruta_base_datos = os.path.join(self.dir_data, "registros.db")
|
ruta_base_datos = os.path.join(self.dir_data, "registros.db")
|
||||||
|
|
20
run_daily.py
20
run_daily.py
|
@ -1,6 +1,5 @@
|
||||||
from defs import MISPProcessorTop
|
from defs import MISPProcessorTop
|
||||||
import calendar
|
import calendar
|
||||||
from config import ORG_OMITIR
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,19 +25,16 @@ obj = MISPProcessorTop()
|
||||||
|
|
||||||
# Se llama a calcular fecha
|
# Se llama a calcular fecha
|
||||||
#iocs = obj.calcula_calidad_iocs(desde_fecha, hasta_fecha, 1000)
|
#iocs = obj.calcula_calidad_iocs(desde_fecha, hasta_fecha, 1000)
|
||||||
try:
|
|
||||||
iocs = obj.calcula_calidad_iocs(fecha_anterior, fecha_anterior, max_ioc)
|
|
||||||
print(iocs)
|
|
||||||
|
|
||||||
if iocs:
|
iocs = obj.calcula_calidad_iocs(fecha_anterior, fecha_anterior, max_ioc)
|
||||||
|
|
||||||
|
if iocs:
|
||||||
# Solo para efectos de backup fisico por dia...
|
# Solo para efectos de backup fisico por dia...
|
||||||
#obj.guarda_ioc_json(iocs, fecha_anterior.strftime('%Y%m%d')"_"+".json")
|
#obj.guarda_ioc_json(iocs, fecha_anterior.strftime('%Y%m%d')"_"+".json")
|
||||||
output = []
|
|
||||||
for x in iocs:
|
# Se imprime estructura
|
||||||
if x['comunidad'] not in ORG_OMITIR:
|
print(iocs)
|
||||||
output.append(x)
|
|
||||||
|
|
||||||
# Guarda en BD (SQLite)
|
# Guarda en BD (SQLite)
|
||||||
obj.guardar_bd(output,fecha_anterior)
|
obj.guardar_bd(iocs,fecha_anterior)
|
||||||
except Exception as e:
|
|
||||||
print(str(e))
|
|
Loading…
Add table
Reference in a new issue