NB : Une prise en main de WAZUH est un préalable à la lecture du document ci-dessous
1- Environnement de test
L’ensemble des machines virtuelles et containers sont exécutés sur un socle PROXMOX VE, y compris le routeur CISCO simulé dans un environnement GNS3 en container LXC.
La configuration de collecte des Logs et Evènements est représentée ci-dessous.
Elle est centrée (bulle bleue au sein du LAN) sur un serveur WAZUH, SIEM permettant la collecte des évènements, leur analyse et l’engagement d’actions de remédiation. Son administration est réalisée via un LapTop (seul poste de travail « physique » de l’architecture).
Sont présentes également présentes dans le LAN 3 machines Windows (cadre vert) : 1 serveur qui assurent les rôles de contrôleur de Domaine et de collecteur d’évènements (et sur lequel un agent WAZUH est installé) et 2 postes de travail (un Win 10 et un Win 11).
Enfin toujours dans le LAN : plusieurs machines Linux qui adressent leurs SysLogs à un collecteur RsysLog (cadre rouge), sur lequel un agent WAZUH est installé.
Le LAN décrit ci-dessus est raccordé à un FireWall PFsense, jouant aussi le rôel d’IDS (avec Suricata) et ayant un agent Wazuh installé.
Derrière l’interface DMZ de ce FireWall PFsense, est connecté un FireWall Linux (avec NFTables), avec agent Wazuh installé, derrière lequel est créé un réseau VxLAN au sein duquel un routeur CISCO et des machines Linux sont présentes (cadre rouge).
Et ce routeur CISCO ainsi que ces machines Linux adressent leurs SysLogs au serveur RsysLog déjà mentionné ci-dessus (avec un NAT du FireWall PFsense vers le collecteur RsysLog pour le port UDP/514) .
SCHEMA GENERAL DE L’ENVIRONNEMENT DE TEST.

2- Configurations des machines
Les fichiers de configuration NFtables et SysLogs sont très classiques et disponibles sur demande.
L’installation complémentaire d’un serveur LAMP et de l’application PHP LogAnalyser permet une bonne visualisation des Logs collectés sur le serveur RsysLog :

Pour l’installation de WAZUH, aucune difficulté en adoptant la machine virtuelle (OVA) proposée sur leur site
et qui intègrent ses 3 composantes (indexer, manager et dashboard), avec pour moteur OpenSearch (Fork AWS de ElasticSearch en 2021).

Et on installe donc les agents sur les serveurs concernés comme évoqué plus haute :


2- Scénarios de corrélation d’évènements testés
Nous avons testé les 6 scénarios ci-dessous
– Bruteforce rdp Windows
– Bruteforce ssh Linux
– Connexion clef USB
– Risque de compromission de numéro de carte VISA
– Détection de scan par IDS (Suricata)
– Succession de blocages par Firewall
A chaque fois nous avons tiré profit de la mécanique Wazuh reposant sur le principe de « decoders » qui vont permettre d’identifier et d’extraire les données pertinentes dans les logs ou journaux d’évènements et des « rules » qui vont permettre de lever des alertes selon les valeurs de ces données pertinentes.
Wazuh dispose déjà d’un ensemble de decoders et de rules très consistant, ne sont donc évoquées ci-dessous que les adaptations requises.
2.1 Bruteforce RDP Windows
Pour simuler ce bruteforce, nous allons utiliser Hydra sur une machine Kali et « attaquer » l’une des machines Windows (Win10) avec l’hypothèse que nous aurions obtenu un login (lebowski@ad.dudes.lan) mais aucun indice sur le mot de passe.
A noter bien-sûr que cet évènement sera relayé vers Wazuh par le serveur Windows, lecteur des évènements de cette machine Win10.
Pour l’exercice, nous allons donc tenter une dizaine d’accès RDP avec ce login et des mots de passes farfelus.
# hydra -L ./userwin.txt -P ./junkpasswords.txt -F rdp://win10.dudes.lan -V

Et, les decoders et rules prédéfinis de Wazuh remplissant leur rôle, une alerte de niveau 10 est levée :

Ayant configuré la notification par mail de Wazuh pour les alertes >= 10, on reçoit bien le mail ad hoc qui précise en particulier :
le login ayant fait l’objet de l’attaque, le poste de travail concerné et évidemment l’adresse IP source de l’attaque. Et ces différents champs
sont accessibles pour filtrage ou analyse statistique dans Wazuh et l’index OpenSearch « Wazuh-Alerts »

2.2 Bruteforce SSH Linux
Tout comme pour le bruteforce RDO, nous utilisons Hydra sur une machine Kali mais attaquons cette fois ci une machine Linux autorisant
le login de root en SSH.
# hydra -L ./userssh.txt -P ./junkpasswords.txt -F ssh://Heimdall -V

