LINUX:Wazuh-Décodeurs et Règles

De WIKI sur Linux (ADB)
Révision datée du 22 novembre 2022 à 22:59 par Adebast (discussion | contributions) (Adebast a déplacé la page LINUX:Wazuh: Décodeurs et Règles vers LINUX:Wazuh-Décodeurs et Règles sans laisser de redirection)
(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)
Aller à la navigation Aller à la recherche

retour à Wazuh: HIDS


But

Le serveur manager de Wazuh récolte les journaux auprès des agents. Ces journaux sont ensuite analysés. Une première phase consiste à décoder le journal pour y repérer des informations utiles. Sur cette base, un ensemble de règles analysent cette information prédigérée pour au final émettre un alarme graduée de 0 à 16, le niveau 0 étant l’absence d'alarme.


Le logiciel vient avec un grand ensemble de décodeurs et de règles. Mais il se peut que vous soyez confronté à un problème ou que vous souhaitiez refouler des attaques intempestives malgré qu'elles ne soient pas problématiques. Nous allons donner quelques exemples pratiques qui permettent de mieux comprendre le fonctionnement de cet écosystème.


Emplacement

Les décodeurs et les règles sont réparties en deux ensembles, celles qui viennent avec le logiciel et celles que vous créez.

Voici les répertoires où elles se situent:

  • "/var/ossec/ruleset/decoders" : les décodeurs du logiciel
  • "/var/ossec/ruleset/rules"  : les règles du logiciel
  • "/var/ossec/etc/decoders"  : les décodeurs personnels
  • "/var/ossec/etc/rules"  : les règles personnelles

Il est important de ne jamais modifier les fichiers venant avec le logiciel car vous risquez de perdre vos modifications lors d'une mise à jour. On utilise l'espace personnel.


Utilitaires

Il existe un utilitaire qui permet de tester interactivement les réactions des décodeurs et des règles. Normalement toute modification de décodeur ou de règle ne nécessite pas de redémarrage du service "wazuh-manager.service" mais j'ai remarqué que tout suppression ou renommage d'un de ces fichiers mettait en erreur cet utilitaire. Par contre, les modifications ne le perturbent pas, pour autant qu'il soit relancé.

Cet utilitaire est:

/var/ossec/bin/wazuh-logtest

L'option "-h" présente l'aide. Le lancement sans option suffit.

L'ancien utilitaire obsolète était plus utile car il donnait le cheminement des règles successives utilisées, ce qui aide grandement l'élaboration d'une règle. Il est encore disponible sous le nom:

/var/ossec/bin/wazuh-logtest-legacy -v

L'option "-v" apporte ce cheminement. Il faudrait cette même approche pour les décodeurs.


1er cas

J'ai un serveur Web. On remarque rapidement que certains essaient d'accéder à des URLs qui donneraient accès à des données fondamentales. Par exemple au produit PHPMyAdmin qui donne accès à des bases de données MySql. Nous allons créer une règle qui permet de les identifier et de les bloquer par le firewall.


Requête type du journal d'HTTP

Dans notre journal "/var/log/access_log", nous sélectionnons quelques exemples qui nous permettrons de tester notre règle.

En voici quatre:


139.5.145.26 - - [24/Feb/2022:05:16:39 +0100] "GET /phpmyadmin2019/index.php?lang=en HTTP/1.1" 301 267 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36"
 
205.185.125.167 - - [20/Feb/2022:19:19:29 +0100] "GET /phpmyadmin/scripts/setup.php HTTP/1.1" 301 263 "-" "ZmEu"
 
147.139.195.52 - - [09/Jan/2022:20:05:09 +0100] "GET /phpMyAdmin-5.1.0-english/index.php?lang=en HTTP/1.1" 301 277 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36"
 
137.184.120.250 - - [28/Jan/2022:22:01:53 +0100] "GET /phpmyadmin/index.php HTTP/1.1" 404 196 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"

Nous remarquons la diversité des requêtes. De plus nous remarquons la diversité des n° de message d'erreur d'HTTP. Dans trois cas, le code "301" signifie une redirection, ce qui est normal car le serveur redirige d'office les requêtes de type "http://" vers "https://"; tous sites devraient utiliser une connexion sécurisée par certificat. Le code "301" n'est donc pas une erreur à la base car il peut conduire par la suite à un code "200" pour une URL correcte, existante. La dernière ("404") correspond à une requête sécurisée car non redirigée mais dont l'URL n'existe pas.


Evaluation

Nous commençons par lancer la commande de test avant tout ajout de règle pour observer la réaction de Wazuh. Quand on a lancé la commande, on colle la ligne du journal que nous voulons tester. Nous allons utiliser la première requête pour nos tests.


On lance la commande:

/var/ossec/bin/wazuh-logtest

Résultat:


Starting wazuh-logtest v4.2.5
Type one log per line
 
139.5.145.26 - - [24/Feb/2022:05:16:39 +0100] "GET /phpmyadmin2019/index.php?lang=en HTTP/1.1" 301 267 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36"
 
**Phase 1: Completed pre-decoding.
       full event: '139.5.145.26 - - [24/Feb/2022:05:16:39 +0100] "GET /phpmyadmin2019/index.php?lang=en HTTP/1.1" 301 267 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36"'
**Phase 2: Completed decoding.
       name: 'web-accesslog'
       id: '301'
       protocol: 'GET'
       srcip: '139.5.145.26'
       url: '/phpmyadmin2019/index.php?lang=en'
**Phase 3: Completed filtering (rules).
       id: '31100'
       level: '0'
       description: 'Access log messages grouped.'
       groups: '['web', 'accesslog']'
       firedtimes: '1'
       mail: 'False'

