OSSEC, Logstash et Slack

OSSEC est un HIDS (Host based Intrusion Detection System) Open Source, système de détection d'intrusions. Il possède plusieurs features pour faire son boulot telles que :

  • Le monitoring des fichiers système : il va balancer une alerte quand le md5sum d'un fichier (typiquement dans /etc) va changer. Par exemple, il va être capable de dire quand un user a été ajouté au système.
  • Un logiciel de détection de rootkits
  • Une serveillance des logs systèmes. Par exemple, il va pouvoir reporter les tentatives de connexions ou bruteforce SSH.
  • Des active responses, qui sont des actions déclenchées après la rencontre de certains évenements; par exemple, ajouter un DROP iptable suite à la détection d'un bruteforce.

Le système est composé d'un serveur et d'un ou plusieurs agents. Les agents font très peu de boulot. Il se contentent d'envoyer les logs au serveur ou le md5sum des fichiers, et c'est le server qui va faire tout le travail d'analyse.

OSSEC

Installation d'OSSEC

Comme d'hab, on est sous Debian. On va passer par le repository d'ATOMICORP pour ça. Il y a aussi le repository de wazuh, indiqué dans la doc officielle d'installation, mais les paquets n'y sont pas à jour.

On lance donc :

wget -q -O - http://www.atomicorp.com/installers/atomic | bash  

Cela va ajouter la source à /etc/apt/sources.list et ajouter la clé GPG.

Ensuite :

apt update  
apt install ossec-hids-server  

Le server contient son propre agent. Il est donc capable de s'analyser lui même. Il va vous poser quelques questions pour pouvoir vous alerter par mail si vous le souhaitez. Comme on va voir plus tard comment être alerté par Slack, on va pas activer ça.

That's it. Vous avez un système de détection d'intrusions qui tourne. Assez simple, non ?

Configuration d'OSSEC

La configuration se fait dans /var/ossec/etc/ossec.conf pour la config globale. C'est ici qu'on va activer les active reponses. Elles sont normalement activées par défaut avec ce package, mais si ce n'est pas le cas, voici comment faire :

D'abord, vérifier que ces blocs soient bien présents. Ce sont les commandes qui vont être éxecutés dans le cadre des actives reponses.

  <command>
    <name>host-deny</name>
    <executable>host-deny.sh</executable>
    <expect>srcip</expect>
    <timeout_allowed>yes</timeout_allowed>
  </command>  

  <command>
    <name>firewall-drop</name>
    <executable>firewall-drop.sh</executable>
    <expect>srcip</expect>
    <timeout_allowed>yes</timeout_allowed>
  </command> 

Puis vérifier l'existence de ces blocs là :

  <!-- Active Response Config -->
  <active-response>
    <!-- This response is going to execute the host-deny
       - command for every event that fires a rule with
       - level (severity) >= 6.
       - The IP is going to be blocked for  600 seconds.
      -->
    <command>host-deny</command>
    <location>local</location>
    <level>6</level>
    <timeout>600</timeout>
    <repeated_offenders>60,120,360,1440,10080</repeated_offenders>
  </active-response>

  <active-response>
    <!-- Firewall Drop response. Block the IP for
       - 600 seconds on the firewall (iptables,
       - ipfilter, etc).
      -->
    <command>firewall-drop</command>
    <location>local</location>
    <level>6</level>
    <timeout>600</timeout>
    <repeated_offenders>60,120,360,1440,10080</repeated_offenders>
  </active-response>

S'il y a une option <disabled>yes</disabled>, la passer à no.

Ajouter ce bloc :

    <syslog_output>
       <server>127.0.0.1</server>
       <port>9000</port>
       <format>default</format>
    </syslog_output>

Ceci va permettre d'envoyer toutes les alertes vers un serveur de type syslog, hébergé en local sur le port 9000. On va y faire écouter un logstash, ce qui pourra dans notre cas permettre d'y brancher Slack et éventuellement alimenter une base Elasticsearch.

Activez le module syslog :

sudo /var/ossec/bin/ossec-control enable client-syslog  

Ne pas oublier de relancer le démon après chaque modif :

sudo systemctl restart ossec  

Ajouter un agent

Tout d'abord, vous allez générer une clé pour l'agent depuis le serveur.