Là encore les decoders et rules de Wazuh fonctionnent et déclenchent une alerte de niveau 10 avec notification d’email :


2.3 Connexion clef USB
Pour alerter sur une connexion de clef USB, nous rajoutons la règle ci-dessous :
<group name= »windows-usb-detect, »>
<rule id= »111000″ level= »7″>
<if_sid>60103</if_sid>
<field name= »win.system.eventID »>^6416$</field>
<match>USBSTOR\\Disk</match>
<options>no_full_log</options>
<description>Windows: Connexion PNP de $(win.eventdata.deviceDescription) a $(win.system.computer)</description>
</rule>
</group>
Qui lève bien une alerte à la connexion d’une clef sur le LapTop Windows (sur lequel un agent a été installé aussi pour ce test) :

2.4 Risque de compromission de numero de carte VISA
Pour ce scénario, nous allons activer un audit du sous-répertoire Utilisateurs sur notre serveur Windows et rechercher dans tous les fichiers accessibles en lecture texte les chaines de caracteres pouvant correspondre à un numéro de VISA.
Pour ce faire on ajoute la commande d’audit ci-dessous dans le fichier ossec.conf, configurant l’agent Wazuh sur notre machine Windows :
<localfile>
<log_format>full_command</log_format>
<command>powershell -c « Get-ChildItem -Path ‘C:\Users\*’ -Recurse -Exclude *.dll,*.exe,*.jpg,*.png,*jpeg,*.sys,*.msi,*.dat |
Select-String ‘(\D|^)4[0-9]{3}(\ |\-|)[0-9]{4}(\ |\-|)[0-9]{4}(\ |\-|)[0-9]{4}(\D|$)’ -EV Err -EA SilentlyContinue »</command>
<alias>Visa PAN scan</alias>
<frequency>86400</frequency>
</localfile>
Et on crée la « rule » sur le serveur Wazuh qui permettra d’intercepter ce log avec le prefix « Visa PAN Scan »
<group name= »PCIDSS »>
<rule id= »101234″ level= »6″>
<if_sid>530</if_sid>
<match>^ossec: output: ‘Visa PAN scan'</match>
<description>Unmasked Card PAN discovered.</description>
<group>pci_dss_3.5.1,visa</group>
</rule>
</group>
On peut alors vérifier l’apparition de l’alerte en créant quelques fichiers .txt avec un numero de carte Visa dans le sous répertoire « Mes Documents » par exemple puis en redémarrant l’agent Wazuh

Et on constate bien l’apparition d’une alerte Wazuh, avec la liste de chaque numero de carte identifié dans le fichier concerné :

Pour aller plus loin, il faut tester avec l’algorithme de Luhn si les numéros au format VISA ci-dessus sont valides.
Pour cela une solution possible consiste en l’activation d’une Active-Response qui peut sur le Server (possible aussi sur l’agent) exécuter un script Bash qui va tester ces numéros.
On définit donc dans le fichier ‘ossec.conf’ du wazuh-server la commande ‘visa-check’ et on précise qu’elle sera déclenchée
quand la regle101234 (alerte définie ci-dessus) sera activée :
<!– pour verifier si un numero détecté au format VISA est conforme selon l’algorithme de Lunh –>
<command>
<name>visa-check</name>
<executable>visa-check.sh</executable>
<timeout_allowed>no</timeout_allowed>
</command>
<active-response>
<disabled>no</disabled>
<command>visa-check</command>
<location>server</location>
<rules_id>101234</rules_id>
</active-response>
Et il faut évidemment créer le script visa-check.sh désigné ci-dessus, et qui tournera pour cet exemple sur la machine locale.
On le créée donc dans le répertoire /var/ossec/active-responses/bin
(le script luhn_test.sh emprunté pour le test de Luhn n’est pas décrit puisqu’accessible à l’URL indiquée).
!/bin/bash
#
# Pour tester si des numeros au format VISA sont valides selon l’algorithme de Luhn
#
##################### Sources Incluses #####################
#
# From https://ethertubes.com/bash-luhn/
# Returns Luhn checksum for supplied sequence
#
############################################
#
# Traitement de l’alerte Wazuh
#
############################################
# Donnees de sorties vers le fichier active-responses.log
TMP_FILE= »/tmp/alert.txt »
LOG_FILE= »logs/active-responses.log »
#
# Lecture des donnees presentes dans le JSON de l’alerte (et ecriture dans fichier temporaire)
read -r INPUT_JSON
FULL_LOG=$(echo $INPUT_JSON | jq -r .parameters.alert.full_log)
echo « $FULL_LOG » > ${TMP_FILE}
#
# Tableau pour stocker les lignes, reprises du fichier temporaire
lines=()
Nb_lines=0
while IFS= read -r line; do
# Ajouter la ligne au tableau
lines+=(« $line »)
((Nb_lines++))
done < « $TMP_FILE »
# Recherche du numero de visa dans chaque ligne
Nb_numcbs=$((Nb_lines-1))
numcbs=()
i=1
while [ $i -le $((Nb_numcbs+1)) ]; do
numcbs+=(« $(echo « ${lines[${i}]} » | cut -d’:’ -f4) »)
((i++))
done
# Test Luhn et Ecriture dans fichier temporaire des numeros pour test
echo -n « $Nb_numcbs numeros de VISA detectés dont ceux-ci invalides » > /tmp/junk.txt
Reponse= »ossec: output: ‘Valid Visa Number’ Dude «
i=0
val=0
while [ $i -lt $Nb_numcbs ]; do
numcb=(« $(echo ${numcbs[${i}]} | xargs) »)
if luhn_test « $numcb » ; then
# Numero de carte VISA Valide
Reponse+= »-$numcb »
val=1
else
# pour debug :
# Numero de carte VISA Invalide
echo -n « -$numcb » >> /tmp/junk.txt
fi
((i++))
done
# pour debug :
echo « $Reponse » > /tmp/cbs.txt
#Ecriture dans fichier active-responses.log
if val==1 ; then
echo « $Reponse » >> ${LOG_FILE}
fi
Et on ajoute une règle qui va traiter ces données une fois collectées via le fichier active-responses.log :
<rule id= »101235″ level= »10″>
<if_sid>530</if_sid>
<match>^ossec: output: ‘Valid Visa Number'</match>
<description>Visa Card Valid Number discovered.</description>
<group>pci_dss_3.5.1,visa</group>
</rule>
</group>
Et ceci permet donc à Wazuh de vérifier directement si les numéros au format Visa identifiés sont conformes, avec une alerte en conséquence :