La phase de décodage a permis d'identifier le n° de message d'erreur d'HTTP ("id"), l'adresse IP de la source de la requête ("srcip") et la requête proprement dite ("url").

La phase suivante affiche le résultat de la règle finale qui a été appliquée. Chaque règle a un n° unique; celle utilisée est la "31100" et le niveau d'alarme est "0" donc il n'y a pas d'alarme or nous désirons qu'il soit au moins de "7".


Règles du logiciel

Celle règle existante est à chercher dans le répertoire adapté "/var/ossec/ruleset/rules". Une rapide recherche avec une commande "grep", nous informe qu'elle se trouve dans le fichier "0245-web_rules.xml" dont voici un extrait:


<group name="web,accesslog,">
  <rule id="31100" level="0">
    <category>web-log</category>
    <description>Access log messages grouped.</description>
  </rule>
  
  <rule id="31108" level="0">
    <if_sid>31100</if_sid>
    <id>^2|^3</id>
    <compiled_rule>is_simple_http_request</compiled_rule>
    <description>Ignored URLs (simple queries).</description>
  </rule>
  
  <rule id="31101" level="5">
    <if_sid>31100</if_sid>
    <id>^4</id>
    <description>Web server 400 error code.</description>
    <group>attack,pci_dss_6.5,pci_dss_11.4,gdpr_IV_35.7.d,nist_800_53_SA.11, nist_800_53_SI.4, tsc_CC6.6,tsc_CC7.1,tsc_CC8.1,tsc_CC6.1,tsc_CC6.8,tsc_CC7.2,tsc_CC7.3,</group>
  </rule>
 </group> 

C'est la première qui a été déclenchée car lors de son pré-décodage, il a repéré que c'était une requête de type WEB ("<category>web-log</category>"); le niveau est "0" ("level=0").

La seconde règle aurait pu être déclenchée mais elle n'est pas d'un niveau supérieur. Cette seconde règle fait suite à la première ("<if_sid>31100</if_sid>"); elle n'est évaluée que si la première est validée. La condition "<id>^2|^3</id>" signifie qu'elle est validée pour tout message d'erreur d'HTTP commençant par "2" ou (barre verticale les séparant) "3" (le chapeau signifiant "commençant par").

Pour faire bonne mesure, nous avons ajouté une troisième règle qui dépend de la première et qui concerne tout message d'erreur commençant par "4", ce qui est le cas de notre quatrième entrée du journal.


Règle personnelle

Nous allons donc concevoir une règle sur base de ces informations.

Dans le répertoire "/var/ossec/etc/rules" reprenant les règles personnelles, nous créons le fichier "perso-web_rules.xml" dans laquelle on ajoute la règle suivante:


<group name="web,accesslog,">
  <rule id="100105" level="7">
    <if_sid>31100, 31101,  31108</if_sid>
    <url>^/phpMyAdmin|^/phpmyadmin</url>
    <description>Web attaque 1 (ADB).</description>
  </rule>
 </group> 

Toute règle doit avoir un n° unique sinon le service "wzud-manager.service" ne démarre pas. Nous avons choisi le n° "100105" de niveau "7".

Cette règle ne sera évaluée que si une des trois règles de base est validée ("<if_sid>31100, 31101, 31102</if_sid>"). Ensuite le champs "url" doit commencer par "/phpmyadmin" ou par "/phpMyAdmin". Nous avons ajouté cette variante également rencontrée. (Unix est sensible à la case au contraire de Windows)


Validation

Après l'ajout de la règle, on lance de nouveau la commande pour vérification:

/var/ossec/bin/wazuh-logtest

Résultat:


Starting wazuh-logtest v4.2.5
Type one log per line
 
139.5.145.26 - - [24/Feb/2022:05:16:39 +0100] "GET /phpmyadmin2019/index.php?lang=en HTTP/1.1" 301 267 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36"
 
**Phase 1: Completed pre-decoding.
       full event: '139.5.145.26 - - [24/Feb/2022:05:16:39 +0100] "GET /phpmyadmin2019/index.php?lang=en HTTP/1.1" 301 267 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36"'
**Phase 2: Completed decoding.
       name: 'web-accesslog'
       id: '301'
       protocol: 'GET'
       srcip: '139.5.145.26'
       url: '/phpmyadmin2019/index.php?lang=en'
**Phase 3: Completed filtering (rules).
       id: '100105'
       level: '7'
       description: 'Web attaque 1 (ADB).'
       groups: '['web', 'accesslog']'
       firedtimes: '1'
       mail: 'True'
**Alert to be generated.

Notre règle a bien été déclenchée.


Pour comparaison, on lance l'ancienne commande:

/var/ossec/bin/wazuh-logtest-legacy -v

Résultat:


2022/03/02 16:40:01 wazuh-testrule: INFO: Started (pid: 2712892).
Since Wazuh v4.1.0 this binary is deprecated. Use wazuh-logtest instead
wazuh-testrule: Type one log per line.
 
139.5.145.26 - - [24/Feb/2022:05:16:39 +0100] "GET /phpmyadmin2019/index.php?lang=en HTTP/1.1" 301 267 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36"
 
**Phase 1: Completed pre-decoding.
      full event: '139.5.145.26 - - [24/Feb/2022:05:16:39 +0100] "GET /phpmyadmin2019/index.php?lang=en HTTP/1.1" 301 267 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36"'
      timestamp: '(null)'
      hostname: 'serverdb'
      program_name: '(null)'
      log: '139.5.145.26 - - [24/Feb/2022:05:16:39 +0100] "GET /phpmyadmin2019/index.php?lang=en HTTP/1.1" 301 267 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36"'
