diff --git a/README.md b/README.md index 0706ab2..d8bd344 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ La base de données SQL est structurée de la façon suivante: ## 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. 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. ```bash -.venv/bin/python3 tests/cef-generator.py +.venv/bin/python3 sql/cef-generator.py ``` ## Demo diff --git a/config.json b/config.json index 10aac78..5f69321 100644 --- a/config.json +++ b/config.json @@ -6,6 +6,10 @@ "db_user": "sidps", "db_password": "SUPERPASSWORD", "db_port": "3306", + "cef_version": 1, + "device_product": "SIDPS", + "device_vendor": "ArKa", + "device_version": "vAlpha", "synscan_time": 180, "synscan_count": 5, "tcpconnectscan_time": 180, diff --git a/idps/database.py b/idps/database.py index 291c595..bba8130 100644 --- a/idps/database.py +++ b/idps/database.py @@ -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.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 - @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: cursor = self.conn.cursor() sql_query = """ INSERT INTO alertes ( - cef_version, date_alerte, event_gravite, device_product, - device_vendor, device_version, alerte_name, sourceAddress, - destinationAddress, destinationPort, sourcePort, protocol, - applicationProtocol, reason, action, commentaire + cef_version, date_alerte, agent_severity, device_event_class_id, + device_product, device_vendor, device_version, name, dst, src, + dpt, spt, msg, proto, bytesin, bytesout, reason, act ) VALUES ( - %(cef_version)s, %(date_alerte)s, %(event_gravite)s, %(device_product)s, - %(device_vendor)s, %(device_version)s, %(alerte_name)s, %(src)s, - %(dst)s, %(destinationPort)s, %(sourcePort)s, %(protocol)s, - %(applicationProtocol)s, %(reason)s, %(action)s, %(commentaire)s + %(cef_version)s, %(date_alerte)s, %(agent_severity)s, %(device_event_class_id)s, + %(device_product)s, %(device_vendor)s, %(device_version)s, %(name)s, %(dst)s, + %(src)s, %(dpt)s, %(spt)s, %(msg)s, %(proto)s, %(bytesin)s, %(bytesout)s, + %(reason)s, %(act)s ); """ # Paramètres pour la requête SQL params = { - "cef_version": alert["CEF"], - "date_alerte": alert["datetime"], - "event_gravite": alert["agent_severity"], - "device_product": alert["Device Product"], - "device_vendor": alert["Device Vendor"], - "device_version": alert["Device Version"], - "alerte_name": alert["name"], - "src": alert["src"], - "dst": alert["dst"], - "destinationPort": alert["dstPort"], - "sourcePort": alert["srcPort"], - "protocol": alert["protocol"], - "applicationProtocol": alert["applicationProtocol"], - "reason": alert["reason"], - "action": alert["action"], - "commentaire": alert["commentaire"] + "cef_version": self.get_key("cef_version", 1), + "date_alerte": date_alert, + "agent_severity": agent_severity, + "device_event_class_id": device_event_class_id, + "device_product": self.get_key("device_product", "SIDPS"), + "device_vendor": self.get_key("device_vendor", "ArKa"), + "device_version": self.get_key("device_version", "vAlpha"), + "name": name, + "src": src, + "dst": dst, + "dpt": dpt, + "spt": spt, + "msg": msg, + "proto": proto, + "bytesin": bytesin, + "bytesout": bytesout, + "reason": reason, + "act": act } # Exécution de la requête d'insertion @@ -61,4 +78,4 @@ class Database: @param key: clé du paramètre souhaité @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) diff --git a/idps/rules/TCP/Scan/synscan.py b/idps/rules/TCP/Scan/synscan.py index de69fc5..2b0e729 100644 --- a/idps/rules/TCP/Scan/synscan.py +++ b/idps/rules/TCP/Scan/synscan.py @@ -1,3 +1,5 @@ +from datetime import datetime + def rule(packet, tcp_packets, db): """Règle SYNScan: 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) 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") diff --git a/idps/rules/TCP/Scan/tcpconnectscan.py b/idps/rules/TCP/Scan/tcpconnectscan.py index b21bffa..7629158 100644 --- a/idps/rules/TCP/Scan/tcpconnectscan.py +++ b/idps/rules/TCP/Scan/tcpconnectscan.py @@ -1,3 +1,5 @@ +from datetime import datetime + def rule(packet, tcp_packets, db): """Règle TCPConnect Scan: 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) 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") diff --git a/idps/tcp.py b/idps/tcp.py index b113495..0869f13 100644 --- a/idps/tcp.py +++ b/idps/tcp.py @@ -12,6 +12,8 @@ class TCP: def add_packet(self, ip_src, port_src, ip_dst, port_dst, flags, timestamp): """Ajoute le suivi d'un paquet dans le dictionnaire""" + timestamp = int(timestamp) + # Initialisation de la liste de paquets pour l'IP source if ip_src not in self.packets: self.packets[ip_src] = [] @@ -82,7 +84,7 @@ class TCP: return None 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 None diff --git a/sql/adduser.sql b/sql/adduser.sql deleted file mode 100644 index 072ffed..0000000 --- a/sql/adduser.sql +++ /dev/null @@ -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; diff --git a/sql/cef-generator.py b/sql/cef-generator.py index edd512b..affabda 100644 --- a/sql/cef-generator.py +++ b/sql/cef-generator.py @@ -9,7 +9,6 @@ import time import random from datetime import datetime - def generate_alert(alert_type): # Dictionnaire pour différents types d'alertes réseau et fichiers alert_templates = { @@ -71,7 +70,7 @@ def generate_filename(): def generate_alerts(conn, cursor, main_headers): # 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: 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 sql_query = """ INSERT INTO alertes ( - cef_version, date_alerte, event_gravite, device_product, - device_vendor, device_version, alerte_name, sourceAddress, - destinationAddress, destinationPort, sourcePort, protocol, - applicationProtocol, reason, action, commentaire + cef_version, date_alerte, agent_severity, device_event_class_id, + device_product, device_vendor, device_version, name, dst, src, + dpt, spt, msg, proto, bytesin, bytesout, reason, act ) VALUES ( - %(cef_version)s, %(date_alerte)s, %(event_gravite)s, %(device_product)s, - %(device_vendor)s, %(device_version)s, %(alerte_name)s, %(src)s, - %(dst)s, %(destinationPort)s, %(sourcePort)s, %(protocol)s, - %(applicationProtocol)s, %(reason)s, %(action)s, %(commentaire)s + %(cef_version)s, %(date_alerte)s, %(agent_severity)s, %(device_event_class_id)s, + %(device_product)s, %(device_vendor)s, %(device_version)s, %(name)s, %(dst)s, + %(src)s, %(dpt)s, %(spt)s, %(msg)s, %(proto)s, %(bytesin)s, %(bytesout)s, + %(reason)s, %(act)s ); """ @@ -97,20 +95,22 @@ def generate_alerts(conn, cursor, main_headers): params = { "cef_version": merged["CEF"], "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_vendor": merged["Device Vendor"], "device_version": merged["Device Version"], - "alerte_name": merged["name"], + "name": merged["name"], "src": merged["src"], "dst": merged["dst"], - "destinationPort": None, # A définir si disponible - "sourcePort": None, # A définir si disponible - "protocol": "TCP", # Par défaut, à adapter si besoin - "applicationProtocol": "N/A", # À ajuster en fonction des besoins - "reason": "Suspicious activity detected", # Exemple, à adapter - "action": "Alerted", # Exemple d'action - "commentaire": "" # Optionnel + "dpt": None, + "spt": None, + "msg": "Message", + "proto": "TCP", + "bytesin": None, + "bytesout": None, + "reason": "Activité suspecte", + "act": "Alert" } # Exécution de la requête d'insertion @@ -132,17 +132,11 @@ def main(): cursor = conn.cursor() - # En-têtes généraux - CEF_version = 1 - Device_vendor = "ArKa" - Device_product = "SIDPS" - Device_version = "vAlpha" - main_headers = { - "CEF": CEF_version, - "Device Vendor": Device_vendor, - "Device Product": Device_product, - "Device Version": Device_version + "CEF": 1, + "Device Vendor": "ArKa", + "Device Product": "SIDPS", + "Device Version": "vAlpha" } # Lancer la génération d'alertes diff --git a/sql/db-schema.sql b/sql/db-schema.sql index e33ff05..94772e1 100644 --- a/sql/db-schema.sql +++ b/sql/db-schema.sql @@ -12,18 +12,21 @@ CREATE TABLE alertes ( id SERIAL PRIMARY KEY, -- Identifiant unique pour chaque alerte 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 - 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_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 - alerte_name VARCHAR(512), -- Nom descriptif de l'alerte - destinationAddress VARCHAR(45), -- Adresse IP de destination impliquée dans l'alerte - sourceAddress VARCHAR(45), -- Adresse IP source impliquée dans l'alerte - destinationPort INT, -- Port de destination utilisé pour l'événement ou l'alerte - sourcePort INT, -- Port source de l'événement ou de l'alerte - protocol VARCHAR(10), -- Protocole réseau impliqué (ex : TCP, UDP) - applicationProtocol VARCHAR(20), -- Protocole applicatif impliqué (ex : HTTP, FTP) - reason TEXT, -- Description de la raison de l'alerte expliquant pourquoi elle a été générée - action VARCHAR(50), -- Action entreprise en réponse à l'alerte (ex : bloqué, alerté uniquement, ...) - commentaire TEXT -- Champ texte pour des notes ou commentaires additionnels concernant l'alerte + name VARCHAR(512), -- Nom descriptif de l'alerte + -- Champ d'extension du CEF + dst VARCHAR(45), -- Adresse IP de destination impliquée dans l'alerte + src VARCHAR(45), -- Adresse IP source impliquée dans l'alerte + dpt INT, -- Port de destination utilisé pour l'événement ou l'alerte + spt INT, -- Port source de l'événement ou de l'alerte + msg VARCHAR(1023), -- Champ texte pour des notes ou commentaires additionnels concernant l'alerte + proto VARCHAR(10), -- Protocole réseau impliqué (ex : TCP, UDP) + bytesin INT, -- Quantité de bytes (8 bits ici) entrant (cas de flood ou DOS) + 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, ...) ); diff --git a/sql/init.sql b/sql/init.sql index c9677cf..61f7aa8 100644 --- a/sql/init.sql +++ b/sql/init.sql @@ -18,18 +18,20 @@ CREATE TABLE alertes ( id SERIAL PRIMARY KEY, -- Identifiant unique pour chaque alerte 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 - 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_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 - alerte_name VARCHAR(512), -- Nom descriptif de l'alerte - destinationAddress VARCHAR(45), -- Adresse IP de destination impliquée dans l'alerte - sourceAddress VARCHAR(45), -- Adresse IP source impliquée dans l'alerte - destinationPort INT, -- Port de destination utilisé pour l'événement ou l'alerte - sourcePort INT, -- Port source de l'événement ou de l'alerte - protocol VARCHAR(10), -- Protocole réseau impliqué (ex : TCP, UDP) - applicationProtocol VARCHAR(20), -- Protocole applicatif impliqué (ex : HTTP, FTP) - reason TEXT, -- Description de la raison de l'alerte expliquant pourquoi elle a été générée - action VARCHAR(50), -- Action entreprise en réponse à l'alerte (ex : bloqué, alerté uniquement, ...) - commentaire TEXT -- Champ texte pour des notes ou commentaires additionnels concernant l'alerte + name VARCHAR(512), -- Nom descriptif de l'alerte + dst VARCHAR(45), -- Adresse IP de destination impliquée dans l'alerte + src VARCHAR(45), -- Adresse IP source impliquée dans l'alerte + dpt INT, -- Port de destination utilisé pour l'événement ou l'alerte + spt INT, -- Port source de l'événement ou de l'alerte + msg VARCHAR(1023), -- Champ texte pour des notes ou commentaires additionnels concernant l'alerte + proto VARCHAR(10), -- Protocole réseau impliqué (ex : TCP, UDP) + bytesin INT, -- Quantité de bits entrant (cas de flood ou DOS) + bytesout INT, -- Quantité des bits 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, ...) );