Qui peut être transcrite en notification Email explicite :

2.5 Détection de Scan avec Suricata
Pour bien exploiter les logs venant de Suricata (installé via package sur PFsense), on modifie le decoder exstant de Wazuh (qui ne renseigne pas le champ standard ‘srcip’ mais utilise la variante ‘src_ip’, non interprétée par les alertes prédéfinies) :
<decoder name= »json »>
<prematch>^{\s* »</prematch>
</decoder>
<decoder name= »json_child »>
<parent>json</parent>
<regex type= »pcre2″> »src_ip »: »([^ »]+) »</regex>
<order>srcip</order>
</decoder>
<decoder name= »json_child »>
<parent>json</parent>
<plugin_decoder>JSON_Decoder</plugin_decoder>
</decoder>
Et on crée la règle ad-hoc pour être alerté si Scan :
<group name= »ids,suricata, »>
<rule id= »86602″ level= »9″ overwrite= »yes »>
<if_sid>86601</if_sid>
<match>ET SCAN </match>
<description>Suricata: Succession de scans de type Nmap, venant de [$(srcip)] Dude </description>
<mitre>
<id>T1595</id>
</mitre> <group>multiple_scans,pci_dss_1.4,pci_dss_10.6.1,gpg13_4.12,hipaa_164.312.a.1,hipaa_164.312.b,nist_800_53_SC.7,nist_800_53_AU.6,tsc_CC6.7,tsc_CC6.8,tsc_CC7.2,tsc_CC7.3,</group>
</rule>
</group>
Une fois redémarré le server Wazuh, et lancé un nouveau scan NMAP on a bien notre alerte Suricata :

Et ces alertes peuvent être compilées via un dashboard OpenSearch, ci-dessous avec l’histogramme des alertes Suricata sur 48h par ex :

2.6 Succession de blocages par firewall
Nous allons envoyer des requetes sur un millier de ports du firewall en procédant tout simplement avec une commande NMAP :
# nmap -T4 -A -v 192.168.1.253 (cette adresse correspond à l’adresse WAN du Firewall PFsense)
Mais pour que cela fonctionne avec PFsense, nous devons modifier le decoder correspondant car celui fourni avec Wazuh est obsolete.
(nous avons également adapté les decoders et rules pour améliorer le traitement des alertes NFtables, meme principe que pour PFsense)
Celui ci-dessous convient très bien :
<!– identifier les Logs venant d’un Firewall PFsense –>
<decoder name= »pf-Firewall »>
<prematch type= »pcre2″>filterlog</prematch>
</decoder>
<!– recuperer les champs venant d’un Firewall PFsense –>
<decoder name= »pf-Firewall-Fields »>
<parent>pf-Firewall</parent>
<regex type= »pcre2″>.*?- – .+?(?=,).+?(?=,).+?(?=,),(.+?(?=,)),(.+?(?=,)),.+?(?=,),(.+?(?=,)),</regex>
<order>id,interface,action</order>
</decoder>
<decoder name= »pf-Firewall-Fields »>
<parent>pf-Firewall</parent>
<regex type= »pcre2″ offset= »after_regex »>.+?(?=,),.+?(?=,),.+?(?=,),.+?(?=,),.+?(?=,),.+?(?=,),.+?(?=,),.+?(?=,),(.+?(?=,)),.+?(?=,),(.+?(?=,)),(.+?(?=,)),(.+?(?=,)),(.+?(?=,)),</regex>
<order>protocol,srcip,dstip,scrport,dstport</order>
</decoder>
Et nous devons également recréer une règle d’alerte :
<group name= »firewall, »>
<rule id= »100100″ level= »0″>
<decoded_as>pf-Firewall</decoded_as>
<description>pfSense firewall Dudes rules grouped.</description>
</rule>
<rule id= »100101″ level= »3″>
<if_sid>100100</if_sid>
<action>block</action>
<description>Paquet bloqué par PFsense</description>
<group>firewall_drop,pci_dss_1.4,gpg13_4.12,hipaa_164.312.a.1,nist_800_53_SC.7,tsc_CC6.7,tsc_CC6.8,</group>
</rule>
<rule id= »100102″ level= »9″ frequency= »20″ timeframe= »10″ ignore= »20″>
<if_matched_sid>100101</if_matched_sid>
<same_source_ip />
<description>Succession de blocked/dropped vers l’adresse [$(dstip)] et venant de [$(srcip)]</description>
<mitre>
<id>T1110</id>
</mitre>
group>multiple_blocks,pci_dss_1.4,pci_dss_10.6.1,gpg13_4.12,hipaa_164.312.a.1,hipaa_164.312.b,nist_800_53_SC.7,nist_800_53_AU.6,tsc_CC6.7,tsc_CC6.8,tsc_CC7.2,tsc_CC7.3,</group>
</rule>
</group>
Et l’alerte levée durant le scan prouve que cela fonctionne (ainsi d’ailleurs que la Géolocalisation Suricata que nous avons activée…) :

On peut créer une visualisation dans le dashboard OpenSearch pour représenter toutes ces alertes par source ip, sur 48h par ex :

Et avec OpenSearch, une multitude d’autres possibilités de corrélations existent.
A titre d’exemple :
Activons une alerte pour que toutes les 5 minutes soit vérifié si des blocages multiples des firewalls ont eu lieu et s’ils ont concernés plusieurs adresses de destination.
Pour cela, crééons un « monitor » dans le mode Alerting d’OpenSearch :

Avec le script Query-DSL ci-après :
{
« size »: 0,
« query »: {
« bool »: {
« filter »: [
{
« match_all »: {
« boost »: 1
}
},
{
« match_phrase »: {
« rule.groups »: {
« query »: « multiple_blocks »,
« slop »: 0,
« zero_terms_query »: « NONE »,
« boost »: 1
}
}
},
{
« range »: {
« timestamp »: {
« from »: « now-5m »,
« to »: « now »,
« include_lower »: false,
« include_upper »: true,
« format »: « strict_date_optional_time »,
« boost »: 1
}
}
}
],
« adjust_pure_negative »: true,
« boost »: 1
}
},
« aggregations »: {
« list_dst_ips »: {
« terms »: {
« field »: « data.dstip »,
« size »: 10,
« min_doc_count »: 1,
« shard_min_doc_count »: 0,
« show_term_doc_count_error »: false,
« order »: [
{
« _count »: « desc »
},
{
« _key »: « asc »
}
]
}
},
« unique_dst_ips »: {
« cardinality »: {
« field »: « data.dstip »
}
}
}
}
Ce script va être exécuté toutes les 5 minutes à la rechercher des alertes évoquant « mulitple_blocks » dans les 5 minutes précédentes
et va « agréger » le résultat de recherche par « dst_ips », en procédant aussi à leur décompte.
Il reste alors à définir la condition qui devra être remplie pour que l’alerte soit déclenchée.
En l’occurence, au moins 2 adresses dst_ips distinctes :

Dernière chose, préciser le mode de notification (Slack dans cet exemple, dont le canal de notification a déjà été configuré) et format le texte du message d’alerte :

Et nous obtenons ainsi une alerte explicite si nous lançons un Nmap sur nos deux firewalls (PFsense – 192.168.1.253 et NFtables – 192.168.1.123) :

Et nous avions bien-sûr aussi ces 2 alertes distinctes dans le tableau de bord des évènements Wazuh :

La grande puissance d’OpenSearch tient notamment à ces possibilités de trier, de filtrer et d’agréger ces alertes.
Références utiles sur le Net :