**Phase 2: Completed decoding.
      decoder: 'web-accesslog'
      srcip: '139.5.145.26'
      protocol: 'GET'
      url: '/phpmyadmin2019/index.php?lang=en'
      id: '301'
**Rule debugging:
   Trying rule: 4 - Generic template for all web rules.
      *Rule 4 matched.
      *Trying child rules.
   Trying rule: 31100 - Access log messages grouped.
      *Rule 31100 matched.
      *Trying child rules.
   Trying rule: 31108 - Ignored URLs (simple queries).
   Trying rule: 31511 - Blacklisted user agent (wget).
   Trying rule: 31115 - URL too long. Higher than allowed on most browsers. Possible attack.
   Trying rule: 31103 - SQL injection attempt.
   Trying rule: 100105 - Web attaque 1 (ADB).
      *Rule 100105 matched.
**Phase 3: Completed filtering (rules).
      Rule id: '100105'
      Level: '7'
      Description: 'Web attaque 1 (ADB).'
**Alert to be generated.

On a ici plus de détails sur le cheminement.


Activation

Il nous reste à ajouter ce n° de règle "100105" au fichier de configuration "/var/ossec/etc/ossec.conf" de notre serveur à la section "<active-response>", option "<rules_id>" comme vu précédemment. Noublions pas de relancer le service.


2ème cas

Une attaque de premier niveau consiste à repérer les ports réseaux ouverts sur un serveur; pour y parvenir, on utilise un programme particulier, un "Port Scanner". Au niveau du firewall, il est facile d'ajouter une règle ayant une action "-j LOG" pour les repérer; il ajoute alors une entrée dans le journal d'IPTABLES; dans mon cas, ce fichier se nomme "/var/log/iptables". On va donc sur base de ce message bloquer cette personne.


Requête type du journal d'IPTABLES

Voici un exemple d'entrée dans le journal "/var/log/iptables":


Feb 15 23:11:10 serverdb kernel: PORTSCAN(I): IN=enp0s25 OUT= MAC=00:1c:c0:79:7c:12:30:91:8f:ba:ce:62:08:00 SRC=192.241.220.106 DST=192.168.1.110 LEN=40 TOS=0x00 PREC=0x00 TTL=47 ID=18882 DF PROTO=TCP SPT=52119 DPT=995 WINDOW=0 RES=0x00 RST URGP=0 


Evaluation

Comme pour le cas précédent, nous allons utiliser la commande:

/var/ossec/bin/wazuh-logtest

Résultat:


Starting wazuh-logtest v4.2.5
Type one log per line
 
Feb 15 23:11:10 serverdb kernel: PORTSCAN(I): IN=enp0s25 OUT= MAC=00:1c:c0:79:7c:12:30:91:8f:ba:ce:62:08:00 SRC=192.241.220.106 DST=192.168.1.110 LEN=40 TOS=0x00 PREC=0x00 TTL=47 ID=18882 DF PROTO=TCP SPT=52119 DPT=995 WINDOW=0 RES=0x00 RST URGP=0
 
**Phase 1: Completed pre-decoding.
       full event: 'Feb 15 23:11:10 serverdb kernel: PORT-SCAN(I): IN=enp0s25 OUT= MAC=00:1c:c0:79:7c:12:30:91:8f:ba:ce:62:08:00 SRC=192.241.220.106 DST=192.168.1.110 LEN=40 TOS=0x00 PREC=0x00 TTL=47 ID=18882 DF PROTO=TCP SPT=52119 DPT=995 WINDOW=0 RES=0x00 RST URGP=0 '
       timestamp: 'Feb 15 23:11:10'
       hostname: 'serverdb'
       program_name: 'kernel'
**Phase 2: Completed decoding.
       name: 'kernel'
       parent: 'kernel'
       action: 'PORTSCAN(I):'
       dstip: '192.168.1.110'
       dstport: '995'
       protocol: 'TCP'
       srcip: '192.241.220.106'
       srcport: '52119'
**Phase 3: Completed filtering (rules).
       id: '4100'
       level: '0'
       description: 'Firewall rules grouped.'
       groups: '['firewall']'
       firedtimes: '1'
       mail: 'False'

On constate que l'adresse IP de la source est bien détectée ("srcip"). C'est une règle ("4100") de type firewall qui s'active mais son niveau est "0" sans provoquer d'alarme. Le message spécifique du journal d'IPTABLES est bien repéré sous le champs "action".


Règle du logiciel

Cette règle se retrouve dans le fichier "/var/ossec/ruleset/rules/0900-firewall_rules.xml". Ce fichier ne comprend que quelques règles.

Voici un extrait:


<group name="firewall,">
 <rule id="4100" level="0">
   <category>firewall</category>
   <description>Firewall rules grouped.</description>
 </rule>
</group>


Règle personnelle

On va donc créer un fichier qui va contenir nos règles personnelles liées au firewall. On le nommera "/var/ossec/etc/rules/perso-fw_rules.xml". Ici c'est le champs "action" qui va nous servir à activer notre règle. Cette règle fera suite à la détection préalable faite par la règle "4100". Le niveau d'alarme est "7".

Voici cette règle:


<group name="firewall,">
 <rule id="100404" level="7">
   <if_sid>4100</if_sid>
   <action>PORTSCAN(I):</action>
   <description>Firewall 1-Ports Scanner.</description>
 </rule>
</group>


Validation

On passe à l'étape évaluation. On lance la commande:

/var/ossec/bin/wazuh-logtest

Résultat:


Starting wazuh-logtest v4.2.5
Type one log per line
 