Sur le serveur

Lancez la commande :

sudo /var/ossec/bin/manage_agents

****************************************
* OSSEC HIDS v2.9.1 Agent manager.     *
* The following options are available: *
****************************************
   (A)dd an agent (A).
   (E)xtract key for an agent (E).
   (L)ist already added agents (L).
   (R)emove an agent (R).
   (Q)uit.
Choose your action: A,E,L,R or Q:  

Sélectionnez A pour ajouter un agent :

- Adding a new agent (use '\q' to return to the main menu).
  Please provide the following:
   * A name for the new agent: test
   * The IP Address of the new agent: any
   * An ID for the new agent[021]: 
Agent information:  
   ID:021
   Name:tmp
   IP Address:any

Confirm adding it?(y/n):  

Donnez un nom identifiable, puis l'addresse IP de l'agent (pas un DNS). S'il change d'IP ou si plusieurs agent ont la même IP (par exemple derrière un NAT), précisez any à la place de l'IP. Pour l'ID, laissez par défaut.

Après confirmation, vous allez vous retrouver sur l'écran précédent. Choisissez E pour extraire la clé, puis sélectionnez l'ID de l'agent précédemment créé :

****************************************
* OSSEC HIDS v2.9.1 Agent manager.     *
* The following options are available: *
****************************************
   (A)dd an agent (A).
   (E)xtract key for an agent (E).
   (L)ist already added agents (L).
   (R)emove an agent (R).
   (Q)uit.
Choose your action: A,E,L,R or Q: E

Available agents:  
   [...]
   ID: 021, Name: tmp, IP: any
Provide the ID of the agent to extract the key (or '\q' to quit): 021

Agent key information for '021' is:  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==

** Press ENTER to return to the main menu.

Relancez le service

sudo systemctl restart ossec  

Sur l'agent

Sur l'agent, installer le package ossec-hids-agent (pas besoin de faire tout ça sur le serveur, de toutes façon les packages agent et server ne peuvent pas être installés en même temps) :

sudo apt install ossec-hids-agent  

Le process d'install va demander l'IP du serveur (pas un hostname ni un DNS).

Lancez la même commande manage_agent sur l'agent

/var/ossec/bin/manage_agents
****************************************
* OSSEC HIDS v2.9.1 Agent manager.     *
* The following options are available: *
****************************************
   (I)mport key from the server (I).
   (Q)uit.
Choose your action: I or Q: 

Importez la clé récupérée depuis le serveur, puis relancer le service

sudo systemctl restart ossec  

Vérifiez que l'agent peut se connecter au serveur en consultant les logs dans /var/ossec/logs/ossec.log

2017/08/08 10:22:28 ossec-agentd: INFO: Trying to connect to server 163.XXXXXXXXXX, port 1514.  
2017/08/08 10:22:28 INFO: Connected to 163.XXXXXXXX at address 163.XXXXXXXXXX, port 1514  

A ce point, sur le serveur, vous pouvez voir déjà quelques alertes dans /var/ossec/logs/alerts/alert.log. Si vous êtes sur un serveur avec un port SSH accessible depuis Internet, vous aurez surement des entrées de type :

** Alert 1502187904.1942233: - syslog,sshd,authentication_failed,
2017 Aug 08 12:25:04 (**********) 62.**********->/var/log/auth.log  
Rule: 5716 (level 5) -> 'SSHD authentication failed.'  
Src IP: 58.**********  
User: root  
Aug  8 12:25:02 ********** sshd[32251]: Failed password for root from 58.********** port 22519 ssh2  

Quelqu'un a essayé vainement de se loguer sur le système avec root. S'il échoue un trop grand nombre de fois, il sera ajouté dans les actives reponses et dropé par iptables.

Ignorer certaines alertes

J'avais beaucoup de bruit, même en limitant via logstash (voir plus bas), donc j'ai du créer sous OSSEC des règles particulières permettant de ne pas reporter certains évenements. Beaucoup en l'occurrence sur le système de fichier utilisé par Docker. J'ai donc décidé de l'exclure des reports.

Pour ça, j'ai édité le fichier suivant : /var/ossec/rules/local_rules.xml. J'ai ajouté le bloc suivant :

