mirror of
https://github.com/Oxbian/SIDPS.git
synced 2025-05-17 22:18:13 +02:00
feat: protection system for the idps
This commit is contained in:
parent
4b84c4ebb1
commit
c4df869596
@ -6,26 +6,36 @@
|
||||
"db_user": "sidps",
|
||||
"db_password": "SUPERPASSWORD",
|
||||
"db_port": "3306",
|
||||
"protection": 1,
|
||||
"cef_version": 1,
|
||||
"device_product": "SIDPS",
|
||||
"device_vendor": "ArKa",
|
||||
"device_version": "vAlpha",
|
||||
"synscan_time": 180,
|
||||
"synscan_count": 5,
|
||||
"synscan_bantime": 300,
|
||||
"tcpconnectscan_time": 180,
|
||||
"tcpconnectscan_count": 5,
|
||||
"tcpconnectscan_bantime": 300,
|
||||
"ackscan_time": 180,
|
||||
"ackscan_count": 5,
|
||||
"ackscan_bantime": 300,
|
||||
"finscan_time": 180,
|
||||
"finscan_count": 5,
|
||||
"finscan_bantime": 300,
|
||||
"nullscan_time": 180,
|
||||
"nullscan_count": 5,
|
||||
"nullscan_bantime": 300,
|
||||
"xmasscan_time": 180,
|
||||
"xmasscan_count": 5,
|
||||
"xmasscan_bantime": 300,
|
||||
"synflood_time": 60,
|
||||
"synflood_count": 100,
|
||||
"synflood_bantime": 300,
|
||||
"tcpconnectflood_time": 60,
|
||||
"tcpconnectflood_count": 100,
|
||||
"tcpconnectflood_bantime": 300,
|
||||
"syndos_time": 60,
|
||||
"syndos_count": 100
|
||||
"syndos_count": 100,
|
||||
"syndos_bantime": 300
|
||||
}
|
||||
|
@ -6,26 +6,36 @@
|
||||
"db_user": "sidps",
|
||||
"db_password": "SUPERPASSWORD",
|
||||
"db_port": "3306",
|
||||
"protection": 0,
|
||||
"cef_version": 1,
|
||||
"device_product": "Sonde IDS",
|
||||
"device_vendor": "ArKa",
|
||||
"device_version": "vAlpha",
|
||||
"synscan_time": 180,
|
||||
"synscan_count": 5,
|
||||
"synscan_bantime": 300,
|
||||
"tcpconnectscan_time": 180,
|
||||
"tcpconnectscan_count": 5,
|
||||
"tcpconnectscan_bantime": 300,
|
||||
"ackscan_time": 180,
|
||||
"ackscan_count": 5,
|
||||
"ackscan_bantime": 300,
|
||||
"finscan_time": 180,
|
||||
"finscan_count": 5,
|
||||
"finscan_bantime": 300,
|
||||
"nullscan_time": 180,
|
||||
"nullscan_count": 5,
|
||||
"nullscan_bantime": 300,
|
||||
"xmasscan_time": 180,
|
||||
"xmasscan_count": 5,
|
||||
"xmasscan_bantime": 300,
|
||||
"synflood_time": 60,
|
||||
"synflood_count": 100,
|
||||
"synflood_bantime": 300,
|
||||
"tcpconnectflood_time": 60,
|
||||
"tcpconnectflood_count": 100,
|
||||
"tcpconnectflood_bantime": 300,
|
||||
"syndos_time": 60,
|
||||
"syndos_count": 100
|
||||
"syndos_count": 100,
|
||||
"syndos_bantime": 300
|
||||
}
|
||||
|
11
config.json
11
config.json
@ -12,20 +12,29 @@
|
||||
"device_version": "vAlpha",
|
||||
"synscan_time": 180,
|
||||
"synscan_count": 5,
|
||||
"synscan_bantime": 300,
|
||||
"tcpconnectscan_time": 180,
|
||||
"tcpconnectscan_count": 5,
|
||||
"tcpconnectscan_bantime": 300,
|
||||
"ackscan_time": 180,
|
||||
"ackscan_count": 5,
|
||||
"ackscan_bantime": 300,
|
||||
"finscan_time": 180,
|
||||
"finscan_count": 5,
|
||||
"finscan_bantime": 300,
|
||||
"nullscan_time": 180,
|
||||
"nullscan_count": 5,
|
||||
"nullscan_bantime": 300,
|
||||
"xmasscan_time": 180,
|
||||
"xmasscan_count": 5,
|
||||
"xmasscan_bantime": 300,
|
||||
"synflood_time": 60,
|
||||
"synflood_count": 100,
|
||||
"synflood_bantime": 300,
|
||||
"tcpconnectflood_time": 60,
|
||||
"tcpconnectflood_count": 100,
|
||||
"tcpconnectflood_bantime": 300,
|
||||
"syndos_time": 60,
|
||||
"syndos_count": 100
|
||||
"syndos_count": 100,
|
||||
"syndos_bantime": 300
|
||||
}
|
||||
|
@ -46,13 +46,13 @@ class Database:
|
||||
|
||||
# Paramètres pour la requête SQL
|
||||
params = {
|
||||
"cef_version": self.get_key("cef_version", 1),
|
||||
"cef_version": self.config.get("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"),
|
||||
"device_product": self.config.get("device_product", "SIDPS"),
|
||||
"device_vendor": self.config.get("device_vendor", "ArKa"),
|
||||
"device_version": self.config.get("device_version", "vAlpha"),
|
||||
"name": name,
|
||||
"src": src,
|
||||
"dst": dst,
|
||||
@ -72,10 +72,3 @@ class Database:
|
||||
cursor.close()
|
||||
except mysql.connector.Error as err:
|
||||
print("Erreur lors de l'envoi de l'alerte: {}".format(err))
|
||||
|
||||
def get_key(self, key, default_val):
|
||||
"""Donne le contenue d'un paramètre spécifique de la config
|
||||
@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, default_val)
|
||||
|
96
idps/main.py
96
idps/main.py
@ -1,13 +1,48 @@
|
||||
from scapy.all import sniff, TCP, IP
|
||||
from scapy.config import conf
|
||||
conf.debug_dissector = 2
|
||||
|
||||
import importlib.util
|
||||
import os
|
||||
import time
|
||||
import tcp
|
||||
import database
|
||||
import json
|
||||
import protection
|
||||
|
||||
from scapy.all import sniff, TCP, IP
|
||||
from scapy.config import conf
|
||||
conf.debug_dissector = 2
|
||||
|
||||
|
||||
def check_frame_w_rules(packet, rules_functions, objets):
|
||||
"""Appliquer chaque règle des fonctions au paquet capturé.
|
||||
@param packet: Paquet actuel à analyser
|
||||
@param rules_functions: liste de fonctions de règles
|
||||
@param objets: Dictionnaire contenant le dictionnaire de config, la liste des paquets tcp précédents,
|
||||
la db, et le gestionnaire de règles Iptables"""
|
||||
|
||||
for rule_func in rules_functions:
|
||||
try:
|
||||
rule_func(packet, objets)
|
||||
except Exception as e:
|
||||
print(f"Erreur lors de l'exécution de la règle : {e}")
|
||||
|
||||
|
||||
def packet_callback(packet, rules_functions, objets):
|
||||
"""Callback réception d'un paquet
|
||||
@param packet: Paquet actuel à classer
|
||||
@param rules_functions: liste des fonctions de règles
|
||||
@param objets: Dictionnaire contenant le dictionnaire de config, la liste des paquets tcp précédents,
|
||||
la db, et le gestionnaire de règles Iptables"""
|
||||
|
||||
# Nettoyage des règles et paquets TCP dépassé
|
||||
objets["iptables_manager"].del_rules()
|
||||
objets["tcp_packets"].clean_old_packets()
|
||||
|
||||
if IP in packet and TCP in packet:
|
||||
packet_origin = objets["tcp_packets"].add_packet(packet[IP].src, packet[TCP].sport, packet[IP].dst, packet[TCP].dport, packet[TCP].flags, time.time())
|
||||
|
||||
# Stockage du paquet originel lié à ce paquet pour identifier la provenance de l'attaque
|
||||
objets['pkt_origin'] = packet_origin
|
||||
|
||||
check_frame_w_rules(packet, rules_functions['TCP'], objets)
|
||||
|
||||
|
||||
def load_rules(rules_dirpath = "/app/idps/rules"):
|
||||
@ -68,37 +103,6 @@ def load_rules(rules_dirpath = "/app/idps/rules"):
|
||||
return rules_functions
|
||||
|
||||
|
||||
def check_frame_w_rules(packet, rules_functions, packets, db):
|
||||
"""Appliquer chaque règle des fonctions au paquet capturé.
|
||||
@param packet: Paquet actuel à analyser
|
||||
@param rules_functions: liste de fonctions de règles
|
||||
@param packets: liste des paquets précédents (utile pour TCP)
|
||||
@param db: Objet database pour envoyer les alertes à la BDD
|
||||
"""
|
||||
|
||||
for rule_func in rules_functions:
|
||||
try:
|
||||
rule_func(packet, packets, db)
|
||||
except Exception as e:
|
||||
print(f"Erreur lors de l'exécution de la règle : {e}")
|
||||
|
||||
|
||||
def packet_callback(packet, rules_functions, tcp_packets, db):
|
||||
"""Callback réception d'un paquet
|
||||
@param packet: Paquet actuel à classer
|
||||
@param rules_functions: liste des fonctions de règles
|
||||
@param tcp_packets: Objet contenant une liste des paquets tcp précédents
|
||||
@param db: Objet database pour envoyer des alertes à la BDD
|
||||
"""
|
||||
|
||||
#print(packet)
|
||||
if IP in packet and TCP in packet:
|
||||
tcp_packets.add_packet(packet[IP].src, packet[TCP].sport, packet[IP].dst, packet[TCP].dport, packet[TCP].flags, time.time())
|
||||
#print(tcp_packets[packet[IP].src])
|
||||
check_frame_w_rules(packet, rules_functions['TCP'], tcp_packets, db)
|
||||
tcp_packets.clean_old_packets()
|
||||
|
||||
|
||||
def read_config(config_filepath='config.json'):
|
||||
"""Charge les configurations depuis le fichier de config"""
|
||||
|
||||
@ -115,29 +119,29 @@ def read_config(config_filepath='config.json'):
|
||||
def start_idps():
|
||||
"""Charge les règles et démarre l'IDPS"""
|
||||
|
||||
print(f"Récupération des configurations")
|
||||
print("Récupération des configurations")
|
||||
config = read_config()
|
||||
print(f"Configurations chargées")
|
||||
print("Configurations chargées")
|
||||
|
||||
print(f"Chargement des règles...")
|
||||
print("Chargement des règles...")
|
||||
rules_functions = load_rules(config["rules_dirpath"])
|
||||
print(f"Les règles sont chargées")
|
||||
print("Les règles sont chargées")
|
||||
|
||||
print(f"Connexion à la base de données")
|
||||
print("Connexion à la base de données")
|
||||
db = database.Database(config)
|
||||
print(f"Connexion réussite à la base de données")
|
||||
|
||||
# Opti possible: charger les règles par protocole, permettant des filtrages et donc optimiser
|
||||
# le nombre de fonctions vérifiant le paquet (snort s'arrête à la première corrélation par exemple)
|
||||
print("Connexion réussite à la base de données")
|
||||
|
||||
tcp_packets = tcp.TCP(300)
|
||||
protection_system = protection.Protection(config["protection"])
|
||||
|
||||
objets = {"config": config, "database": db, "tcp_packets": tcp_packets, "iptables_manager": protection_system}
|
||||
|
||||
# Lancer scapy & envoyer le paquet à chaque règle de l'IDPS
|
||||
sniff(iface=config["ifaces"], prn=lambda packet: packet_callback(packet, rules_functions, tcp_packets, db), store=0)
|
||||
sniff(iface=config["ifaces"], prn=lambda packet: packet_callback(packet, rules_functions, objets), store=0)
|
||||
|
||||
|
||||
def main():
|
||||
print(f"Démarrage de l'IDPS")
|
||||
print("Démarrage de l'IDPS")
|
||||
start_idps()
|
||||
|
||||
|
||||
|
49
idps/protection.py
Normal file
49
idps/protection.py
Normal file
@ -0,0 +1,49 @@
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
|
||||
class Protection:
|
||||
"""Classe pour activer la protection du système avec iptables"""
|
||||
|
||||
def __init__(self, activate = 0):
|
||||
"""Initialisation de la protection avec une liste pour stockées les règles créer"""
|
||||
self.rules = []
|
||||
self.activate = int(activate)
|
||||
|
||||
def add_rule(self, rule, duration):
|
||||
"""Ajouter une règle dans iptables
|
||||
@param rule: Règle à ajouter
|
||||
@param duration: Durée d'execution de la règle"""
|
||||
|
||||
print(f"Rule: {rule}, {duration}, {self.activate}")
|
||||
if self.activate == 0:
|
||||
return
|
||||
|
||||
print("Rule run")
|
||||
try:
|
||||
subprocess.run(rule.split(' '), check=True)
|
||||
print(f"[iptables] Règle ajouter {rule}")
|
||||
self.rules.append([rule, time.time(), duration])
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"[iptables] Erreur suppression de la règle {rule}: {e}")
|
||||
|
||||
def del_rules(self):
|
||||
"""Supprimer les règles obsolètes de l'iptables"""
|
||||
|
||||
if self.activate is False:
|
||||
return
|
||||
|
||||
curr = time.time()
|
||||
for i, elt in enumerate(self.rules, 0):
|
||||
if elt[1] + elt[2] <= curr:
|
||||
self.del_rule(self, elt[0].replace("-I", "-D"), i)
|
||||
|
||||
def del_rule(self, rule, i):
|
||||
"""Supprimer une règle dans iptables"""
|
||||
|
||||
try:
|
||||
subprocess.run(rule.split(' '), check=True)
|
||||
print("[iptables] Règle supprimer {rule}")
|
||||
self.rules.pop(i)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"[iptables] Erreur suppression de la règle {rule}: {e}")
|
@ -2,7 +2,7 @@ from datetime import datetime
|
||||
import time
|
||||
|
||||
|
||||
def rule(packet, tcp_packets, db):
|
||||
def rule(packet, objets):
|
||||
"""
|
||||
Règle SYN Dos:
|
||||
Un SYN DOS va envoyer des requêtes TCP avec le flag SYN en très grand nombre afin de surcharger le serveur ou la cible avec des grosses quantités de données
|
||||
@ -15,17 +15,19 @@ def rule(packet, tcp_packets, db):
|
||||
return
|
||||
|
||||
# Initialisation des paramètres à partir de la configuration si nécessaire
|
||||
if (rule.seuil == 0 and rule.time_window == 0):
|
||||
rule.time_window = db.get_key("synsdos_time", 60)
|
||||
rule.seuil = db.get_key("syndos_count", 100)
|
||||
if (rule.seuil == 0 and rule.time_window == 0 and rule.ban_time == 0):
|
||||
rule.time_window = objets["config"].get("synsdos_time", 60)
|
||||
rule.seuil = objets["config"].get("syndos_count", 100)
|
||||
rule.ban_time = objets["config"].get("syndos_bantime", 300)
|
||||
|
||||
# Comptage des paquets TCP correspondant aux motifs spécifiques
|
||||
syndeny_count = tcp_packets.count_packet_of_type(["S", "RA"], rule.time_window, True)
|
||||
synaccept_count = tcp_packets.count_packet_of_type(["S", "SA", "R"], rule.time_window, True)
|
||||
syndeny_count = objets["tcp_packets"].count_packet_of_type(["S", "RA"], rule.time_window, True)
|
||||
synaccept_count = objets["tcp_packets"].count_packet_of_type(["S", "SA", "R"], rule.time_window, True)
|
||||
|
||||
# Détection si le seuil est dépassé et que la charge utile des paquets est non nulle
|
||||
if (syndeny_count + synaccept_count >= rule.seuil and len(packet['TCP'].payload) > 0):
|
||||
db.send_alert(datetime.now(), 5, None, "Syn DOS", packet['IP'].src, packet['IP'].dst, proto="TCP", reason="Détection de nombreux patterns de Syn->SynACK->Reset et Syn->Reset ACK", act="Alerte")
|
||||
objets["database"].send_alert(datetime.now(), 5, None, "Syn DOS", objets["pkt_origin"][0], objets["pkt_origin"][2], proto="TCP", reason="Détection de nombreux patterns de Syn->SynACK->Reset et Syn->Reset ACK", act="Alerte")
|
||||
objets["iptables_manager"].add_rule("iptables -I FORWARD -s " + objets["pkt_origin"][0] + " -j DROP", rule.ban_time)
|
||||
print("Alerte, seuil dépassé, risque de Syn DOS détecté.")
|
||||
rule.cooldown = time.time()
|
||||
|
||||
@ -34,3 +36,4 @@ def rule(packet, tcp_packets, db):
|
||||
rule.cooldown = 0
|
||||
rule.time_window = 0
|
||||
rule.seuil = 0
|
||||
rule.ban_time = 0
|
||||
|
@ -2,7 +2,7 @@ from datetime import datetime
|
||||
import time
|
||||
|
||||
|
||||
def rule(packet, tcp_packets, db):
|
||||
def rule(packet, objets):
|
||||
"""
|
||||
Règle SYN Flood:
|
||||
Un SYN Flood va envoyer des requêtes TCP avec le flag SYN en très grand nombre afin de surcharger le serveur ou la cible
|
||||
@ -15,17 +15,19 @@ def rule(packet, tcp_packets, db):
|
||||
return
|
||||
|
||||
# Initialisation des paramètres à partir de la configuration si nécessaire
|
||||
if (rule.seuil == 0 and rule.time_window == 0):
|
||||
rule.time_window = db.get_key("synsflood_time", 60)
|
||||
rule.seuil = db.get_key("synflood_count", 100)
|
||||
if (rule.seuil == 0 and rule.time_window == 0 and rule.ban_time == 0):
|
||||
rule.time_window = objets["config"].get("synsflood_time", 60)
|
||||
rule.seuil = objets["config"].get("synflood_count", 100)
|
||||
rule.ban_time = objets["config"].get("synflood_bantime", 300)
|
||||
|
||||
# Comptage des paquets TCP correspondant aux motifs spécifiques
|
||||
syndeny_count = tcp_packets.count_packet_of_type(["S", "RA"], rule.time_window, True)
|
||||
synaccept_count = tcp_packets.count_packet_of_type(["S", "SA", "R"], rule.time_window, True)
|
||||
syndeny_count = objets["tcp_packets"].count_packet_of_type(["S", "RA"], rule.time_window, True)
|
||||
synaccept_count = objets["tcp_packets"].count_packet_of_type(["S", "SA", "R"], rule.time_window, True)
|
||||
|
||||
# Détection si le seuil est dépassé
|
||||
if (syndeny_count + synaccept_count >= rule.seuil):
|
||||
db.send_alert(datetime.now(), 5, None, "Syn flood", packet['IP'].src, packet['IP'].dst, proto="TCP", reason="Détection de nombreux patterns de Syn->SynACK->Reset et Syn->Reset ACK", act="Alerte")
|
||||
objets["database"].send_alert(datetime.now(), 5, None, "Syn flood", objets["pkt_origin"][0], objets["pkt_origin"][2], proto="TCP", reason="Détection de nombreux patterns de Syn->SynACK->Reset et Syn->Reset ACK", act="Alerte")
|
||||
objets["iptables_manager"].add_rule("iptables -I FORWARD -s " + objets["pkt_origin"][0] + " -j DROP", rule.ban_time)
|
||||
print("Alerte, seuil dépassé, risque de Syn Flood détecté.")
|
||||
rule.cooldown = time.time()
|
||||
|
||||
@ -34,3 +36,4 @@ def rule(packet, tcp_packets, db):
|
||||
rule.cooldown = 0
|
||||
rule.time_window = 0
|
||||
rule.seuil = 0
|
||||
rule.ban_time = 0
|
||||
|
@ -2,7 +2,7 @@ from datetime import datetime
|
||||
import time
|
||||
|
||||
|
||||
def rule(packet, tcp_packets, db):
|
||||
def rule(packet, objets):
|
||||
"""Règle TCPConnect Flood:
|
||||
Un flood TCP connect va effectuer une connexion TCP en très grand nombre
|
||||
Si le port est ouvert le serveur acceptera la connexion SYN -> SYN ACK -> ACK -> Reset ACK
|
||||
@ -13,17 +13,19 @@ def rule(packet, tcp_packets, db):
|
||||
return
|
||||
|
||||
# Vérification si nécessaire de récupérer les variables depuis la config
|
||||
if (rule.seuil == 0 and rule.time_window == 0):
|
||||
rule.time_window = db.get_key("tcpconnectflood_time", 60)
|
||||
rule.seuil = db.get_key("tcpconnectflood_count", 100)
|
||||
if (rule.seuil == 0 and rule.time_window == 0 and rule.ban_time == 0):
|
||||
rule.time_window = objets["config"].get("tcpconnectflood_time", 60)
|
||||
rule.seuil = objets["config"].get("tcpconnectflood_count", 100)
|
||||
rule.ban_time = objets["config"].get("tcpconnectflood_bantime", 300)
|
||||
|
||||
# Comptage du nombre de scan tcp connect acceptés et refusés
|
||||
tcpconnectdeny_count = tcp_packets.count_packet_of_type(["S", "RA"], rule.time_window, True)
|
||||
tcpconnectaccept_count = tcp_packets.count_packet_of_type(["S", "SA", "A", "RA"], rule.time_window, True)
|
||||
tcpconnectdeny_count = objets["tcp_packets"].count_packet_of_type(["S", "RA"], rule.time_window, True)
|
||||
tcpconnectaccept_count = objets["tcp_packets"].count_packet_of_type(["S", "SA", "A", "RA"], rule.time_window, True)
|
||||
|
||||
if (tcpconnectaccept_count + tcpconnectdeny_count >= rule.seuil):
|
||||
db.send_alert(datetime.now(), 5, None, "TCPConnect Flood", 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 TCPconnect Flood")
|
||||
objets["database"].send_alert(datetime.now(), 5, None, "TCPConnect Flood", objets["pkt_origin"][0], objets["pkt_origin"][2], proto="TCP", reason="Détection de nombreux patterns de Syn->SynACK->ACK->Reset ACK et Syn->Reset ACK", act="Alerte")
|
||||
objets["iptables_manager"].add_rule("iptables -I FORWARD -s " + objets["pkt_origin"][0] + " -j DROP", rule.ban_time)
|
||||
print("Alerte, seuils dépassés, risque de TCPconnect Flood")
|
||||
rule.cooldown = time.time()
|
||||
|
||||
|
||||
@ -31,4 +33,4 @@ def rule(packet, tcp_packets, db):
|
||||
rule.cooldown = 0
|
||||
rule.time_window = 0
|
||||
rule.seuil = 0
|
||||
|
||||
rule.ban_time = 0
|
||||
|
@ -2,7 +2,7 @@ from datetime import datetime
|
||||
import time
|
||||
|
||||
|
||||
def rule(packet, tcp_packets, db):
|
||||
def rule(packet, objets):
|
||||
"""Règle ACK Scan:
|
||||
Un ACK Scan va envoyer des requêtes TCP avec le flag ACK
|
||||
Si le firewall ne bloque pas, alors il répond avec le flag Reset
|
||||
@ -12,17 +12,19 @@ def rule(packet, tcp_packets, db):
|
||||
return
|
||||
|
||||
# Vérification si nécessaire de récupérer les variables depuis la config
|
||||
if (rule.seuil == 0 and rule.time_window == 0):
|
||||
rule.time_window = db.get_key("ackscan_time", 180)
|
||||
rule.seuil = db.get_key("ackscan_count", 5)
|
||||
if (rule.seuil == 0 and rule.time_window == 0 and rule.ban_time == 0):
|
||||
rule.time_window = objets["config"].get("ackscan_time", 180)
|
||||
rule.seuil = objets["config"].get("ackscan_count", 5)
|
||||
rule.ban_time = objets["config"].get("ackscan_bantime", 300)
|
||||
|
||||
# Comptage nombre de scan ack acceptés et refusés
|
||||
ackdeny_count = tcp_packets.count_packet_of_type(["A", "R"], rule.time_window, True)
|
||||
ackaccept_count = tcp_packets.count_packet_of_type(["A"], rule.time_window, True)
|
||||
ackdeny_count = objets["tcp_packets"].count_packet_of_type(["A", "R"], rule.time_window, True)
|
||||
ackaccept_count = objets["tcp_packets"].count_packet_of_type(["A"], rule.time_window, True)
|
||||
|
||||
if (ackaccept_count + ackdeny_count >= rule.seuil):
|
||||
db.send_alert(datetime.now(), 5, None, "ACK scan", packet['IP'].src, packet['IP'].dst, proto="TCP", reason="Détection de nombreux patterns de Ack->Reset et Ack pas de réponse", act="Alerte")
|
||||
print(f"Alerte, seuil dépassés, risque d'Ack scan")
|
||||
objets["database"].send_alert(datetime.now(), 5, None, "ACK scan", objets["pkt_origin"][0], objets["pkt_origin"][2], proto="TCP", reason="Détection de nombreux patterns de Ack->Reset et Ack pas de réponse", act="Alerte")
|
||||
print("Alerte, seuil dépassés, risque d'Ack scan")
|
||||
objets["iptables_manager"].add_rule("iptables -I FORWARD -s " + objets["pkt_origin"][0] + " -j DROP", rule.ban_time)
|
||||
rule.cooldown = time.time()
|
||||
|
||||
|
||||
@ -30,3 +32,4 @@ def rule(packet, tcp_packets, db):
|
||||
rule.cooldown = 0
|
||||
rule.time_window = 0
|
||||
rule.seuil = 0
|
||||
rule.ban_time = 0
|
||||
|
@ -2,27 +2,30 @@ from datetime import datetime
|
||||
import time
|
||||
|
||||
|
||||
def rule(packet, tcp_packets, db):
|
||||
def rule(packet, objets):
|
||||
"""Règle Fin Scan:
|
||||
Un Fin Scan va envoyer des requêtes TCP avec le flag Fin
|
||||
Si le port est ouvert alors le serveur répondra pas
|
||||
Sinon le port est fermé et le serveur répondra: Reset ACK
|
||||
"""
|
||||
|
||||
if (rule.cooldown + rule.time_window > time.time()):
|
||||
return
|
||||
|
||||
# Vérification si nécessaire de récupérer les variables depuis la config
|
||||
if (rule.seuil == 0 and rule.time_window == 0):
|
||||
rule.time_window = db.get_key("finscan_time", 180)
|
||||
rule.seuil = db.get_key("finscan_count", 5)
|
||||
if (rule.seuil == 0 and rule.time_window == 0 and rule.ban_time == 0):
|
||||
rule.time_window = objets["config"].get("finscan_time", 180)
|
||||
rule.seuil = objets["config"].get("finscan_count", 5)
|
||||
rule.ban_time = objets["config"].get("finscan_bantime", 300)
|
||||
|
||||
# Comptage du nombre de scan fin acceptés et refusés
|
||||
findeny_count = tcp_packets.count_packet_of_type(["F", "RA"], rule.time_window, True)
|
||||
finaccept_count = tcp_packets.count_packet_of_type(["F"], rule.time_window, True)
|
||||
findeny_count = objets["tcp_packets"].count_packet_of_type(["F", "RA"], rule.time_window, True)
|
||||
finaccept_count = objets["tcp_packets"].count_packet_of_type(["F"], rule.time_window, True)
|
||||
|
||||
if (findeny_count + finaccept_count >= rule.seuil):
|
||||
db.send_alert(datetime.now(), 5, None, "Fin scan", packet['IP'].src, packet['IP'].dst, proto="TCP", reason="Détection de nombreux patterns de Fin->Reset Ack et Fin->rien", act="Alerte")
|
||||
print(f"Alerte, seuil dépassés, risque de Fin Scan")
|
||||
objets["database"].send_alert(datetime.now(), 5, None, "Fin scan", objets["pkt_origin"][0], objets["pkt_origin"][2], proto="TCP", reason="Détection de nombreux patterns de Fin->Reset Ack et Fin->rien", act="Alerte")
|
||||
objets["iptables_manager"].add_rule("iptables -I FORWARD -s " + objets["pkt_origin"][0] + " -j DROP", rule.ban_time)
|
||||
print("Alerte, seuil dépassés, risque de Fin Scan")
|
||||
rule.cooldown = time.time()
|
||||
|
||||
|
||||
@ -30,3 +33,4 @@ def rule(packet, tcp_packets, db):
|
||||
rule.cooldown = 0
|
||||
rule.time_window = 0
|
||||
rule.seuil = 0
|
||||
rule.ban_time = 0
|
||||
|
@ -2,7 +2,7 @@ from datetime import datetime
|
||||
import time
|
||||
|
||||
|
||||
def rule(packet, tcp_packets, db):
|
||||
def rule(packet, objets):
|
||||
"""Règle Null Scan:
|
||||
Un Null Scan va envoyer des requêtes TCP avec aucun flag d'actif
|
||||
Si le port est ouvert alors le serveur ne répondra pas
|
||||
@ -12,17 +12,19 @@ def rule(packet, tcp_packets, db):
|
||||
return
|
||||
|
||||
# Vérification si nécessaire de récupérer les variables depuis la config
|
||||
if (rule.seuil == 0 and rule.time_window == 0):
|
||||
rule.time_window = db.get_key("nullscan_time", 180)
|
||||
rule.seuil = db.get_key("nullscan_count", 5)
|
||||
if (rule.seuil == 0 and rule.time_window == 0 and rule.ban_time == 0):
|
||||
rule.time_window = objets["config"].get("nullscan_time", 180)
|
||||
rule.seuil = objets["config"].get("nullscan_count", 5)
|
||||
rule.ban_time = objets["config"].get("nullscan_bantime", 300)
|
||||
|
||||
# Comptage du nombre de scan null acceptés et refusés
|
||||
nulldeny_count = tcp_packets.count_packet_of_type(["", "RA"], rule.time_window, True)
|
||||
nullaccept_count = tcp_packets.count_packet_of_type([""], rule.time_window, True)
|
||||
nulldeny_count = objets["tcp_packets"].count_packet_of_type(["", "RA"], rule.time_window, True)
|
||||
nullaccept_count = objets["tcp_packets"].count_packet_of_type([""], rule.time_window, True)
|
||||
|
||||
if (nulldeny_count + nulldeny_count >= rule.seuil):
|
||||
db.send_alert(datetime.now(), 5, None, "Null scan", packet['IP'].src, packet['IP'].dst, proto="TCP", reason="Détection de nombreux patterns de None->Reset Ack et None -> rien", act="Alerte")
|
||||
print(f"Alerte, seuil dépassés, risque de Null Scan")
|
||||
if (nullaccept_count + nulldeny_count >= rule.seuil):
|
||||
objets["database"].send_alert(datetime.now(), 5, None, "Null scan", objets["pkt_origin"][0], objets["pkt_origin"][2], proto="TCP", reason="Détection de nombreux patterns de None->Reset Ack et None -> rien", act="Alerte")
|
||||
objets["iptables_manager"].add_rule("iptables -I FORWARD -s " + objets["pkt_origin"][0] + " -j DROP", rule.ban_time)
|
||||
print("Alerte, seuil dépassés, risque de Null Scan")
|
||||
rule.cooldown = time.time()
|
||||
|
||||
|
||||
@ -30,3 +32,4 @@ def rule(packet, tcp_packets, db):
|
||||
rule.cooldown = 0
|
||||
rule.time_window = 0
|
||||
rule.seuil = 0
|
||||
rule.ban_time = 0
|
||||
|
@ -2,7 +2,7 @@ from datetime import datetime
|
||||
import time
|
||||
|
||||
|
||||
def rule(packet, tcp_packets, db):
|
||||
def rule(packet, objets):
|
||||
"""Règle SYN Scan:
|
||||
Un SYNScan va envoyer des requêtes TCP avec le flag SYN
|
||||
Si le port est ouvert alors le serveur répondra: Syn ACK, puis le client Reset la connexion
|
||||
@ -12,17 +12,19 @@ def rule(packet, tcp_packets, db):
|
||||
return
|
||||
|
||||
# Vérification si nécessaire de récupérer les variables depuis la config
|
||||
if (rule.seuil == 0 and rule.time_window == 0):
|
||||
rule.time_window = db.get_key("synscan_time", 180)
|
||||
rule.seuil = db.get_key("synscan_count", 5)
|
||||
if (rule.seuil == 0 and rule.time_window == 0 and rule.ban_time == 0):
|
||||
rule.time_window = objets["config"].get("synscan_time", 180)
|
||||
rule.seuil = objets["config"].get("synscan_count", 5)
|
||||
rule.ban_time = objets["config"].get("synscan_bantime", 300)
|
||||
|
||||
# Comptage du nombre de scan syn acceptés et refusés
|
||||
syndeny_count = tcp_packets.count_packet_of_type(["S", "RA"], rule.time_window, True)
|
||||
synaccept_count = tcp_packets.count_packet_of_type(["S", "SA", "R"], rule.time_window, True)
|
||||
syndeny_count = objets["tcp_packets"].count_packet_of_type(["S", "RA"], rule.time_window, True)
|
||||
synaccept_count = objets["tcp_packets"].count_packet_of_type(["S", "SA", "R"], rule.time_window, True)
|
||||
|
||||
if (synaccept_count + syndeny_count >= rule.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 et Syn->Reset ACK", act="Alerte")
|
||||
print(f"Alerte, seuil dépassés, risque de SynScan")
|
||||
objets["database"].send_alert(datetime.now(), 5, None, "Syn scan", objets["pkt_origin"][0], objets["pkt_origin"][2], proto="TCP", reason="Détection de nombreux patterns de Syn->SynACK->Reset et Syn->Reset ACK", act="Alerte")
|
||||
objets["iptables_manager"].add_rule("/sbin/iptables -I FORWARD -s " + objets["pkt_origin"][0] + " -j DROP", rule.ban_time)
|
||||
print("Alerte, seuil dépassés, risque de SynScan")
|
||||
rule.cooldown = time.time()
|
||||
|
||||
|
||||
@ -30,3 +32,4 @@ def rule(packet, tcp_packets, db):
|
||||
rule.cooldown = 0
|
||||
rule.time_window = 0
|
||||
rule.seuil = 0
|
||||
rule.ban_time = 0
|
||||
|
@ -2,7 +2,7 @@ from datetime import datetime
|
||||
import time
|
||||
|
||||
|
||||
def rule(packet, tcp_packets, db):
|
||||
def rule(packet, objets):
|
||||
"""Règle TCPConnect Scan:
|
||||
Un scan TCP connect va effectuer une connexion TCP en entier sur chaque port scanné.
|
||||
Si le port est ouvert le serveur acceptera la connexion SYN -> SYN ACK -> ACK -> Reset ACK
|
||||
@ -13,17 +13,19 @@ def rule(packet, tcp_packets, db):
|
||||
return
|
||||
|
||||
# Vérification si nécessaire de récupérer les variables depuis la config
|
||||
if (rule.seuil == 0 and rule.time_window == 0):
|
||||
rule.time_window = db.get_key("tcpconnectscan_time", 180)
|
||||
rule.seuil = db.get_key("tcpconnectscan_count", 5)
|
||||
if (rule.seuil == 0 and rule.time_window == 0 and rule.ban_time == 0):
|
||||
rule.time_window = objets["config"].get("tcpconnectscan_time", 180)
|
||||
rule.seuil = objets["config"].get("tcpconnectscan_count", 5)
|
||||
rule.ban_time = objets["config"].get("tcpconnectscan_bantime", 300)
|
||||
|
||||
# Comptage du nombre de scan tcp connect acceptés et refusés
|
||||
tcpconnectdeny_count = tcp_packets.count_packet_of_type(["S", "RA"], rule.time_window, True)
|
||||
tcpconnectaccept_count = tcp_packets.count_packet_of_type(["S", "SA", "A", "RA"], rule.time_window, True)
|
||||
tcpconnectdeny_count = objets["tcp_packets"].count_packet_of_type(["S", "RA"], rule.time_window, True)
|
||||
tcpconnectaccept_count = objets["tcp_packets"].count_packet_of_type(["S", "SA", "A", "RA"], rule.time_window, True)
|
||||
|
||||
if (tcpconnectaccept_count + tcpconnectdeny_count >= rule.seuil):
|
||||
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")
|
||||
objets["database"].send_alert(datetime.now(), 5, None, "TCPConnect Scan", objets["pkt_origin"][0], objets["pkt_origin"][2], proto="TCP", reason="Détection de nombreux patterns de Syn->SynACK->ACK->Reset ACK et Syn->Reset ACK", act="Alerte")
|
||||
objets["iptables_manager"].add_rule("iptables -I FORWARD -s " + objets["pkt_origin"][0] + " -j DROP", rule.ban_time)
|
||||
print("Alerte, seuils dépassés, risque de TCPConnectScan")
|
||||
rule.cooldown = time.time()
|
||||
|
||||
|
||||
@ -31,3 +33,4 @@ def rule(packet, tcp_packets, db):
|
||||
rule.cooldown = 0
|
||||
rule.time_window = 0
|
||||
rule.seuil = 0
|
||||
rule.ban_time = 0
|
||||
|
@ -2,7 +2,7 @@ from datetime import datetime
|
||||
import time
|
||||
|
||||
|
||||
def rule(packet, tcp_packets, db):
|
||||
def rule(packet, objets):
|
||||
"""Règle XMAS Scan:
|
||||
Un XMAS Scan va envoyer des requêtes TCP avec le flag Fin, Push, Urg
|
||||
Si le port est ouvert alors le serveur répondra pas
|
||||
@ -12,17 +12,19 @@ def rule(packet, tcp_packets, db):
|
||||
return
|
||||
|
||||
# Vérification si nécessaire de récupérer les variables depuis la config
|
||||
if (rule.seuil == 0 and rule.time_window == 0):
|
||||
rule.time_window = db.get_key("xmasscan_time", 180)
|
||||
rule.seuil = db.get_key("xmasscan_count", 5)
|
||||
if (rule.seuil == 0 and rule.time_window == 0 and rule.ban_time == 0):
|
||||
rule.time_window = objets["config"].get("xmasscan_time", 180)
|
||||
rule.seuil = objets["config"].get("xmasscan_count", 5)
|
||||
rule.ban_time = objets["config"].get("xmasscan_bantime", 300)
|
||||
|
||||
# Comptage du nombre de scan XMAS acceptés et refusés
|
||||
xmasdeny_count = tcp_packets.count_packet_of_type(["FPU", "RA"], rule.time_window, True)
|
||||
xmasaccept_count = tcp_packets.count_packet_of_type(["FPU"], rule.time_window, True)
|
||||
xmasdeny_count = objets["tcp_packets"].count_packet_of_type(["FPU", "RA"], rule.time_window, True)
|
||||
xmasaccept_count = objets["tcp_packets"].count_packet_of_type(["FPU"], rule.time_window, True)
|
||||
|
||||
if (xmasaccept_count + xmasdeny_count >= rule.seuil):
|
||||
db.send_alert(datetime.now(), 5, None, "XMAS scan", packet['IP'].src, packet['IP'].dst, proto="TCP", reason="Détection de nombreux patterns de Fin Push Urg -> rien et Fin Push Urg->Reset ACK", act="Alerte")
|
||||
print(f"Alerte, seuil dépassés, risque de XMAS Scan")
|
||||
objets["database"].send_alert(datetime.now(), 5, None, "XMAS scan", objets["pkt_origin"][0], objets["pkt_origin"][2], proto="TCP", reason="Détection de nombreux patterns de Fin Push Urg -> rien et Fin Push Urg->Reset ACK", act="Alerte")
|
||||
objets["iptables_manager"].add_rule("iptables -I FORWARD -s " + objets["pkt_origin"][0] + " -j DROP", rule.ban_time)
|
||||
print("Alerte, seuil dépassés, risque de XMAS Scan")
|
||||
rule.cooldown = time.time()
|
||||
|
||||
|
||||
@ -30,3 +32,4 @@ def rule(packet, tcp_packets, db):
|
||||
rule.cooldown = 0
|
||||
rule.time_window = 0
|
||||
rule.seuil = 0
|
||||
rule.ban_time = 0
|
||||
|
38
idps/tcp.py
38
idps/tcp.py
@ -20,15 +20,15 @@ class TCP:
|
||||
|
||||
if flags == "S":
|
||||
self.packets[ip_src].append([port_src, ip_dst, port_dst, ["S"], timestamp])
|
||||
return
|
||||
return self.return_origin_packet(ip_src, port_src, ip_dst, port_dst)
|
||||
|
||||
elif flags is None:
|
||||
self.packets[ip_src].append([port_src, ip_dst, port_dst, [""], timestamp])
|
||||
return
|
||||
return self.return_origin_packet(ip_src, port_src, ip_dst, port_dst)
|
||||
|
||||
elif flags == "FPU":
|
||||
self.packets[ip_src].append([port_src, ip_dst, port_dst, ["FPU"], timestamp])
|
||||
return
|
||||
return self.return_origin_packet(ip_src, port_src, ip_dst, port_dst)
|
||||
|
||||
elif flags == "SA":
|
||||
i, ip = self.find_packet_to_replace(ip_src, port_src, ip_dst, port_dst, "S")
|
||||
@ -36,10 +36,10 @@ class TCP:
|
||||
if i is not None:
|
||||
self.packets[ip][i][3].append("SA")
|
||||
self.packets[ip][i][4] = timestamp
|
||||
return
|
||||
return self.return_origin_packet(ip_src, port_src, ip_dst, port_dst, ip)
|
||||
else:
|
||||
self.packets[ip_src].append([port_src, ip_dst, port_dst, ["SA"], timestamp])
|
||||
return
|
||||
return self.return_origin_packet(ip_src, port_src, ip_dst, port_dst)
|
||||
|
||||
elif flags == "A":
|
||||
i, ip = self.find_packet_to_replace(ip_src, port_src, ip_dst, port_dst, "SA")
|
||||
@ -49,10 +49,10 @@ class TCP:
|
||||
if i is not None:
|
||||
self.packets[ip][i][3].append("A")
|
||||
self.packets[ip][i][4] = timestamp
|
||||
return
|
||||
return self.return_origin_packet(ip_src, port_src, ip_dst, port_dst, ip)
|
||||
else:
|
||||
self.packets[ip_src].append([port_src, ip_dst, port_dst, ["A"], timestamp])
|
||||
return
|
||||
return self.return_origin_packet(ip_src, port_src, ip_dst, port_dst)
|
||||
|
||||
elif flags == "RA":
|
||||
i, ip = self.find_packet_to_replace(ip_src, port_src, ip_dst, port_dst, "A")
|
||||
@ -63,10 +63,10 @@ class TCP:
|
||||
if i is not None:
|
||||
self.packets[ip][i][3].append("RA")
|
||||
self.packets[ip][i][4] = timestamp
|
||||
return
|
||||
return self.return_origin_packet(ip_src, port_src, ip_dst, port_dst, ip)
|
||||
else:
|
||||
self.packets[ip_src].append([port_src, ip_dst, port_dst, ["RA"], timestamp])
|
||||
return
|
||||
return self.return_origin_packet(ip_src, port_src, ip_dst, port_dst)
|
||||
|
||||
elif flags == "R":
|
||||
i, ip = self.find_packet_to_replace(ip_src, port_src, ip_dst, port_dst, "A")
|
||||
@ -77,10 +77,10 @@ class TCP:
|
||||
if i is not None:
|
||||
self.packets[ip][i][3].append("R")
|
||||
self.packets[ip][i][4] = timestamp
|
||||
return
|
||||
return self.return_origin_packet(ip_src, port_src, ip_dst, port_dst, ip)
|
||||
else:
|
||||
self.packets[ip_src].append([port_src, ip_dst, port_dst, ["R"], timestamp])
|
||||
return
|
||||
return self.return_origin_packet(ip_src, port_src, ip_dst, port_dst)
|
||||
|
||||
elif flags == "F":
|
||||
i, ip = self.find_packet_to_replace(ip_src, port_src, ip_dst, port_dst, "A")
|
||||
@ -88,10 +88,10 @@ class TCP:
|
||||
if i is not None:
|
||||
self.packets[ip][i][3].append("F")
|
||||
self.packets[ip][i][4] = timestamp
|
||||
return
|
||||
return self.return_origin_packet(ip_src, port_src, ip_dst, port_dst, ip)
|
||||
else:
|
||||
self.packets[ip_src].append([port_src, ip_dst, port_dst, ["F"], timestamp])
|
||||
return
|
||||
return self.return_origin_packet(ip_src, port_src, ip_dst, port_dst)
|
||||
|
||||
def find_packet_to_replace(self, ip_src, port_src, ip_dst, port_dst, flags):
|
||||
"""Cherche l'indice et le port de source du paquet dont le flag doit être remplacé"""
|
||||
@ -147,3 +147,15 @@ class TCP:
|
||||
"""Retourne la liste des paquets liés à une adresse IP, pour du déboggage"""
|
||||
|
||||
return self.packets.get(src_ip, None)
|
||||
|
||||
def return_origin_packet(self, ip_src, port_src, ip_dst, port_dst, ip = None):
|
||||
"""Retourne le paquet d'origine par rapport à l'ip de référence"""
|
||||
|
||||
if ip is None:
|
||||
return [ip_src, port_src, ip_dst, port_dst]
|
||||
else:
|
||||
if ip == ip_src:
|
||||
return [ip_src, port_src, ip_dst, port_dst]
|
||||
elif ip == ip_dst:
|
||||
return [ip_dst, port_dst, ip_src, port_src]
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user