Feb 15 23:11:10 serverdb kernel: PORTSCAN(I): IN=enp0s25 OUT= MAC=00:1c:c0:79:7c:12:30:91:8f:ba:ce:62:08:00 SRC=192.241.220.106 DST=192.168.1.110 LEN=40 TOS=0x00 PREC=0x00 TTL=47 ID=18882 DF PROTO=TCP SPT=52119 DPT=995 WINDOW=0 RES=0x00 RST URGP=0
 
**Phase 1: Completed pre-decoding.
       full event: 'Feb 15 23:11:10 serverdb kernel: PORTSCAN(I): IN=enp0s25 OUT= MAC=00:1c:c0:79:7c:12:30:91:8f:ba:ce:62:08:00 SRC=192.241.220.106 DST=192.168.1.110 LEN=40 TOS=0x00 PREC=0x00 TTL=47 ID=18882 DF PROTO=TCP SPT=52119 DPT=995 WINDOW=0 RES=0x00 RST URGP=0'
       timestamp: 'Feb 15 23:11:10'
       hostname: 'serverdb'
       program_name: 'kernel'
**Phase 2: Completed decoding.
       name: 'kernel'
       parent: 'kernel'
       action: 'PORTSCAN(I):'
       dstip: '192.168.1.110'
       dstport: '995'
       protocol: 'TCP'
       srcip: '192.241.220.106'
       srcport: '52119'
**Phase 3: Completed filtering (rules).
       id: '100404'
       level: '7'
       description: 'Firewall 1-Ports Scanner.'
       groups: '['firewall']'
       firedtimes: '1'
       mail: 'True'
**Alert to be generated.

Notre règle a bien été déclenchée.


Activation

Il nous reste à ajouter ce n° de règle "100404" au fichier de configuration "/var/ossec/etc/ossec.conf" de notre serveur à la section "<active-response>", option "<rules_id>" comme vu précédemment. Noublions pas de relancer le service.


3ème cas

On désire être averti dès qu'un "file system" arrive à 90%.


Commande

Dans les fichiers "/var/ossec/etc/ossec.conf" des agents local et distants, il existe une commande qui permet d'analyser les espaces disques avec la commande "df -P".


 <localfile>
   <log_format>command</log_format>
   <command>df -P</command>
   <frequency>360</frequency>
 </localfile>


Résultat de commande

Cette commande "df -P" génère un ensemble de lignes; chaque ligne est précédée de "ossec: output: 'df -P': ". Chaqque ligne est analysée séparément.

Voici un exemple:


ossec: output: 'df -P': /dev/mapper/vgserverdb-lvserverdb       488143996 436452180   51691816      91% /disk1


Evaluation

Comme dans les autres cas, on lance la commande sur base de cet exemple:

/var/ossec/bin/wazuh-logtest

Résultat:


Starting wazuh-logtest v4.2.5
Type one log per line
 
ossec: output: 'df -P': /dev/mapper/vgserverdb-lvserverdb       488143996 436452180   51691816      91% /disk1
 
**Phase 1: Completed pre-decoding.
       full event: 'ossec: output: 'df -P': /dev/mapper/vgserverdb-lvserverdb       488143996 436452180   51691816      91% /disk1'
**Phase 2: Completed decoding.
       name: 'ossec'
**Phase 3: Completed filtering (rules).
       id: '530'
       level: '0'
       description: 'OSSEC process monitoring rules.'
       groups: '['ossec', 'process_monitor']'
       firedtimes: '1'
       mail: 'False'

C'est la règle "530" qui réagit mais sans alarme.


Règles du logiciel

On trouve cette règle dans le fichier "/var/ossec/ruleset/rules/0015-ossec_rules.xml".

Voici un extrait:


<group name="ossec,">
 <rule id="500" level="0">
   <category>ossec</category>
   <decoded_as>ossec</decoded_as>
   <description>Grouping of ossec rules.</description>
 </rule>
 <rule id="530" level="0">
   <if_sid>500</if_sid>
   <match>^ossec: output: </match>
   <description>OSSEC process monitoring rules.</description>
   <group>process_monitor,</group>
 </rule>
 <rule id="531" level="7" ignore="7200">
   <if_sid>530</if_sid>
   <match>ossec: output: 'df -P': /dev/</match>
   <regex>100%</regex>
   <description>Partition usage reached 100% (disk space monitor).</description>
   <group>low_diskspace,pci_dss_10.6.1,gpg13_10.1,gdpr_IV_35.7.d,hipaa_164.312.b,nist_800_53_AU.6,tsc_CC7.2,tsc_CC7.3,</group>
 </rule>
 <rule id="532" level="0">
   <if_sid>531</if_sid>
   <match>cdrom|/media|usb|/mount|floppy|dvd</match>
   <description>Ignoring external medias.</description>
 </rule>
</group>

On remarque qu'il existe une règle au cas où le disque est saturé (100%) mais les médias amovibles ou externes sont exclus par la règle suivante qui annule toute alarme.


Règle personnelle

Sur cette base, on conçoit une règle où on recherche un poucentage de deux digits commençant par "9". Cette recherche est exprimée par la chaîne "9\d%".

Dans le répertoire "/var/ossec/etc/rules" reprenant les règles personnelles, nous créons le fichier "perso-os_rules.xml" dans laquelle on ajoute la règle suivante:


<group name="ossec,">
 <rule id="100101" level="7" ignore="7200">
   <if_sid>530</if_sid>
   <match>ossec: output: 'df -P': /dev/</match>
   <regex>9\d%</regex>
   <description>Partition usage reached 90% (disk space monitor).</description>
   <group>low_diskspace,</group>
 </rule>
</group>.


Validation

Après l'ajout de la règle, on lance de nouveau la commande pour vérification:

/var/ossec/bin/wazuh-logtest

Résultat:


Starting wazuh-logtest v4.2.5
Type one log per line
 
