feat: rewrite sql + alert sent to sql db from idps

This commit is contained in:
Oxbian 2024-11-18 21:49:08 -05:00
parent 2d25387fde
commit 48e9554d38
Signed by: Oxbian
GPG Key ID: 0E8F319FD43747E5
10 changed files with 110 additions and 87 deletions

View File

@ -24,7 +24,7 @@ La base de données SQL est structurée de la façon suivante:
## Interface de tests d'alertes ## Interface de tests d'alertes
Un script python `tests/cef-generator.py` permet de générer des alertes CEF dans la base de données SQL. Un script python `sql/cef-generator.py` permet de générer des alertes CEF dans la base de données SQL.
Ce script peut être utile pour le développement d'interface d'affichage des alertes. Pour l'utiliser il faut une base de donnée sql, et mettre les identifiants dans le script. Ce script peut être utile pour le développement d'interface d'affichage des alertes. Pour l'utiliser il faut une base de donnée sql, et mettre les identifiants dans le script.
De plus, ce script à besoin de la librairie `sql` pour pouvoir ajouter / faire des requêtes à la base de données MySQL. De plus, ce script à besoin de la librairie `sql` pour pouvoir ajouter / faire des requêtes à la base de données MySQL.
@ -39,7 +39,7 @@ pip install -r requirements.txt
Puis executer le script `tests/cef-generator.py` avec le python3 du l'environnement virtuel. Puis executer le script `tests/cef-generator.py` avec le python3 du l'environnement virtuel.
```bash ```bash
.venv/bin/python3 tests/cef-generator.py .venv/bin/python3 sql/cef-generator.py
``` ```
## Demo ## Demo

View File

@ -6,6 +6,10 @@
"db_user": "sidps", "db_user": "sidps",
"db_password": "SUPERPASSWORD", "db_password": "SUPERPASSWORD",
"db_port": "3306", "db_port": "3306",
"cef_version": 1,
"device_product": "SIDPS",
"device_vendor": "ArKa",
"device_version": "vAlpha",
"synscan_time": 180, "synscan_time": 180,
"synscan_count": 5, "synscan_count": 5,
"tcpconnectscan_time": 180, "tcpconnectscan_time": 180,

View File

