Skip to content

Le jour où nous avons endigué une tentative de CryptoMining

  • by

La fin d’année 2023 a été marquée pour un de nos clients par une petite frayeur. Spoiler alert : tout a été maîtrisé, aucune donnée leakée, zéro impact donc.

Pour reproduire une suspicion de souci de performance sur le produit phare de ce client, il a été jugé nécessaire de positionner une instance de debug de l’application sur l’environnement de production. Il s’agissait du seul environnement pouvant ressembler à 100% du cas de l’utilisateur final observant des bugs empêchant un travail opérationnel convenable.

L’application en question est développée en Java qui, grâce au JRE ou JDK, permet le Remote Debug à condition d’ouvrir un port. Cette instance nécessitait donc d’une configuration spécifique pour permettre à l’équipe développement, en utilisant le Remote Debug, d’attacher des sondes pour comprendre au mieux l’activité de l’utilisateur final et surtout pouvoir ressortir des données amenant à l’analyse des bugs.

Evidemment, ouverture de port, sur une production qui plus est, devient synonyme de risques et de création de surfaces d’attaques.

Pour y remédier, avant l’ouverture de ce port, des nouvelles règles ufw sont ajoutés pour n’autoriser ce port qu’en provenance d’une source sûre. Après l’ajout des ces règles, un petit redémarrage d’ufw et l’équipe de développement peut se mettre au travail.

Environ 24h plus tard, surprise, nous recevons une alerte de CrowdStrike Falcon (l’anti-virus / analyseur de packets) nous notifiant le blocage d’un accès et d’une tentative d’installation d’un soft de CryptoMining. L’attaquant était passé par le Remote Debugger ouvert un peu plus tôt.

Déjà, bonne nouvelle, CrowdStrike Falcon a su couper court aux intentions de notre ami attaquant.

A partir de là, il nous reste à nous assurer que la porte permettant ce genre d’attaque soit définitivement close. Peu de scrupule à ce niveau car il s’agit ici d’une instance de debug : arrêt clair et net de cette dernière.

Après (re)vérification, ufw était bien configuré pour ne laisser passer que les personnes autorisées. Et pourtant…

Petit rappel, certains OS utilisent ufw comme logiciel de pare-feu par défaut : Ubuntu depuis sa version 8.04 LTS, Debian, etc. Dans tous les cas, il s’agit d’un pare-feu utilisé par un large panel de personnes dans le monde.

Pour comprendre ce qu’il s’est passé, nous commençons à remettre tout simplement en question ufw :

  1. Comment fonctionne t’il ?
  2. Est-il surchargé par quelque chose ?
  3. Qu’a t’on réellement fait lorsqu’on a créé l’instance de debug ?

Pour répondre à la première question, il suffit de se rendre sur sa documentation : UncomplicatedFirewall – Ubuntu Wiki. On peut y lire qu’il rédige une suite de règles iptables. Rien de surprenant jusque là.

The Linux kernel in Ubuntu provides a packet filtering system called netfilter, and the traditional interface for manipulating netfilter are the iptables suite of commands.

UncomplicatedFirewall – Ubuntu Wiki

Seconde question, au début pas vraiment de réponse. On voit bien les règles iptables qui sont présentes. Par contre, ce qui est constaté c’est une autre règle montre que le port en question est redirigé vers une interface Docker.

Pour la dernière question, vu le constat précédent, nous pouvons imaginer facilement que Docker / Docker Swarm est en conflit avec ufw. Pour ajouter du contexte, l’instance de debug était lancée avec un service Docker Swarm en publiant le port du Remote Debug.

Ce qui se passe donc, est que Docker écrit ses règles iptables avant ufw. En lisant de haut en bas les règles, la règle Docker arrive en premier, donc dans le cycle de vie du paquet, il n’arrive jamais jusqu’à la validation ufw : Docker bypass ufw dans ces conditions.

Maintenant que le problème et la cause se sont présentés, il n’est pas difficile de retrouver des ressources sur le web relatant de cela. La plupart d’ailleurs conseille de désactiver la gestion d’iptables par Docker. Cela semble intéressant au premier abord, par contre nous comprenons vite que les fonctionnalités propres à Docker en terme de networking sont donc inutilisables. Il existe mille raisons de ne pas s’arrêter à cette solution : réseau de containers, l’accès au réseau externe, etc.

Une solution difficile à maintenir mais qui permet de conserver toutes les features de Docker et d’ufw peut se trouver sur ce repository (chaifeng/ufw-docker – Solving UFW and Docker issues). Ce ne sera pas la solution la plus élégante mais fait preuve, je trouve, d’efficacité.

Leçon apprise sur plusieurs plan :

  • Même temporaire, tout ouverture de port nécessite une réflexion assez avancée : c’est notamment car il s’agit d’un fonctionnement en marge que la maîtrise est perdue,
  • Bien vérifier la compatibilité des outils utilisés quotidiennement,
  • Ne pas hésiter à mettre en place des outils de monitoring pour par exemple détecter des ports ouverts non désirés,
  • Avoir un système permettant de sécuriser ce genre d’attaque comme Endpoint Protection.

tl;dr

Modifez le fichier de configuration d’ufw  (/etc/ufw/after.rules) et ajoutez ces règles à la fin du fichier :

# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:ufw-docker-logging-deny - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j ufw-user-forward

-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16

-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN

-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 172.16.0.0/12

-A DOCKER-USER -j RETURN

-A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
-A ufw-docker-logging-deny -j DROP

COMMIT
# END UFW AND DOCKER

Puis relancez ufw : sudo systemctl restart ufw ou sudo ufw reload. Il se peut qu’un redémarrage soit nécessaire.

Tous les crédits vont à chaifeng.