ossec: output: 'df -P': /dev/mapper/vgserverdb-lvserverdb       488143996 436452180   51691816      91% /disk1
 
**Phase 1: Completed pre-decoding.
       full event: 'ossec: output: 'df -P': /dev/mapper/vgserverdb-lvserverdb       488143996 436452180   51691816      91% /disk1'
**Phase 2: Completed decoding.
       name: 'ossec'
**Phase 3: Completed filtering (rules).
       id: '100101'
       level: '7'
       description: 'Partition usage reached 90% (disk space monitor).'
       groups: '['ossec']'
       firedtimes: '1'
       mail: 'True'
**Alert to be generated.

Notre règle a bien été déclenchée.


Activation

Cet alarme va déclencher une alerte mail. Dans ce cas, une action dans le firewall n'a aucun sens.


4ème cas

Sur une machine, j'ai installé le logiciel Nagios, programme de surveillance du matériel et des services à travers le réseau. Sur cette machine existe un serveur de messagerie comprenant les protocoles POP3, POP3S, IMAP et IMPAPS au travers du logiciel DOVECOT. Ces services ont logiquement été mis sous surveillance à intervals réguliers. Dès ce moment, quatre alarmes de niveau 5 intempestives sont apparues et répétées régulièrement. Nous avons voulu remettre ces alarmes à un niveau 0 pour cet émetteur et ce récepteur bien précis, identifiés par leurs adresses IP. Deux approches sont présentées.


Requête type du journal de la messagerie

Voici quatre exemples d'entrées dans le journal "/var/log/mail":


Mar  1 20:13:14 serverdb dovecot[1034]: pop3-login: Disconnected: Aborted login by logging out (no auth attempts in 0 secs): user=<>, rip=192.168.1.110, lip=192.168.1.110, secured, session=<rKDy9CzZIsTAqAE8>
 
Mar  1 20:12:01 serverdb dovecot[1034]: imap-login: Disconnected: Aborted login by logging out (no auth attempts in 0 secs): user=<>, rip=192.168.1.110, lip=192.168.1.110, secured, session=<PwmX8CzZUqHAqAE8>
 
Mar  1 20:05:39 serverdb dovecot[1034]: pop3-login: Disconnected: Aborted login by logging out (no auth attempts in 0 secs): user=<>, rip=192.168.1.110, lip=192.168.1.110, TLS, session=<ZY3O2SzZ3tLAqAE8>
 
Feb 21 21:24:28 serverdb dovecot[1034]: imap-login: Disconnected: Aborted login by logging out (no auth attempts in 0 secs): user=<>, rip=192.168.1.110, lip=192.168.1.110, TLS, session=<J3/qBI3Y9NzAqAE8>


Evaluation

Comme pour les cas précédents, nous allons utiliser la commande qui émet une alarme de niveau 5. Nous avons pris comme exemple la première ligne de l'extrait du journal:

/var/ossec/bin/wazuh-logtest

Résultat:


Starting wazuh-logtest v4.2.5
Type one log per line
 
Mar  1 20:13:14 serverdb dovecot[1034]: pop3-login: Disconnected: Aborted login by logging out (no auth attempts in 0 secs): user=<>, rip=192.168.1.110, lip=192.168.1.110, secured, session=<rKDy9CzZIsTAqAE8>
 
**Phase 1: Completed pre-decoding.
       full event: 'Mar  1 20:13:14 serverdb dovecot[1034]: pop3-login: Disconnected: Aborted login by logging out (no auth attempts in 0 secs): user=<>, rip=192.168.1.110, lip=192.168.1.110, secured, session=<rKDy9CzZIsTAqAE8>'
       timestamp: 'Mar  1 20:13:14'
       hostname: 'serverdb'
       program_name: 'dovecot'
**Phase 2: Completed decoding.
       name: 'dovecot'
       parent: 'dovecot'
**Phase 3: Completed filtering (rules).
       id: '9707'
       level: '5'
       description: 'Dovecot Aborted Login.'
       groups: '['dovecot', 'invalid_login']'
       firedtimes: '1'
       gdpr: '['IV_35.7.d', 'IV_32.2']'
       gpg13: '['7.1']'
       hipaa: '['164.312.b']'
       mail: 'True'
       nist_800_53: '['AU.14', 'AC.7']'
       pci_dss: '['10.2.4', '10.2.5']'
       tsc: '['CC6.1', 'CC6.8', 'CC7.2', 'CC7.3']'
**Alert to be generated.

C'est la règle n° 9707 qui est concernée.


Règles du logiciel

Cette règle se retrouve dans le fichier "/var/ossec/ruleset/rules/0155-dovecot_rules.xml".

Voici un extrait:


<group name="dovecot,">
  <rule id="9700" level="0">
    <decoded_as>dovecot</decoded_as>
    <description>Dovecot Messages Grouped.</description>
  </rule>
  <rule id="9707" level="5">
    <if_sid>9700</if_sid>
    <match>: Aborted login</match>
    <description>Dovecot Aborted Login.</description>
    <group>invalid_login,pci_dss_10.2.4,pci_dss_10.2.5,gpg13_7.1,gdpr_IV_35.7.d,gdpr_IV_32.2,hipaa_164.312.b, nist_800_53_AU.14,nist_800_53_AC.7,tsc_CC6.1,tsc_CC6.8,tsc_CC7.2,tsc_CC7.3,</group>
  </rule>
</group>


1ère approche

Pour cette première approche, nous avons utilisé la même méthode que dans les cas précédents; c'est à dire l'ajout d'une règle.


Règle personnelle

