Securisation de son micro site

 · 15 min read
 · Johann
Table of contents

Avant d'aller plus loin dans la technique...

Seconde partie sur la mise en place d'un micro-site, orientée sécurité.

Avant toute chose, notons que la sécurité peut (doit) se faire sur plusieurs fronts. J'indique ici la mise en oeuvre de la sécurité au niveau supérieur de la couche OSI (pour les vulnérabilités OWASP), j'aborde également rapidement le niveau réseau.

D'autres points SSI pourraient être évoqués, sur lesquels je ne m'attarde pas:

  • la configuration d'Apache; de nombreux sites précisent comment restreindre un maximum la configuration de celui ci.
  • la sécurité de l'outil Hugo. Je n'ai pas les compétences pour indiquer comment hausser la sécurité au niveau développement logiciel, mais dans l'idéal ça devrait être pris en compte.

Nous allons donc voir comment mettre en place un outil qui permet à la fois de bénéficier des remontées d'alertes de la communauté, mais également de ses propres règles locales sur le réseau.

Nous utiliserons au final un outil classique dans l'univers du pentest pour vérifier que tout fonctionne bien.

Déploiement de crowdsec

Crowdsec c'est la success story du moment, dès que j'en entends parler c'est qu'ils ont gagné un prix à un des nombreux congrès de cybersécu quelque part dans le monde.

En même temps l'outil est bien pensé et très simple à mettre en place. Il est open source, et dans sa version gratuite doté de nombreuses fonctionnalités, amplement suffisant pour l'utilisateur lambda.

Notons enfin que c'est (en partie?) un produit Français, cocorico!

Le peuple vaincra

D'aucuns diront que Crowdsec c'est le Waze de la cybersécurité.

L'image me parait plutôt bonne, car effectivement on va s'adosser à un service d'alertes centralisées pour obtenir non pas l'emplacement de la maréchaussée mais bien une base de réputation d'ips.

Vous pourrez évidement ajouter vos propres IPs référencées à cette base, ou choix étonnant de ne pas transmettre vos attaques à la communauté.

Ca va de l'ip qui fait 'juste' un scan de port sans aller plus loin aux range d'ip connus pour bruteforcer tout ce qui passe en passant par le bon vieux DDOS.

Les plus vieux se diront qu'il y a un outil pour éviter ça, fail2ban. Qui fonctionne très bien certes, mais incrémenter notre liste d'intrus au fur et à mesure des années tout seul dans notre coin c'est risqué, et une perte de temps énorme. Alors coopérons!

La simplicité de mise en oeuvre

La documentation officielle est bien fournie et on a rapidement une vue globale de comment ça fonctionne.

On commence par l'application en elle même, disponible sur les dépots de l'éditeur. Je ne réinvente pas la roue, tout est expliqué ici: https://docs.crowdsec.net/docs/getting_started/install_crowdsec/

