NtopNG pour détecter les scans (V2).
Depuis plusieurs versions NtopNG utilise une petite base de données Sqlite pour stocker des données prises « au vol ». Au fil des versions le nombre de données, et de tables, a fortement augmenté. Certaines de ces données permettent de repérer les accès qui n’ont donné lieu à aucun transfert vers le « client » car ils tentaient un accès à un port fermé ou bloqué par le pare-feu. D'autres éléments indiquent des accès depuis des adresses "blacklistées".
A partir de ces données il est très facile, et rapide de repérer les scans de ports, même l’accès à un seul port est repérable. La base étant très légère (à la journée) les traitements sont très rapides et peuvent donc être exécutés souvent.
D’abord une mini description de la structure de ces bases de données situées dans /var/lib/ntopng dans une installation « « standard ».
L’organisation générale comporte 3 répertoires nommés : « -1 » « 0 » et « 1 ». chacun de ces répertoires contient plusieurs répertoires et nous nous intéresserons ici qu’à « alert ».
Chacun de ces répertoires contient un fichier, ici « alert_store_v11.db » dont le nom varie, malheureusement, à chaque version. Les noms des colonnes ont aussi varié au cours des versions …
En ouvrant ce répertoire dans SqliteBrowser on peu y voir la liste des tables et index.
Remarque : Il est normal que certains des 3 répertoires contiennent des tables vides … toute recherche doit donc concerner les trois répertoires.
La table qui nous intéresse ici est « flow_alerts » dont les colonnes suivantes nous intéressent :
- cli_ip et cli_port adresse et port de l’émetteur
- srv_ip et srv_port adresse et port du destinataire
On y trouve d’autres colonnes intéressantes :
- cli_name et srv_name les noms DNS
- cli_country et srv_country les codes pays
- cli_blacklisted et srv_blacklisted des indicateurs de qualité des adresses (0 ou 1) indiquant l’appartenance à une liste noire de l’adresse correspondante. Les listes noires sont maintenues à jour automatiquement et sont paramétrables dans l’interface Web de NtopNG, y compris leur fréquence de mise à jour?
Enfin les compteurs de la transmission en octets et paquets :
- cli2srv_bytes / cli2srv_pkts et srv2cli_bytes / srv2cli_pkts ce sont principalement ces compteurs qui nous intéressent car une valeur à zéro implique le refus de transmission qui marque un port fermé ou bloqué par une règle de firewall.
Certaines colonnes telles is_cli_attacker , is_srv_attacker ou is_srv_attacker , is_srv_victim précisent les choses par leur valeur 0 je fais partie des gentils, 1 je suis un méchant !
Il est donc très facile de lister par des ordres SQL simples les adresses IP dont les accès n’ont donné lieu à aucun transfert en retour.
Ensuite les adresses IP sont comparées aux « set » de "méchants" existants, si elles sont trouvées on abandonne car elles sont déjà bloquées, si elles ne sont pas trouvées elles sont stockées dans un set spécial d’attente, mais tout accès depuis une adresse présente dans ce set sera « dropé » par une règle iptables comme pour les adresses contenues dans les sets "permanents" de méchants.
Ce set est exploré toutes les heures et toute adresse présente depuis plus d’une heure est transférée dans un set permanent sauvegardé et détruite du set d’attente.
Le transfert dans un set permanent entraîne par ailleurs l’analyse des accès liés à cette adresse sur les dernières 24 heures et l’envoi d’un message au site ABUSEIPDB.COM avec la liste des ports touchés pour faire profiter les autres de la découverte d’un vilain méchant et horrible qui scanne internet, probablement pour un but pas très honnête ...
Toutes ces opérations sont assez simples à réunir et quelques scripts, un peu de bash, de SQL et de mise en forme avec awk font l’affaire.
Voir ci-dessous l'ordre SQL qui "va bien".
select min(first_seen),cli_ip from flow_alerts
where cli_ip not like '192.168.1%' and cli_ip not like '192.168.2%'
and cli_ip not like '192.168.3%' and cli_ip not like '192.168.4%'
and cli_ip not like '%:%' and cli_ip not like '0.0.0.0'
and srv_port < 32768
and tstamp > ${DATEMIN}
and srv_port not in (80,443)
and cli_port not in (liste de ports)
group by cli_ip
order by tstamp;
.quit
DATEMIN est obtenu depuis le script bash :
DATEMIN=$(date +%s)
DATEMIN=$((${DATEMIN} - 3600 )) # ou 7200 pour étendre ) 2 heures