<group name="rootcheck">  
  <rule id="110000" level="0">
    <if_sid>510</if_sid>
    <match>/var/lib/docker</match>
    <description>Ignore event on var lib docker</description>
  </rule>
</group>  
  • Level 0 veut dire que je ne veut pas que cet évenement soit report
  • if_sid permet de se baser sur la règle 510, qui est en l'occurence la règle que je veux filtrer
  • La clause match permet de regex sur le message. Comme le rootcheck indique systématiquement le chemin du fichier, je peux me baser la dessus pour filtrer.

Logstash

Pour transmettre toutes ces infos sur Logstash, on va construire un input de type syslog sur Logstash et un output de type Slack.

Dans OSSEC 2.9.1, il existe un plugin pour écrire directement sur Slack. En fait, il s'agit d'une active reponse qui va trigger à chaque évenement. Personnellement, cela ne me convenait pas car il me manquait du contrôle sur ce qui était envoyé à logtash. Je voulais pouvoir filtrer certaines règles pour qu'elle ne soient pas sur Slack.

Installation Logstash

On installe donc logstash

sudo apt install logtash  

Il faut également installer le plugin output slack. Pour cela, téléchargez le gem file depuis rubygems.org.

wget -qO /tmp/logstash-output-slack-2.0.3.gem https://rubygems.org/downloads/logstash-output-slack-2.0.3.gem  
/usr/share/logstash/bin/logstash-plugin install /tmp/logstash-output-slack-2.0.3.gem

Redémarrez logstash :

sudo systemctl restart logstash  

Configuration Logstash

On crée le fichier /etc/logstash/conf.d/ossec.conf. Il contient :

  • La définition de l'input de type syslog :
input {  
  udp {
    host => "127.0.0.1"
    port => 9000
    type => "syslog"
  }
}
  • Un filtre qui permet de reconnaitre les champs fournis par OSSEC (lire les commentaires). Je me suis basé sur les travaux de ce blog :
filter {  
  if [type] == "syslog" {
    # Match les champs sur le message syslog original
    grok {
      match => { "message" => "%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_host} %{DATA:syslog_program}: Alert Level: %{BASE10NUM:Alert_Level}; Rule: %{BASE10NUM:Rule} - %{GREEDYDATA:Description}; Location: (?<agent>\(%{HOSTNAME}\) %{IP})->%{GREEDYDATA:Details}" }
      add_field => [ "ossec_server", "%{host}" ]
    }
    # On drop les infos qu'on ne veut pas
    mutate {
      remove_field => [ "syslog_hostname", "syslog_message", "syslog_pid", "message", "@version", "type", "host" ]
    }
    # On converti le niveau d'alerte en entier pour pouvoir faire des tests dans la partie output
    mutate {
      convert => { "Alert_Level" => "integer" }
    }
  }
}
  • La définition de l'output avec certains filtres (lire les commentaires) :
output {  
  # Ici, j'envoie vers Slack seulement si je ne rencontre pas ces règles (ces messages arrivent régulièrement et provoquent beaucoup de bruit dans mon cas) ET que le niveau d'alerte dépasse 7.
  if [Rule] not in ["5710", "5712","5503","2502", "5720"] and [Alert_Level] >= 7 {
    file {
      path => "/var/log/logstash/ossec.log"
    }
    slack {
      url => "https://hooks.slack.com/services/XXXXX/XXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXXXX"
      channel => "XXXXXXX"
      username => "OSSEC-HIDS"
      format => ""
      # La, je joue avec le format pour afficher des jolies alertes et pas un message texte moche.
      attachments => [{
        "fallback" => "OSSEC-HIDS"
        "color" => "#FF0000"
        "title" => "%{Description}"
        "text" => "%{Details}" 
        "fields" => [{
          "title" => "Agent" 
          "value" => "%{agent}"
          "short" => true
          },{
          "title" => "Alert level"
          "value" => "%{Alert_Level}"
          "short" => true
          },{
          "title" => "Rule"
          "value" => "%{Rule}"
          "short" => true
          },{
          "title" => "Timestamp"
          "value" => "%{@timestamp}"
          "short" => true
          }]
      }]
    }
  }
}

On relance le tout

sudo systemctl restart logstash  

On reçoit donc des jolies notifications de ce type :

Damien Gustave

Read more posts by this author.