On va donc créer un fichier qui va contenir nos règles personnelles liées à la messagerie. On le nommera "/var/ossec/etc/rules/perso-mail_rules.xml". Le décodage ne nous fourni que peu d'information. Seul le n° de règle activée (9707) nous est fourni. Il ne nous reste que l'exploitation directe du message brut. Il nous faut désactiver cette alarme pour un émetteur et un récepteur bien précis au travers de leurs adresses IP. Cette partie du message qui correspond à ces critères est "rip=192.168.1.110, lip=192.168.1.110" et l'interrogation émise par Nagios qui ne tente pas d'effectuer une authentification ("Disconnected: Aborted login by logging out"), est utilisé.

Voici cette règle:

/var/ossec/etc/rules/perso-mail_rules.xml


<group name="dovecot,">
  <rule id="100153" level="0">
    <if_sid>9707</if_sid>
    <match>rip=192.168.1.110, lip=192.168.1.110</match>
    <match>Disconnected: Aborted login by logging out</match>
    <description>Nagios-dovecot (ADB).</description>
  </rule>
</group>


Validation

On passe à l'étape évaluation. On lance la commande:

/var/ossec/bin/wazuh-logtest

Résultat:


Starting wazuh-logtest v4.2.5
Type one log per line
 
Mar  1 20:13:14 serverdb dovecot[1034]: pop3-login: Disconnected: Aborted login by logging out (no auth attempts in 0 secs): user=<>, rip=192.168.1.110, lip=192.168.1.110, secured, session=<rKDy9CzZIsTAqAE8>
 
**Phase 1: Completed pre-decoding.
       full event: 'Mar  1 20:13:14 serverdb dovecot[1034]: pop3-login: Disconnected: Aborted login by logging out (no auth attempts in 0 secs): user=<>, rip=192.168.1.110, lip=192.168.1.110, secured, session=<rKDy9CzZIsTAqAE8>'
       timestamp: 'Mar  1 20:13:14'
       hostname: 'serverdb'
       program_name: 'dovecot'
**Phase 2: Completed decoding.
       name: 'dovecot'
       parent: 'dovecot'
**Phase 3: Completed filtering (rules).
       id: '100153'
       level: '0'
       description: 'Nagios-dovecot (ADB).'
       groups: '['dovecot']'
       firedtimes: '1'
       mail: 'False'


2ème approche

Dans cette seconde approche, nous allons agir en amont. Les décodeurs ne nous donnent aucune information sur l'émetteur et le récepteur. Nous tacherons de les mettre en évidence.


Décodeurs du logiciel

On retrouve les décodeurs utilisés dans le fichier "/var/ossec/ruleset/decoders/0085-dovecot_decoders.xml".

Voici un extrait:


<decoder name="dovecot">
  <program_name>^dovecot</program_name>
</decoder>
 
<decoder name="dovecot-disconnect-user">
  <parent>dovecot</parent>
  <prematch offset="after_parent">^\w\w\w\w-login: Disconnected\.+user=</prematch>
  <regex offset="after_parent">user=(\S+), method=\S+, rip=(\S+), lip=(\S+),</regex>
  <order>srcuser, srcip, dstip</order>
</decoder>
 
<decoder name="dovecot-disconnect">
  <parent>dovecot</parent>
  <prematch offset="after_parent">^\w\w\w\w-login: Disconnected</prematch>
  <regex offset="after_prematch">rip=(\S+), lip=(\S+),|rip=(\S+), lip=(\S+)$</regex>
  <order>srcip, dstip</order>
</decoder>

La première entrée précise que les messages du journal sont émis par le programme "dovecot". C'est le prédécodage qui lui fourni cette information ("program_name"); deux autres informations sont aussi identifiées ("timestamp" et "hostname"). Ces champs proviennent du début du message du journal ("Mar 1 20:13:14 serverdb dovecot[1034]:"). Cette partie est acquise et mise de côté. Le reste va être décodé par les décodeurs suivants.

Par héritage, les décodeurs suivants analysent cette seconde partie. Elles concernent la déconnexion ("Disconnected") suite à une demande d'authentification ("-login"). La chaîne "^\w\w\w\w" signifie qu'en début de message ("^"), on prend quatre caratères alphanumériques de base limités à droite par la chaîne "-login:". Ces quatre caractères collent aux mots POP3 et IMAP recherchés.

Sur les trois décodeurs présentés, c'est le second qui s'active.


Pour une explication des codes spéciaux utilisés dans la recherche, voyez l'explication dans la documentation de Wazuh à l'URL https://documentation.wazuh.com/current/user-manual/ruleset/ruleset-xml-syntax/regex.html


Présentation de la solution

Comme les résultats du second décodeur ne nous conviennent pas, il faut en insérer un juste avant. Mais comme il ne faut jamais modifier un décodeur venant avec le logiciel, il faut le mettre dans le répertoire personnel "/var/ossec/etc/decoders". Mais si ce décodeur est mis seul, il sera traité en dernier lieu et ce sera toujours le décodeur du logiciel qui sera activé. Pour contourner ce problème, on va utiliser une astuce.

  • En premier lieu, on recopie le fichier "/var/ossec/ruleset/decoders/0085-dovecot_decoders.xml" dans le répertoire "/var/ossec/etc/decoders". Par soucis de visibilité, on garde son nom. ("/var/ossec/etc/decoders/0085-dovecot_decoders.xml")
  • En second lieu, on désactive le décodeur d'origine, par l'ajout d'une option dans le fichier de configuration du serveur de Wazuh. ("/var/ossec/etc/ossec.conf")
  • En troisième lieu, on ajoute notre décodeur dans le fichier "/var/ossec/etc/decoders/0085-dovecot_decoders.xml".
  • Enfin on redémare le service 'wazuh-manager.service".


Configuration du service