@ -9,44 +9,61 @@ class Database:
self.conn = mysql.connector.connect(host=config["db_host"], database=config["db_database"], user=config["db_user"], password=config["db_password"], port=config["db_port"]) self.conn = mysql.connector.connect(host=config["db_host"], database=config["db_database"], user=config["db_user"], password=config["db_password"], port=config["db_port"])
self.config = config self.config = config
def send_alert(self, alert): def send_alert(self, date_alert = None, agent_severity = None, device_event_class_id = None,
name = None, src = None, dst = None, dpt = None, spt = None, msg = None,
proto = None, bytesin = None, bytesout = None, reason = None, act = None):
"""Ajoute une alerte dans la base de données """Ajoute une alerte dans la base de données
@param alert: Alerte à rajouter dans la BDD""" @param date_alert: Timestamp de l'alerte
@param agent_severity: Criticité de l'alerte (0 - 10)
@param device_event_class_id: Identifiant de signature, pour le moteur de corrélation
@param name: Nom descriptif de l'alerte
@param src: Adresse IP source
@param dst: Adresse IP destination
@param dpt: Port de destination
@param spt: Port source
@param msg: Champ de texte pour des notes ou commentaires additionnels
@param proto: Protocol couche 4 (réseau) utilisé
@param bytesin: Quantité de bytes (8 bits ici) entrants
@param bytesout: Quantité de bytes (8 bits ici) sortants
@param reason: Description de la raison de l'alerte
@param act: Action prise en réponse de l'alerte
"""
try: try:
cursor = self.conn.cursor() cursor = self.conn.cursor()
sql_query = """ sql_query = """
INSERT INTO alertes ( INSERT INTO alertes (
cef_version, date_alerte, event_gravite, device_product, cef_version, date_alerte, agent_severity, device_event_class_id,
device_vendor, device_version, alerte_name, sourceAddress, device_product, device_vendor, device_version, name, dst, src,
destinationAddress, destinationPort, sourcePort, protocol, dpt, spt, msg, proto, bytesin, bytesout, reason, act
applicationProtocol, reason, action, commentaire
) VALUES ( ) VALUES (
%(cef_version)s, %(date_alerte)s, %(event_gravite)s, %(device_product)s, %(cef_version)s, %(date_alerte)s, %(agent_severity)s, %(device_event_class_id)s,
%(device_vendor)s, %(device_version)s, %(alerte_name)s, %(src)s, %(device_product)s, %(device_vendor)s, %(device_version)s, %(name)s, %(dst)s,
%(dst)s, %(destinationPort)s, %(sourcePort)s, %(protocol)s, %(src)s, %(dpt)s, %(spt)s, %(msg)s, %(proto)s, %(bytesin)s, %(bytesout)s,
%(applicationProtocol)s, %(reason)s, %(action)s, %(commentaire)s %(reason)s, %(act)s
); );
""" """
# Paramètres pour la requête SQL # Paramètres pour la requête SQL
params = { params = {
"cef_version": alert["CEF"], "cef_version": self.get_key("cef_version", 1),
"date_alerte": alert["datetime"], "date_alerte": date_alert,
"event_gravite": alert["agent_severity"], "agent_severity": agent_severity,
"device_product": alert["Device Product"], "device_event_class_id": device_event_class_id,
"device_vendor": alert["Device Vendor"], "device_product": self.get_key("device_product", "SIDPS"),
"device_version": alert["Device Version"], "device_vendor": self.get_key("device_vendor", "ArKa"),
"alerte_name": alert["name"], "device_version": self.get_key("device_version", "vAlpha"),
"src": alert["src"], "name": name,
"dst": alert["dst"], "src": src,
"destinationPort": alert["dstPort"], "dst": dst,
"sourcePort": alert["srcPort"], "dpt": dpt,
"protocol": alert["protocol"], "spt": spt,
"applicationProtocol": alert["applicationProtocol"], "msg": msg,
"reason": alert["reason"], "proto": proto,
"action": alert["action"], "bytesin": bytesin,
"commentaire": alert["commentaire"] "bytesout": bytesout,
"reason": reason,
"act": act
} }
# Exécution de la requête d'insertion # Exécution de la requête d'insertion
@ -61,4 +78,4 @@ class Database:
@param key: clé du paramètre souhaité @param key: clé du paramètre souhaité
@param default_val: valeur par défaut si la clé n'existe pas""" @param default_val: valeur par défaut si la clé n'existe pas"""
return self.config.get(key, None) return self.config.get(key, default_val)

View File

@ -1,3 +1,5 @@
from datetime import datetime
def rule(packet, tcp_packets, db): def rule(packet, tcp_packets, db):
"""Règle SYNScan: """Règle SYNScan:
Un SYNScan va envoyer des requêtes TCP avec le flag SYN Un SYNScan va envoyer des requêtes TCP avec le flag SYN
@ -9,4 +11,5 @@ def rule(packet, tcp_packets, db):
seuil = db.get_key("synscan_count", 5) seuil = db.get_key("synscan_count", 5)
if (tcp_packets.count_packet_of_type("RA", time_window) + tcp_packets.count_packet_of_type("SA", time_window)) + tcp_packets.count_packet_of_type("R", time_window) >= seuil: if (tcp_packets.count_packet_of_type("RA", time_window) + tcp_packets.count_packet_of_type("SA", time_window)) + tcp_packets.count_packet_of_type("R", time_window) >= seuil:
db.send_alert(datetime.now(), 5, None, "Syn scan", packet['IP'].src, packet['IP'].dst, proto="TCP", reason="Détection de nombreux patterns de Syn->SynACK->Reset ACK et Syn->Reset ACK", act="Alerte")
print(f"Alerte, seuil dépassés, risque de SynScan") print(f"Alerte, seuil dépassés, risque de SynScan")

View File

@ -1,3 +1,5 @@
from datetime import datetime
def rule(packet, tcp_packets, db): def rule(packet, tcp_packets, db):
"""Règle TCPConnect Scan: """Règle TCPConnect Scan:
Un scan TCP connect va effectuer une connexion TCP en entier sur chaque port scanné. Un scan TCP connect va effectuer une connexion TCP en entier sur chaque port scanné.
@ -8,4 +10,5 @@ def rule(packet, tcp_packets, db):
seuil = db.get_key("tcpconnectscan_count", 5) seuil = db.get_key("tcpconnectscan_count", 5)
if (tcp_packets.count_packet_of_type("A", time_window) + tcp_packets.count_packet_of_type("RA", time_window)) >= seuil: if (tcp_packets.count_packet_of_type("A", time_window) + tcp_packets.count_packet_of_type("RA", time_window)) >= seuil:
print(f"Alerte, seuils dépassés, risque de TCPConnectScan") db.send_alert(datetime.now(), 5, None, "TCPConnect Scan", packet['IP'].src, packet['IP'].dst, proto="TCP", reason="Détection de nombreux patterns de Syn->SynACK->ACK->Reset->ACK et Syn->Reset ACK", act="Alerte")
print(f"Alerte, seuils dépassés, risque de TCPConnectScan")

View File

@ -12,6 +12,8 @@ class TCP:
def add_packet(self, ip_src, port_src, ip_dst, port_dst, flags, timestamp): def add_packet(self, ip_src, port_src, ip_dst, port_dst, flags, timestamp):
"""Ajoute le suivi d'un paquet dans le dictionnaire""" """Ajoute le suivi d'un paquet dans le dictionnaire"""
timestamp = int(timestamp)
# Initialisation de la liste de paquets pour l'IP source # Initialisation de la liste de paquets pour l'IP source
if ip_src not in self.packets: if ip_src not in self.packets:
self.packets[ip_src] = [] self.packets[ip_src] = []
@ -82,7 +84,7 @@ class TCP:
return None return None
for i, [p_s, ip_d, p_d, f, stamp] in enumerate(self.packets[ip_src]): for i, [p_s, ip_d, p_d, f, stamp] in enumerate(self.packets[ip_src]):
if p_s == port_src and ip_d == ip_dst and p_d == port_dst and f in flags: if p_s == port_src and ip_d == ip_dst and p_d == port_dst and flags in f:
return i return i
return None return None

View File

@ -1,5 +0,0 @@
CREATE DATABASE sidps DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE USER 'sidps'@'localhost' IDENTIFIED BY 'SUPERPASSWORD';
GRANT ALL PRIVILEGES ON sidps.* TO 'sidps'@'localhost';
FLUSH PRIVILEGES;
use sidps;

View File

@ -9,7 +9,6 @@ import time
import random import random
from datetime import datetime from datetime import datetime
def generate_alert(alert_type): def generate_alert(alert_type):
# Dictionnaire pour différents types d'alertes réseau et fichiers # Dictionnaire pour différents types d'alertes réseau et fichiers
alert_templates = { alert_templates = {
@ -71,7 +70,7 @@ def generate_filename():
def generate_alerts(conn, cursor, main_headers): def generate_alerts(conn, cursor, main_headers):
# Récupérer ces données depuis une fonction # Récupérer ces données depuis une fonction
alertes = ["Syn Flood", "Port Scanning", "Suspicious File Creation", "Critical File Deletion Attempt"] alertes = ["Syn Flood", "Port Scanning"] #, "Suspicious File Creation", "Critical File Deletion Attempt"]
while True: while True:
data = generate_alert(random.choice(alertes)) data = generate_alert(random.choice(alertes))
@ -81,15 +80,14 @@ def generate_alerts(conn, cursor, main_headers):
# Préparer la requête SQL d'insertion # Préparer la requête SQL d'insertion
sql_query = """ sql_query = """
INSERT INTO alertes ( INSERT INTO alertes (
cef_version, date_alerte, event_gravite, device_product, cef_version, date_alerte, agent_severity, device_event_class_id,
device_vendor, device_version, alerte_name, sourceAddress, device_product, device_vendor, device_version, name, dst, src,
destinationAddress, destinationPort, sourcePort, protocol, dpt, spt, msg, proto, bytesin, bytesout, reason, act
applicationProtocol, reason, action, commentaire
) VALUES ( ) VALUES (
%(cef_version)s, %(date_alerte)s, %(event_gravite)s, %(device_product)s, %(cef_version)s, %(date_alerte)s, %(agent_severity)s, %(device_event_class_id)s,
%(device_vendor)s, %(device_version)s, %(alerte_name)s, %(src)s, %(device_product)s, %(device_vendor)s, %(device_version)s, %(name)s, %(dst)s,
%(dst)s, %(destinationPort)s, %(sourcePort)s, %(protocol)s, %(src)s, %(dpt)s, %(spt)s, %(msg)s, %(proto)s, %(bytesin)s, %(bytesout)s,
%(applicationProtocol)s, %(reason)s, %(action)s, %(commentaire)s %(reason)s, %(act)s
); );
""" """
@ -97,20 +95,22 @@ def generate_alerts(conn, cursor, main_headers):
params = { params = {
"cef_version": merged["CEF"], "cef_version": merged["CEF"],
"date_alerte": datetime.now(), "date_alerte": datetime.now(),
"event_gravite": int(merged["agent_severity"]), "agent_severity": int(merged["agent_severity"]),
"device_event_class_id": None,
"device_product": merged["Device Product"], "device_product": merged["Device Product"],
"device_vendor": merged["Device Vendor"], "device_vendor": merged["Device Vendor"],
"device_version": merged["Device Version"], "device_version": merged["Device Version"],
"alerte_name": merged["name"], "name": merged["name"],
"src": merged["src"], "src": merged["src"],
"dst": merged["dst"], "dst": merged["dst"],
"destinationPort": None, # A définir si disponible "dpt": None,
"sourcePort": None, # A définir si disponible "spt": None,
"protocol": "TCP", # Par défaut, à adapter si besoin "msg": "Message",
"applicationProtocol": "N/A", # À ajuster en fonction des besoins "proto": "TCP",
"reason": "Suspicious activity detected", # Exemple, à adapter "bytesin": None,
"action": "Alerted", # Exemple d'action "bytesout": None,
"commentaire": "" # Optionnel "reason": "Activité suspecte",
"act": "Alert"
} }
# Exécution de la requête d'insertion # Exécution de la requête d'insertion
@ -132,17 +132,11 @@ def main():
cursor = conn.cursor() cursor = conn.cursor()
# En-têtes généraux
CEF_version = 1
Device_vendor = "ArKa"
Device_product = "SIDPS"
Device_version = "vAlpha"
main_headers = { main_headers = {
"CEF": CEF_version, "CEF": 1,
"Device Vendor": Device_vendor, "Device Vendor": "ArKa",
"Device Product": Device_product, "Device Product": "SIDPS",
"Device Version": Device_version "Device Version": "vAlpha"
} }
# Lancer la génération d'alertes # Lancer la génération d'alertes

View File

@ -12,18 +12,21 @@ CREATE TABLE alertes (
id SERIAL PRIMARY KEY, -- Identifiant unique pour chaque alerte id SERIAL PRIMARY KEY, -- Identifiant unique pour chaque alerte
cef_version VARCHAR(10) DEFAULT 'CEF:1', -- Version du format CEF utilisé cef_version VARCHAR(10) DEFAULT 'CEF:1', -- Version du format CEF utilisé
date_alerte TIMESTAMP(3) NOT NULL, -- Date et heure de l'alerte avec une précision de millisecondes date_alerte TIMESTAMP(3) NOT NULL, -- Date et heure de l'alerte avec une précision de millisecondes
event_gravite INT CHECK (event_gravite >= 0 AND event_gravite <= 10), -- Niveau de gravité de l'alerte sur une échelle de 0 à 10 agent_severity INT CHECK (agent_severity >= 0 AND agent_severity <= 10), -- Niveau de gravité de l'alerte sur une échelle de 0 à 10
device_event_class_id VARCHAR(1023), -- Identifiant de la signature permettant d'aider les moteurs de corrélations
device_product VARCHAR(63), -- Nom du produit à l'origine de l'alerte device_product VARCHAR(63), -- Nom du produit à l'origine de l'alerte
device_vendor VARCHAR(63), -- Nom du fournisseur ou fabricant du produit device_vendor VARCHAR(63), -- Nom du fournisseur ou fabricant du produit
device_version VARCHAR(31), -- Version du produit ou dispositif ayant généré l'alerte device_version VARCHAR(31), -- Version du produit ou dispositif ayant généré l'alerte
alerte_name VARCHAR(512), -- Nom descriptif de l'alerte name VARCHAR(512), -- Nom descriptif de l'alerte
destinationAddress VARCHAR(45), -- Adresse IP de destination impliquée dans l'alerte -- Champ d'extension du CEF
sourceAddress VARCHAR(45), -- Adresse IP source impliquée dans l'alerte dst VARCHAR(45), -- Adresse IP de destination impliquée dans l'alerte
destinationPort INT, -- Port de destination utilisé pour l'événement ou l'alerte src VARCHAR(45), -- Adresse IP source impliquée dans l'alerte
sourcePort INT, -- Port source de l'événement ou de l'alerte dpt INT, -- Port de destination utilisé pour l'événement ou l'alerte
protocol VARCHAR(10), -- Protocole réseau impliqué (ex : TCP, UDP) spt INT, -- Port source de l'événement ou de l'alerte
applicationProtocol VARCHAR(20), -- Protocole applicatif impliqué (ex : HTTP, FTP) msg VARCHAR(1023), -- Champ texte pour des notes ou commentaires additionnels concernant l'alerte
reason TEXT, -- Description de la raison de l'alerte expliquant pourquoi elle a été générée proto VARCHAR(10), -- Protocole réseau impliqué (ex : TCP, UDP)
action VARCHAR(50), -- Action entreprise en réponse à l'alerte (ex : bloqué, alerté uniquement, ...) bytesin INT, -- Quantité de bytes (8 bits ici) entrant (cas de flood ou DOS)
commentaire TEXT -- Champ texte pour des notes ou commentaires additionnels concernant l'alerte bytesout INT, -- Quantité des bytes (8 bits ici) sortants
reason VARCHAR(1023), -- Description de la raison de l'alerte expliquant pourquoi elle a été générée
act VARCHAR(50) -- Action entreprise en réponse à l'alerte (ex : bloqué, alerté uniquement, ...)
); );

View File

@ -18,18 +18,20 @@ CREATE TABLE alertes (
id SERIAL PRIMARY KEY, -- Identifiant unique pour chaque alerte id SERIAL PRIMARY KEY, -- Identifiant unique pour chaque alerte
cef_version VARCHAR(10) DEFAULT 'CEF:1', -- Version du format CEF utilisé cef_version VARCHAR(10) DEFAULT 'CEF:1', -- Version du format CEF utilisé
date_alerte TIMESTAMP(3) NOT NULL, -- Date et heure de l'alerte avec une précision de millisecondes date_alerte TIMESTAMP(3) NOT NULL, -- Date et heure de l'alerte avec une précision de millisecondes
event_gravite INT CHECK (event_gravite >= 0 AND event_gravite <= 10), -- Niveau de gravité de l'alerte sur une échelle de 0 à 10 agent_severity INT CHECK (agent_severity >= 0 AND agent_severity <= 10), -- Niveau de gravité de l'alerte sur une échelle de 0 à 10
device_event_class_id VARCHAR(1023), -- Identifiant de la signature permettant d'aider les moteurs de corrélations
device_product VARCHAR(63), -- Nom du produit à l'origine de l'alerte device_product VARCHAR(63), -- Nom du produit à l'origine de l'alerte
device_vendor VARCHAR(63), -- Nom du fournisseur ou fabricant du produit device_vendor VARCHAR(63), -- Nom du fournisseur ou fabricant du produit
device_version VARCHAR(31), -- Version du produit ou dispositif ayant généré l'alerte device_version VARCHAR(31), -- Version du produit ou dispositif ayant généré l'alerte
alerte_name VARCHAR(512), -- Nom descriptif de l'alerte name VARCHAR(512), -- Nom descriptif de l'alerte
destinationAddress VARCHAR(45), -- Adresse IP de destination impliquée dans l'alerte dst VARCHAR(45), -- Adresse IP de destination impliquée dans l'alerte
sourceAddress VARCHAR(45), -- Adresse IP source impliquée dans l'alerte src VARCHAR(45), -- Adresse IP source impliquée dans l'alerte
destinationPort INT, -- Port de destination utilisé pour l'événement ou l'alerte dpt INT, -- Port de destination utilisé pour l'événement ou l'alerte
sourcePort INT, -- Port source de l'événement ou de l'alerte spt INT, -- Port source de l'événement ou de l'alerte
protocol VARCHAR(10), -- Protocole réseau impliqué (ex : TCP, UDP) msg VARCHAR(1023), -- Champ texte pour des notes ou commentaires additionnels concernant l'alerte
applicationProtocol VARCHAR(20), -- Protocole applicatif impliqué (ex : HTTP, FTP) proto VARCHAR(10), -- Protocole réseau impliqué (ex : TCP, UDP)
reason TEXT, -- Description de la raison de l'alerte expliquant pourquoi elle a été générée bytesin INT, -- Quantité de bits entrant (cas de flood ou DOS)
action VARCHAR(50), -- Action entreprise en réponse à l'alerte (ex : bloqué, alerté uniquement, ...) bytesout INT, -- Quantité des bits sortants
commentaire TEXT -- Champ texte pour des notes ou commentaires additionnels concernant l'alerte reason VARCHAR(1023), -- Description de la raison de l'alerte expliquant pourquoi elle a été générée
act VARCHAR(50) -- Action entreprise en réponse à l'alerte (ex : bloqué, alerté uniquement, ...)
); );