Merge branch 'main' into new_functionality

This commit is contained in:
2024-11-26 10:15:11 -05:00
22 changed files with 342 additions and 165 deletions

View File

@ -2,8 +2,9 @@ FROM python:alpine3.20
# Installation des paquets nécessaires pour scapy
RUN apk -U upgrade && \
apk add --no-cache libpcap libpcap-dev gcc musl-dev libffi-dev nmap iproute2
RUN pip install scapy
apk add --no-cache nmap iproute2
RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
RUN apk -U add --no-cache hping3
COPY Demo/Dockerfiles/attaquant-entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

View File

@ -1,15 +1,9 @@
FROM python:alpine3.20
FROM httpd:alpine
# Installation des paquets nécessaires pour scapy
RUN apk -U upgrade && \
apk add --no-cache libpcap libpcap-dev gcc musl-dev libffi-dev iproute2
RUN pip install scapy
# Installation des paquets nécessaire pour le routage
RUN apk -U upgrade && apk add --no-cache iproute2
COPY Demo/Dockerfiles/cible-entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
# Copier le script de détection d'attaques
#COPY cible.py /cible.py
# Lancer le script de détection
#CMD ["python", "/cible.py"]
CMD ["httpd-foreground"]

View File

@ -16,7 +16,7 @@ WORKDIR /app
COPY idps /app/idps
# Copie du fichier de configuration
COPY config.json /app/config.json
COPY Demo/config/config-idps.json /app/config.json
# Utiliser le script comme point d'entrée
ENTRYPOINT ["/entrypoint.sh"]

View File

@ -12,7 +12,7 @@ WORKDIR /app
COPY idps /app/ids
# Copie du fichier de configuration
COPY config.json /app/config.json
COPY Demo/config/config-ids.json /app/config.json
# Lancer le script de la sonde IDS
CMD ["python3", "/app/ids/ids.py"]
CMD ["python3", "/app/ids/main.py"]

View File

@ -0,0 +1,41 @@
{
"rules_dirpath": "/app/idps/rules",
"ifaces": ["eth1"],
"db_host": "172.20.3.10",
"db_database": "sidps",
"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_bantime": 300
}

View File

@ -0,0 +1,41 @@
{
"rules_dirpath": "/app/ids/rules",
"ifaces": ["br-c56b595383ad"],
"db_host": "172.20.3.10",
"db_database": "sidps",
"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_bantime": 300
}

View File

@ -20,7 +20,6 @@ services:
context: ..
dockerfile: Demo/Dockerfiles/Dockerfile.idps
container_name: idps
command: sleep infinity
cap_add:
- NET_ADMIN
- NET_RAW
@ -39,7 +38,6 @@ services:
context: ..
dockerfile: Demo/Dockerfiles/Dockerfile.cible
container_name: cible
command: sleep infinity
cap_add:
- NET_ADMIN
networks:
@ -67,7 +65,6 @@ services:
context: ..
dockerfile: Demo/Dockerfiles/Dockerfile.ids
container_name: ids
command: sleep infinity
cap_add:
- NET_ADMIN
- NET_RAW

View File

@ -55,11 +55,20 @@ Pour lancer cette démonstration, il faudra avoir `docker` & `docker compose` d'
```bash
cd Demo/
docker compose up -d
./deploy.sh
```
## TODO
La cible (172.20.2.3) héberge un serveur web apache avec la page par défaut sur son port 80.
Les conteneurs attaquants disposent tous les deux de nmap et de hping3 pour réaliser des scan et des floods / DOS.
Rappel des commandes pour flood avec et sans charge utile avec hping3:
```bash
hping3 -S --flood IP
hping3 -S --flood -d TAILLE IP
```
## TODO:
- Noyau d'analyse de l'IDS
- Interface web pour visualiser les alertes / rechercher dedans
- Moteur de corrélation des alertes (récupération + renvoi dans MySQL).
- Moteur de détection par comportement

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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