Le Hub officiel met en exergue trois grandes "familles":

  • Les collections: qui sont un package cadeau de parser (analyseur) et scénarios
    • dans mon exemple, Apache2 s'appuie sur apache2 parser et *base http scenarios *
  • Les configurations: qui permettent à travers les logs de remonter des alertes
  • Les bouncers: c'est le videur qui te jette quand tu as trop abusé
    • ça peut être une tape sur la main (affichage d'un captcha)
    • ça peut être la sortie par la fenêtre (un ban de 4H ou plus)

Pour en savoir plus, de bons articles existent déjà, comme par exemple https://www.cachem.fr/crowdsec-fail2ban-moderne-collaboratif/

Les mains dans le cscli-boui

Le wrapper bien foutu

Après avoir installé CrowdSec sur votre machine, la CLI de CrowdSec (accessible via la commande cscli) vous gérez tout le périmètre de l'outil.

Je trouve que tout le sérieux du produit est visible à travers ce wrapper:

  • il est bien documenté
  • les commandes sont bien identifiées
  • il est très complet

Je précise que je l'ai installé en local, pour un usage local. Cependant on peut rajouter par exemple des bouncers sur d'autres périphériques, qui seront accessibles depuis l'interface centrale.

Le sujet est évoqué ici: https://blog.raspot.in/fr/blog/crowdsec-ajout-et-configuration-dun-bouncer

En gros nul besoin de chercher une interface graphique, ça fait parfaitement le boulot depuis un terminal:

Usage:

  cscli [command]


Available Commands:

  alerts         Manage alerts
  bouncers       Manage bouncers [requires local API]
  capi           Manage interaction with Central API (CAPI)
  collections    Manage collections from hub
  completion     Generate completion script
  config         Allows to view current config
  console        Manage interaction with Crowdsec console (https://app.crowdsec.net)
  dashboard      Manage your metabase dashboard container [requires local API]
  decisions      Manage decisions
  explain        Explain log pipeline
  help           Help about any command
  hub            Manage Hub
  hubtest        Run functional tests on hub configurations
  lapi           Manage interaction with Local API (LAPI)
  machines       Manage local API machines [requires local API]
  metrics        Display crowdsec prometheus metrics.
  notifications  Helper for notification plugin configuration
  parsers        Install/Remove/Upgrade/Inspect parser(s) from hub
  postoverflows  Install/Remove/Upgrade/Inspect postoverflow(s) from hub
  scenarios      Install/Remove/Upgrade/Inspect scenario(s) from hub
  simulation     Manage simulation status of scenarios
  support        Provide commands to help during support
  version        Display version and exit.

Par défaut il y a déjà des collections de disponibles, pour les obtenir:

root@debian-routeur:~# cscli collections list

COLLECTIONS
──────────────────────────────────────────────────────────────────────────────────────────────────────────────
 Name                                📦 Status   Version   Local Path
──────────────────────────────────────────────────────────────────────────────────────────────────────────────
 crowdsecurity/base-http-scenarios   ✔️ enabled   0.6       /etc/crowdsec/collections/base-http-scenarios.yaml
 crowdsecurity/endlessh              ✔️ enabled   0.1       /etc/crowdsec/collections/endlessh.yaml
 crowdsecurity/http-cve              ✔️ enabled   1.8       /etc/crowdsec/collections/http-cve.yaml
 crowdsecurity/iptables              ✔️ enabled   0.1       /etc/crowdsec/collections/iptables.yaml
 crowdsecurity/linux                 ✔️ enabled   0.2       /etc/crowdsec/collections/linux.yaml
 crowdsecurity/smb                   ✔️ enabled   0.1       /etc/crowdsec/collections/smb.yaml
 crowdsecurity/sshd                  ✔️ enabled   0.2       /etc/crowdsec/collections/sshd.yaml      
──────────────────────────────────────────────────────────────────────────────────────────────────────────────

C'est déjà assez vaste, avec une prise en compte des attaques sur SSH, http, les dernières CVE...

Revenons à notre micro-site

Je recentre mon souhait, à savoir protéger l'accès de mon site des attaques les plus courantes.

Dans les faits nous l'avons vu, avec les collections installées par défaut c'est déjà presque le cas. Je vais cependant rajouter une collection apache2 qui s'appuie sur le scénario générique http.

Pour cela rien de plus simple, c'est tout indiqué sur la documentation en ligne https://hub.crowdsec.net/author/crowdsecurity/collections/apache2

cscli collections install crowdsecurity/apache2 

La nouvelle collection est installée, reste à redémarrer le service pour que ça soit fonctionnel.

Mon exemple est assez limité, car si vous avez lu mon précédent article, je n'ai pas un site élaboré. Pas de php, pas de base de données, un angle d'attaque limité. Ca se traduira par un périmètre limité de sécurité...

Mais pour celles et ceux qui disposent d'un Wordpress, un Synology ouvert sur le web ou encore un Nextcloud, n'hésitez pas à aller voir les collections qui cibleront bien mieux vos besoins.

https://hub.crowdsec.net/browse/#collections

Engageons un videur

En l'état si vous tentez par exemple simplement de naviguer sur mon super site hébergé par Hugo avec un user-agent exotique, hé bien il ne va rien se passer.

Avant de crier au scandale regardons un peu ce qu'à vu l'ami cs:

root@debian-routeur:~# cscli decisions list
╭────────┬──────────┬────────────────────┬───────────────────────────────────┬─────────┬─────────┬────────────────────────┬────────┬────────────────────┬──────────╮
   ID     Source      Scope:Value                   Reason                Action   Country            AS            Events      expiration      Alert ID 
├────────┼──────────┼────────────────────┼───────────────────────────────────┼─────────┼─────────┼────────────────────────┼────────┼────────────────────┼──────────┤
 731988  crowdsec  Ip:1.2.3.4          crowdsecurity/http-bad-user-agent  captcha  SG       14061 DIGITALOCEAN-ASN  2      
╰────────┴──────────┴────────────────────┴───────────────────────────────────┴─────────┴─────────┴────────────────────────┴────────┴────────────────────┴──────────╯

Il a bien fait son boulot, il voit qu'une ip avec un bad-user-agent navigue chez moi, mais pour activer une prise de décision, il va falloir installer un bouncer.

Le problème dans mon cas, c'est qu'Hugo c'est du simple HTML. Pour l'instant - sauf erreur de ma part - il n'y a pas de bouncer pour le duo Apache + Html, uniquement NGINX.

A contrario il y a un bouncer PHP qui semble bien s'associer à Apache: https://doc.crowdsec.net/docs/bouncers/php/.

J'ai trouvé plusieurs articles qui évoquent le sujet, donc celui ci dont je me suis fortement inspiré. Tentons notre chance!

Pour le déploiement du bouncer, encore très facile:

git clone https://github.com/crowdsecurity/cs-php-bouncer.git
cd cs-php-bouncer/

Puis on lance l'installation:

./install.sh --apache 

Pour ceux qui comme moi on l'erreur sur le répertoire vendor, il manquait un paquet essentiel: php7.4-gd. Je l'indique car même si le script indique que le bouncer a bien été déployé, ce n'est pas le cas. Tout le reste ne fonctionnera alors pas!

cscli bouncers list
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 Name                            IP Address   Valid   Last API pull          Type                              Version   Auth Type
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 crowdsec-php-bouncer-eUKouR3g   127.0.0.1    ✔️       2023-01-21T12:27:01Z   Standalone CrowdSec PHP Bouncer   v0.15.0   api-key
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Intégrer du php avec Hugo

Je pars du principe que vous arrivez à consulter un site en php depuis votre navigateur pour la suite. L'article précédement indiqué donne l'essentiel pour installer sur son serveur Php (paquets nécessaires, Composer...)

C'est la partie sur laquelle j'ai passé/perdu le plus de temps, en allant un peu dans tous les sens.

Une fois le bouncer installé, j'ai bien des remontées d'alertes. Que je tente un ban ou un captcha pour mon ip, il ne se passe absolument rien.

cscli decisions add --ip 192.168.1.20 --duration 5m --type captcha

Je force manuellement mon ip locale, sur une durée de 5mn, avoir présentation d'un captcha.

cscli decisions list
╭─────────┬──────────┬──────────────────┬──────────────────────────────────────────────────────────────────────────┬─────────┬─────────┬───────────────────────┬────────┬────────────────────┬──────────╮
│   ID    │  Source  │   Scope:Value    │                                  Reason                                  │ Action  │ Country │          AS           │ Events │     expiration     │ Alert ID │
├─────────┼──────────┼──────────────────┼──────────────────────────────────────────────────────────────────────────┼─────────┼─────────┼───────────────────────┼────────┼────────────────────┼──────────┤
│ 1046248 │ cscli    │ Ip:192.168.1.20  │ manual 'captcha' from '98fca7cf19e14d5d9d7d0087dfe8dcedi6LmlupX2G43dxra' │ captcha │         │                       │ 1      │ 4m47.720234544s    │ 190      │

Ce qu'on retriendra dans les grandes lignes, c'est que le tableau indique:

  • l'ID de la liste
  • l'IP concernée
  • la raison, portée par un scénario
  • l'action menée
  • la durée de la prise en compte de celle-ci

Mais à l'évidence ça ne fonctionne pas sur du HTML classique.

Je la fait court, n'étant pas un spécialiste en développement j'ai tatonné:

  • mise en place d'une page CSS qui redirige vers mon HTML: KO
  • modification de ma page HTML pour intégrer du CSS: KO
  • modification d'Apache pour prioriser une page CSS avec redirection sur un sous dossier: ça fonctionne...sauf qu'en visant directement le sous dossier le captcha saute.

Les idées commençaient à me manquer lorsque j'ai tenté un truc de sioux, à savoir renommer mon index.html en index.php, sans en modifier le code... et ça fonctionne! Bon on est d'accord c'est de l'équilibrisme à l'aveuglette, mais des tests que j'ai ensuite lancé ça semble tenir la route et Hugo comme Crowdsec on le comportement attendu.

On tente un captcha:

Puis un ban:

Jouons au script kiddies

La partie pentest, faut pas se mentir c'est quand même ce qu'on attends à chaque fin de déploiement d'un nouvel outil.

Mes tests depuis une ip privée sur le même réseau fonctionnent, mais va falloir aller un peu plus loin pour être sûr que ma petite vie privée est à l'abri des méchants pirates.

Lâchez nikto

Si vous ne connaissez pas, je vous invite à vous ruer sur votre moteur de recherche préféré pour découvir cet outil.

Je vais lancer le test le plus classique qu'il soit, alors que je suis connecté en VPN:

nikto -host http://mondomaine.fr

On laisse tourner un peu, mais la sanction coté Crowdsec est rapide et sans pitiée:

cscli decisions list
╭─────────┬──────────┬────────────────────┬──────────────────────────────────────┬─────────┬─────────┬───────────────────────┬────────┬────────────────────┬──────────╮
│   ID    │  Source  │    Scope:Value     │                Reason                │ Action  │ Country │          AS           │ Events │     expiration     │ Alert ID │
├─────────┼──────────┼────────────────────┼──────────────────────────────────────┼─────────┼─────────┼───────────────────────┼────────┼────────────────────┼──────────┤
│ 1046254 │ crowdsec │ Ip:185.210.219.194 │ crowdsecurity/http-probing           │ ban     │ AT      │ 9009 M247 Europe SRL  │ 12     │ 3h59m47.319813384s │ 196      │
│ 1046253 │ crowdsec │ Ip:185.210.219.194 │ crowdsecurity/http-crawl-non_statics │ captcha │ AT      │ 9009 M247 Europe SRL  │ 49     │ 3h59m29.778993433s │ 195      │
  • On voit que Nikto à tout d'abord dû effectuer son scan avec un user-agent moisi, immédiatement repéré, résultat un captcha
  • Puis quelques dizaines de secondes après, un scan du site lui vaut un ban pur et simple.

Bon, on est d'accord, c'est efficace.

Et pour iptables?

La partie des attaques au niveau du réseau n'est pas en reste, crowdsec propose un paquet debian qui embarque le bouncer iptables, pour l'installer:

apt install crowdsec-firewall-bouncer-iptables

A noter que j'ai eu un souci lors de la première installation du bouncer, qui ne semblait pas fonctionner. En regardant les logs je me suis aperçu d'un problème de connexion avec l’API, qui se trouve pourtant en local.

/var/log/crowdsec-firewall-bouncer.log

Une simple désinstallation avec purge puis réinstallation du paquet a solutionné mon problème... L'informatique et ses miracles.

Enfin on active le service:

systemctl enable crowdsec-firewall-bouncer.service

J'ai ensuite listé les bouncers:

cscli bouncers list

Horreur! j'ai deux fois le même bouncer d'installé. Mon premier bouncer fantôme est toujours présent, certainement ce qui résulte de la première installation de mon paquet. Le problème se règle facilement:

cscli bouncer delete FirewallBouncer-1674044085

Tout rentre dans l'ordre, j'ai un unique bouncer connecté à l'API:

─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 Name                            IP Address   Valid   Last API pull          Type                              Version                                                             Auth Type
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 crowdsec-php-bouncer-eUKouR3g   127.0.0.1    ✔️       2023-01-21T13:38:17Z   Standalone CrowdSec PHP Bouncer   v0.15.0                                                             api-key
 FirewallBouncer-1674308697      127.0.0.1    ✔️       2023-01-21T13:45:40Z   crowdsec-firewall-bouncer         v0.0.25-debian-pragmatic-0a4fde8e9440927d02ce187d1716306af9a13780   api-key
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Désormais les actions indiquées par mes configurations seront appliquées, ici vous l'aurez compris par des règles iptables.

D'ailleurs vérifions qu'il y a bien une nouvelle chaine de présente dans mes règles de pare-feu:

root@debian-routeur:~# iptables -L
Chain INPUT (policy DROP)
target     prot opt source               destination
DROP       all  --  anywhere             anywhere             match-set crowdsec-blacklists src

Parfait, elle arrive en premier. Tentons un bruteforce sur mon ip publique toujours via mon VPN.

Très rapidement j'ai un retour:

cscli decisions list
╭─────────┬──────────┬────────────────────┬───────────────────────────────────────┬─────────┬─────────┬─────────────────────────────────────────────┬────────┬────────────────────┬──────────╮
│   ID    │  Source  │    Scope:Value     │                Reason                 │ Action  │ Country │                     AS                      │ Events │     expiration     │ Alert ID │
├─────────┼──────────┼────────────────────┼───────────────────────────────────────┼─────────┼─────────┼─────────────────────────────────────────────┼────────┼────────────────────┼──────────┤
│ 1255819 │ crowdsec │ Ip:185.210.219.194 │ crowdsecurity/ssh-slow-bf             │ ban     │ AT      │ 9009 M247 Europe SRL                        │ 12     │ 3h59m55.315585877s │ 236      │

De la même manière j'ai un ban pour mon ip, cette fois ci pour une autre raison invoquée par le bouncer iptables. Tout est fonctionnel.

Conclusion

Pour celles et ceux qui ont lu ce long article, vous avez toutes les astuces nécessaires pour mettre en place via crowdsec des mesures de sécurité pour votre site.

Aisée à prendre en main et terriblement efficace, c'est le genre d'outil dont on va se servir pendant de longue année. Pour ma part il aura enfin détronné fail2ban!

A bientôt j'espère :)