On ajoute la ligne mise en gras ci-dessous sous la section "ruleset" dans le fichier de configuration du serveur "/var/ossec/etc/ossec.conf".


 <ruleset>
   <decoder_exclude>ruleset/decoders/0085-dovecot_decoders.xml</decoder_exclude>
 </ruleset>

Elle désactive la prise en compte de ce fichier de décodeurs.


Décodeur personnel

L'extrait des décodeurs présenté plus haut devient (fichier "/var/ossec/etc/decoders/0085-dovecot_decoders.xml") suite à l'ajout d'un décodeur:


<decoder name="dovecot">
  <program_name>^dovecot</program_name>
</decoder>
 
<decoder name="dovecot-disconnect-adb">
  <parent>dovecot</parent>
  <prematch offset="after_parent">^\w\w\w\w-login: Disconnected: Aborted login by logging out</prematch>
  <regex offset="after_prematch">user=\p>, rip=(\S+), lip=(\S+),</regex>
  <order>srcip, dstip</order>
</decoder>
 
<decoder name="dovecot-disconnect-user">
  <parent>dovecot</parent>
  <prematch offset="after_parent">^\w\w\w\w-login: Disconnected\.+user=</prematch>
  <regex offset="after_parent">user=(\S+), method=\S+, rip=(\S+), lip=(\S+),</regex>
  <order>srcuser, srcip, dstip</order>
</decoder>
 
<decoder name="dovecot-disconnect">
  <parent>dovecot</parent>
  <prematch offset="after_parent">^\w\w\w\w-login: Disconnected</prematch>
  <regex offset="after_prematch">rip=(\S+), lip=(\S+),|rip=(\S+), lip=(\S+)$</regex>
  <order>srcip, dstip</order>
</decoder>

L'ajout est mis en gras. La balise "<prematch offset="after_parent">" permet de faire une première sélection et si elle est acceptée, on passe à la seconde identification par la balise "<regex offset="after_prematch">".

Les chaînes "(\S+)" privilégient une suite de caratères ("\S+") qui sera placée dans un champs par le fait de la présence des parenthèses qui les entourent. Il y a deux de ces zones. La correspondance avec le nom des champs est reprise dans l'ordre derrière la balise "<order>".

  • la zone derrière la chaîne "rip=" (comme remote IP) correspond au champs "srcip"
  • la zone derrière la chaîne "lip=" (comme local IP) correspond au champs "dstip"

Derrière la chaîne "user=", on s'attend à "<>" mais ces deux caratères identifient habituellement une balise; pour éviter cette interprétation, la chaîne "\p" remplace quelques caractères spéciaux tel "<".


Règle personnelle

Maintenant le décodeur nous fournira deux champs reprenant les adresses IP des corresponants. Notre règle peut dès lors les utiliser.

Voici cette règle qui remplace celle présentée ci-dessus dans le fichier "/var/ossec/etc/rules/perso-mail_rules.xml". De plus on se base directement sur la première règle qui identifie le programme qui a généré ce message dans le journal:


<group name="dovecot,">
  <rule id="100153" level="0">
    <if_sid>9700</if_sid>
    <match>Disconnected: Aborted login by logging out</match>
    <srcip>192.168.1.110</srcip>
    <dstip>192.168.1.110</dstip>
    <description>Nagios-dovecot (ADB).</description>
  </rule>
</group>


Validation

On passe à l'étape évaluation. On lance la commande:

/var/ossec/bin/wazuh-logtest

Résultat:


Starting wazuh-logtest v4.2.5
Type one log per line
 
Mar  1 20:13:14 serverdb dovecot[1034]: pop3-login: Disconnected: Aborted login by logging out (no auth attempts in 0 secs): user=<>, rip=192.168.1.110, lip=192.168.1.110, secured, session=<rKDy9CzZIsTAqAE8>
 
**Phase 1: Completed pre-decoding.
       full event: 'Mar  1 20:13:14 serverdb dovecot[1034]: pop3-login: Disconnected: Aborted login by logging out (no auth attempts in 0 secs): user=<>, rip=192.168.1.110, lip=192.168.1.110, secured, session=<rKDy9CzZIsTAqAE8>'
       timestamp: 'Mar  1 20:13:14'
       hostname: 'serverdb'
       program_name: 'dovecot'
**Phase 2: Completed decoding.
       name: 'dovecot'
       parent: 'dovecot'
       dstip: '192.168.1.110'
       srcip: '192.168.1.110'
**Phase 3: Completed filtering (rules).
       id: '100153'
       level: '0'
       description: 'Nagios-dovecot (ADB).'
       groups: '['dovecot']'
       firedtimes: '1'
       mail: 'False'

On remarque l'apparition des champs "dstip" et "srcip" lors de la phase de décodage. Notre règle s'active bien à un niveau 0.


5ème cas

J'ai un site Web de type WordPress. Le moyen commun d'accéder à ce type de site pour le modifier est d'effectuer une authentification. Le script "wp-login.php" permet cet accès. Je désire d'abord être averti si quelqu'un veut y accéder alors que personne n'en a besoin. Ensuite je veux bloquer tout autre appel de ce script pour une URL inexistante.


Requête type du journal d'HTTP

Dans notre journal "/var/log/access_log", nous sélectionnons quelques exemples qui nous permettrons de tester nos règles.

En voici quatre:


157.55.39.130 - - [27/Feb/2022:16:40:09 +0100] "GET /abeille/wp-login.php HTTP/2.0" 200 8413 "-" "Mozilla/5.0 (compatible;bingbot/2.0; +http://www.bing.com/bingbot.htm)"
 
64.225.26.22 - - [02/Sep/2021:09:15:17 +0200] "GET /abeille/wp-login.php HTTP/1.1" 301 253 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36"
 
