Compare commits

..

No commits in common. "5aa4d720c787572486ab48afd6014495598c1380" and "7cf5253c708cf1500e819d052837ed094388a4cc" have entirely different histories.

4 changed files with 27 additions and 138 deletions

View file

@ -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":

View file

@ -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
View file

@ -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")

View file

@ -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))