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 :