4min.

Mitiger une attaque sur un site en quelques commandes avec fail2ban

Un vendredi après-midi, le site d’un client devient lent, des notifications d’erreurs venant de nos différents outils de monitoring commencent à arriver sur Slack, les graphes de redirection.io montrent un nombre de requêtes qui explose, …

Avec un WAF (Web Application Firewall) type AWS WAF, DataDome, … ce genre d’anomalie se gère toute seule ou à minima, il est très simple de faire quelques configurations pour la gérer. Sans WAF, voici une solution rapide à mettre en place sur vos serveurs.

En regardant les logs d’accès, nous nous apercevons immédiatement qu’il est en train de se passer quelque chose sur la page de connexion /login.

Nombre de hits sur /login et sur la page d'accueil

196 000 hits sur /login pour seulement 17 000 sur la page d’accueil ! 😱

Le composant Symfony RateLimiter est utilisé sur le projet, mais celui-ci protège contre un trop grand nombre d’essais par identifiant. Mais ici, l’attaquant ne fait qu’un essai pour un identifiant donné, et il a une longue liste d’identifiants à tester !

Une première réponse rapide a été de bannir les quelques IPs qui tapent le site à grande vitesse. En récupérant une liste de quelques dizaines d’IPs via les logs de redirection.io, nous les ajoutons dans la configuration nginx, nous appliquons celle-ci avec ansible et… enfin un peu de calme !

Pendant 30 secondes 😆

Évidemment, ce genre d’attaque ne se fait pas avec quelques IPs, mais avec une petite armée de machines zombies qui vont faire les appels HTTP qu’on leur dit de faire.

Nous passons donc à l’étape 2. Il va falloir configurer fail2ban.

Note personnel : Je n’avais jamais eu à le faire jusqu’à présent, et je m’attendais à quelque chose un peu compliqué… Que nenni ! Ça a été d’une simplicité déconcertante.

fail2ban est une application qui selon des règles qu’on lui définit, va analyser des logs pour déterminer s’il est nécessaire de bannir une IP en ajoutant une règle iptables – en 2 mots : LE firewall Linux (presque aussi bien que OpenOffice 😏). L’application procure plusieurs filtres déjà fait pour l’analyse des logs des serveurs web, ssh, … les plus connus.

Ici, nous voulions cibler exclusivement l’attaque sur l’url /login.

Nous avons donc créé un nouveau fichier de filtre dans le dossier des configurations : /etc/fail2ban/filter.d/nginx-login-attack.conf :

[Definition]
failregex = ^<HOST>.*"(GET|POST) \/login.*" (200) .*$
ignoreregex =

Nous cherchons dans les logs les lignes qui indiquent /login que ce soit en GET ou en POST.

Nous créons ensuite une « jail » dans le fichier /etc/fail2ban/jail.d/nginx.conf :

[nginx-login-attack]
enabled  = true
port     = http,https
filter    = nginx-login-attack
logpath  = /var/log/nginx/xxxxxxxxxxxx.com.access.log
maxretry = 20
findtime  = 60
bantime  = 3600

Nous appelons cette jail « nginx-login-attack » qui utilise le filtre créé précédemment en lui indiquant également le chemin d’accès du fichier de logs à analyser.

Ensuite viennent les 3 valeurs importantes de cette configuration :

  • maxretry à partir de combien d’occurrences nous considérons ce log problématique ;
  • findtime : et sur un intervalle de combien de secondes ;
  • bantime : enfin, pendant combien de secondes nous allons bannir cette IP.

Et voilà !

Avant d’activer cette nouvelle configuration, il est possible de faire un dry-run avec la commande :

|⇒  sudo fail2ban-regex /var/log/nginx/xxxxxxxxxxxx.com.access.log /etc/fail2ban/filter.d/nginx-login-attack.conf

Running tests
=============

Use   failregex filter file : nginx-login-attack, basedir: /etc/fail2ban
Use     	log file : /var/log/nginx/xxxxxxxxxxxx.com.access.log
Use     	encoding : UTF-8

Results
=======

Failregex: 779 total
|-  #) [# of hits] regular expression
|   1) [779] ^<HOST>.*"(GET|POST) \/login.*" (200) .*$
`-

Ignoreregex: 0 total

Lines: 178713 lines, 0 ignored, 779 matched, 177934 missed
[processed in 45.58 sec]

Tout semble OK.

Nous relançons le service et fail2ban commence son travail. Nous pouvons regarder l’état de la règle à tout moment :

|⇒  sudo fail2ban-client status nginx-login-attack               	
Status for the jail: nginx-login-attack
|- Filter
|  |- Currently failed:    181
|  |- Total failed:    39458
|  `- File list:    /var/log/nginx/xxxxxxxxxxxx.com.access.log
`- Actions
   |- Currently banned:    1051
   |- Total banned:    1052

Nous pouvons voir ainsi les milliers d’IPs qui se font bannir petit à petit. Nous inspectons les logs d’accès sur redirection.io. Ça se calme à grande vitesse et nous voyons toujours passer des connexions réelles. Parfait !

Pour tester, on peut aussi s’amuser à envoyer nous même de nombreuses requêtes sur /login pour se faire bannir 😆

C’est simple, un petit outil basique type ab fait l’affaire !

Sur notre machine local, nous lançons :

ab -n 100 https://xxxxxxxxxxxx.com/login

Yeahy!

Nous sommes banni du serveur, notre navigateur affiche bien une erreur « La connexion a échoué » \o/

Sur le serveur, il suffit de regarder les règles iptables pour voir notre IP apparaître.

$ sudo iptables -S
...
-A f2b-nginx-login-attack -s 193.243.66.101/32 -j REJECT --reject-with icmp-port-unreachable
...

Et enfin, pour pouvoir avoir accès à nouveau au site, nous lançons une commande pour nous retirer de la liste des IPs bannies – il est à noter que la « jail » ne concernait que pour les ports « http, https » :

sudo fail2ban-client set nginx-login-attack unbanip 193.243.66.101

La première version de la configuration avait un bantime de 1H, ce qui n’est pas assez pour décourager les hits. Un passage à 48H a permis d’en finir une fois pour toute avec cette attaque.

Vivement la prochaine ! 😛

Commentaires et discussions