14.29.240.225 - - [04/Mar/2022:23:44:49 +0100] "GET /wp-login.php HTTP/1.1" 404 16 "-" "Apache-HttpClient/4.5.2 (Java/1.8.0_161)"
 
143.110.247.244 - - [20/Dec/2021:18:23:27 +0100] "GET /outil/wp-login.php HTTP/1.1" 301 243 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"

Les deux premiers sont acceptés mais demandent un avertissement par mail. Les deux suivants nécessitent un blocage.


Evaluation

Nous passerons cette étape qui a déjà été abordée au cas n° 1 et qui ne nous apportera aucune information supplémentaire.


Règles du logiciel

De même ici, les règles du logiciel rencontrées sont les mêmes que dans le cas n° 1.


Règles personnelles

Nous allons donc concevoir des règles sur base de ces informations et des contraintes.

Dans le répertoire "/var/ossec/etc/rules" reprenant les règles personnelles, nous créons le fichier "perso-web_rules.xml" s'il n'existe pas dans laquelle on ajoute les règles suivantes:


<group name="web,accesslog,">
 <rule id="100161" level="0">
   <if_sid>31100, 31101, 31108</if_sid>
   <url>/wp-login.php</url>
   <description>Web WP-LOGIN (ADB).</description>
 </rule>
 
 <rule id="100162" level="5">
   <if_sid>100161</if_sid>
   <url>^/abeille/wp-login.php</url>
   <description>Web abeille WP-LOGIN (ADB).</description>
 </rule>
 
 <rule id="100119" level="7">
   <if_sid>100161</if_sid>
   <url>!^/abeille/wp-login.php</url>
   <description>Web attaque WP-LOGIN (ADB).</description>
 </rule>
</group> 

Une des règles 31100, 31101 ou 31108 a été activée selon le n° d'erreur d'HTTP. Nous en héritons dans la première règle qui a pour but de repérer dans l'URL l'utilisation du script PHP "/wp-login.php". Son niveau est 0 car cette règle ne sert que de filtrage intermédiaire.

Les deux règles suivantes héritent de cette première règle.

La deuxième règle repère l'appel de ce script existant sur mon site dans le champs "url". Une alerte de niveau 5 est émise afin qu'un mail soit envoyé. Cette limite est définie dans le fichier de configuration du serveur comme vu précédement (section <alerts>, option <email_alert_level>). Si on ne veut pas d'alerte, on met ce niveau à 0 ou tout au moins en dessus de 5 dans mon cas.

La troisième règle est la négation de la seconde par la présence d'un point d'exclamation ("!") devant la chaîne recherchée dans le champs "url". Elle émet une alerte de niveau 7.


Validation

Après l'ajout des règles, on lance de nouveau la commande pour vérification:

/var/ossec/bin/wazuh-logtest

On commence par un accès au site avec le premier message du journal.

Résultat:


Starting wazuh-logtest v4.2.5
Type one log per line
 
157.55.39.130 - - [27/Feb/2022:16:40:09 +0100] "GET /abeille/wp-login.php HTTP/2.0" 200 8413 "-" "Mozilla/5.0 (compatible;bingbot/2.0; +http://www.bing.com/bingbot.htm)"
 
**Phase 1: Completed pre-decoding.
       full event: '157.55.39.130 - - [27/Feb/2022:16:40:09 +0100] "GET /abeille/wp-login.php HTTP/2.0" 200 8413 "-" "Mozilla/5.0 (compatible;bingbot/2.0; +http://www.bing.com/bingbot.htm)"'
**Phase 2: Completed decoding.
       name: 'web-accesslog'
       id: '200'
       protocol: 'GET'
       srcip: '157.55.39.130'
       url: '/abeille/wp-login.php'
**Phase 3: Completed filtering (rules).
       id: '100162'
       level: '5'
       description: 'Web abeille WP-LOGIN (ADB).'
       groups: '['web', 'accesslog']'
       firedtimes: '1'
       mail: 'True'
**Alert to be generated.

La règle "100162" s'est bien activée. Le niveau d'alerte est bien 5 et un mail est généré ("mail: 'True'"). La description attendue est correcte.

On fait de même avec un mauvais accès avec le troisième message du journal.

Résultat:


Starting wazuh-logtest v4.2.5
Type one log per line
 
14.29.240.225 - - [04/Mar/2022:23:44:49 +0100] "GET /wp-login.php HTTP/1.1" 404 16 "-" "Apache-HttpClient/4.5.2 (Java/1.8.0_161)"
 
**Phase 1: Completed pre-decoding.
       full event: '14.29.240.225 - - [04/Mar/2022:23:44:49 +0100] "GET /wp-login.php HTTP/1.1" 404 16 "-" "Apache-HttpClient/4.5.2 (Java/1.8.0_161)"'
**Phase 2: Completed decoding.
       name: 'web-accesslog'
       id: '404'
       protocol: 'GET'
       srcip: '14.29.240.225'
       url: '/wp-login.php'
**Phase 3: Completed filtering (rules).
       id: '100119'
       level: '7'
       description: 'Web attaque WP-LOGIN (ADB).'
       groups: '['web', 'accesslog']'
       firedtimes: '1'
       mail: 'True'
**Alert to be generated.

La règle "100119" s'est bien activée. Ici l'alerte est de niveau 7 et le descriptif est celui attendu.


Activation

Il nous reste à ajouter ce n° de règle "100119" au fichier de configuration "/var/ossec/etc/ossec.conf" de notre serveur à la section "<active-response>", option "<rules_id>" comme vu précédemment. Noublions pas de relancer le service.




retour à Wazuh: HIDS