|8 min de lecture|Yvann Lièvre

Détecter le cryptojacking en conteneur avec Falco et Tetragon

Détection cryptojacking Kubernetes Falco : la règle qui repère un binaire lancé depuis /tmp, les connexions vers un pool de minage, et ce qui reste en HITL.

FalcoTetragonCryptojacking
Détecter le cryptojacking en conteneur avec Falco et Tetragon

Un conteneur qui consomme 100 % de CPU en continu n'est pas forcément en train de traiter une charge légitime. C'est souvent le symptôme le plus visible d'un cryptojacking : un mineur de crypto-monnaie installé par un attaquant, tournant discrètement sur une infrastructure qui n'est pas la sienne. Le point d'entrée réel n'est presque jamais une image malveillante publiée sciemment sur un registre public : c'est le plus souvent une image légitime, populaire, compromise via une CVE non corrigée, exploitée depuis l'extérieur, puis utilisée pour déposer et lancer un binaire de minage depuis /tmp. La question que se pose un RSSI face à un pic CPU anormal est simple : un de mes conteneurs mine-t-il de la crypto pour un attaquant, et quelle règle Falco le détecte sans noyer l'équipe sous les faux positifs ?

Le scénario réel : de l'image compromise au mineur

La chaîne suit presque toujours le même schéma. Un service exposé publiquement (une API, une application web, un endpoint mal isolé) tourne dans une image dont une dépendance porte une CVE connue, souvent avec un exploit public. L'attaquant obtient l'exécution de code dans le conteneur, télécharge un binaire de minage (XMRig et ses dérivés reviennent le plus souvent) dans un répertoire inscriptible comme /tmp ou un volume monté, puis le lance. Le binaire se connecte ensuite à un pool de minage externe via le protocole Stratum, et consomme le CPU alloué au conteneur pendant des jours, parfois des semaines, avant d'être repéré sur une facture cloud ou un graphique de supervision.

Trois signaux techniques permettent de couper cette chaîne bien avant la facture : l'exécution depuis un répertoire inhabituel, la connexion réseau vers un pool de minage, et la corrélation entre pic CPU et process enfant inattendu sur un conteneur exposé. Aucun des trois pris isolément ne suffit à conclure sans bruit. Ensemble, ils forment une détection fiable.

Règle Falco : exécution depuis /tmp ou un volume monté

Falco observe les appels système au niveau du noyau et expose l'événement spawned_process à chaque nouveau processus. Le signal le plus direct d'un mineur qui démarre est un exécutable dont le chemin (proc.exepath) se trouve dans /tmp, /var/tmp, ou un volume monté dans le conteneur, ce qui n'est presque jamais un comportement d'application légitime en production :

- rule: Binaire exécuté depuis un répertoire inscriptible du conteneur
  desc: >
    Détecte l'exécution d'un binaire depuis /tmp, /var/tmp ou un volume
    monté à l'intérieur d'un conteneur, signal fréquent d'un dépôt
    puis lancement de mineur de crypto-monnaie après compromission.
  condition: >
    spawned_process
    and container.id != host
    and (proc.exepath startswith /tmp
         or proc.exepath startswith /var/tmp
         or fd.name startswith /tmp)
    and not proc.name in (allowed_tmp_binaries)
  output: >
    Exécution suspecte depuis un répertoire temporaire
    (container=%container.name image=%container.image.repository
    proc=%proc.name exepath=%proc.exepath cmdline=%proc.cmdline)
  priority: WARNING
  tags: [cryptomining, container, process]

Le not proc.name in (allowed_tmp_binaries) n'est pas décoratif : sans cette exclusion, la règle déclenche sur toute image de build qui compile ou extrait des artefacts dans /tmp, ce qui est un comportement parfaitement normal pour des pipelines CI ou des images multi-stage. On y revient dans la section anti-faux positifs.

Détecter la connexion sortante vers un pool de minage

Un mineur qui tourne doit parler à un pool pour soumettre ses résultats de calcul. Le protocole le plus courant est Stratum (ports 3333, 4444, 5555, 7777, 8080 selon les pools, souvent en TCP brut ou en TLS sur des ports non standards). Deux approches se combinent : une règle réseau Falco sur les connexions sortantes, et un enrichissement par un feed IOC de domaines et d'adresses de pools connus.

- rule: Connexion sortante vers un pool de minage connu
  desc: >
    Détecte une connexion réseau sortante depuis un conteneur vers un
    domaine ou une adresse IP associée à un pool de minage connu,
    ou vers un port Stratum typique sans contexte applicatif attendu.
  condition: >
    outbound
    and container.id != host
    and (fd.sip.name in (known_mining_pool_domains)
         or fd.rport in (3333, 4444, 5555, 7777, 8080)
         or fd.sip in (mining_pool_ip_ioc_list))
  output: >
    Connexion sortante vers pool de minage suspecté
    (container=%container.name proc=%proc.name
    dest=%fd.sip.name:%fd.rport cmdline=%proc.cmdline)
  priority: CRITICAL
  tags: [cryptomining, network, exfiltration]

La liste mining_pool_ip_ioc_list doit venir d'un feed IOC entretenu et daté, pas d'une liste figée copiée une fois puis jamais mise à jour : les pools changent d'infrastructure régulièrement, et une liste statique se périme en quelques mois. C'est un travail de curation continue, pas une règle qu'on écrit puis qu'on oublie.

Corréler pic CPU et process enfant inattendu

Un exec depuis /tmp seul peut être un faux positif d'outillage. Une connexion vers un port 3333 seule peut être un test ou un service legacy mal nommé. Ce qui élève la confiance, c'est la corrélation : un conteneur exposé publiquement (donc avec une surface d'attaque directe) qui, en dehors de toute fenêtre de déploiement, fait apparaître un process enfant qu'aucune image de référence ne prévoit, en même temps qu'un pic de consommation CPU soutenu sur plusieurs minutes.

# Repérage manuel côté orchestrateur : conteneurs exposés
# avec un CPU soutenu au-dessus de 90 % et un process enfant
# non présent dans l'image de base
kubectl top pods --containers --sort-by=cpu \
  --field-selector metadata.namespace=production
 
# Cross-check : le pod a-t-il un service exposé publiquement ?
kubectl get svc -n production -o wide | grep LoadBalancer

C'est cette corrélation, exec inattendu + connexion Stratum + pic CPU sur une surface exposée, qui distingue un mineur d'un simple bruit d'exploitation, et qui justifie de traiter l'alerte comme critique plutôt que comme une simple ligne de log.

Anti-faux positifs : ne pas alerter aveuglément sur tout exec en /tmp

C'est le point où la plupart des déploiements Falco produisent trop de bruit et finissent ignorés. Trois exclusions structurent une règle utilisable en production :

  • Les images de build légitimes. Les images qui compilent, décompressent des archives ou installent des dépendances au démarrage (init containers, jobs CI) écrivent et exécutent régulièrement depuis /tmp. Scoper la règle en excluant les labels ou les namespaces dédiés au build (namespace != ci-build, container.image.repository not in (build_images)) évite l'essentiel du bruit.
  • Le scope par namespace et par label. Une règle qui s'applique uniformément à tout le cluster traite un environnement de dev comme un environnement de production exposé. Restreindre la sévérité ou l'activation par label (k8s.ns.label.environment == "production") concentre l'attention là où le risque est réel.
  • L'allowlist de binaires connus. Certains outils d'observabilité ou agents de sécurité légitimes déposent eux-mêmes des binaires temporaires. Une allowlist explicite, courte et documentée, vaut mieux qu'une règle qui ignore tout /tmp par défaut.

La règle qui alerte sur tout exec dans /tmp sans ces trois filtres n'est pas une détection, c'est un générateur de tickets qu'on finit par désactiver. La preuve de sa qualité se fait sur un corpus bénin représentatif de vos propres images avant mise en production, pas sur l'intuition qu'elle "a l'air correcte".

Falco pour la détection, Tetragon pour l'enforcement

Falco observe et alerte, mais n'agit pas par défaut au niveau noyau. Pour l'enforcement, c'est-à-dire bloquer ou tuer le process au moment de l'exécution, Tetragon (eBPF, projet Cilium) permet d'écrire une politique équivalente mais avec une action de blocage active :

apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: block-exec-from-tmp
spec:
  kprobes:
  - call: "security_bprm_check"
    syscall: false
    args:
    - index: 0
      type: "linux_binprm"
    selectors:
    - matchBinaries:
      - operator: "Prefix"
        values:
        - "/tmp/"
        - "/var/tmp/"
      matchActions:
      - action: Sigkill

Le passage d'une règle Falco (observation) à une politique Tetragon (blocage kernel automatique) est une décision d'enforcement, pas une simple bascule de configuration. Avant de l'activer en mode kill automatique, il faut avoir fait tourner la règle en mode observation seule pendant une période suffisante sur l'environnement cible, et avoir mesuré son taux de faux positifs sur le trafic réel de vos images de build et de vos jobs légitimes. Une politique Tetragon en Sigkill mal scopée peut interrompre un pipeline CI légitime aussi efficacement qu'un mineur.

Quels signaux justifient un kill automatique, lesquels restent en HITL

Tous les signaux ne méritent pas la même réponse automatisée :

  • Kill automatique défendable : connexion confirmée vers un pool de minage identifié par IOC daté, combinée à un exec depuis /tmp sur un conteneur qui n'a aucune raison fonctionnelle d'y exécuter quoi que ce soit (image de production applicative, pas de build). La combinaison des deux signaux réduit fortement le risque de faux positif.
  • Reste en supervision humaine (HITL) : un exec depuis /tmp isolé, sans connexion réseau suspecte associée ; un pic CPU sans process enfant inattendu identifié ; toute alerte sur un namespace de build, de CI ou de développement où l'écriture en /tmp est un comportement normal attendu.

Le kill automatique n'est justifié que lorsque la combinaison de signaux dépasse largement le bruit de fond mesuré sur votre propre corpus bénin. Dans tous les autres cas, l'alerte doit remonter à un opérateur qui valide avant toute action de remédiation, en particulier sur des conteneurs de production dont l'arrêt a un coût métier.

En résumé

Détecter un cryptojacking en conteneur ne repose pas sur une seule règle magique : c'est la combinaison d'un signal d'exécution (/tmp, volume monté), d'un signal réseau (pool de minage via IOC daté) et d'un signal de corrélation (pic CPU sur une surface exposée) qui donne une alerte exploitable sans noyer l'équipe. Falco couvre l'observation, Tetragon permet l'enforcement kernel quand la confiance est prouvée, pas supposée.

C'est exactement cette méthode, règles validées sur moteur réel, anti-faux positifs prouvés sur corpus bénin, priorisation par exposition réelle, que nous appliquons dans le pack cloud native ThreatClaw : les règles de détection cryptojacking arrivent prêtes à l'emploi, testées, sans que vous ayez à refaire ce travail de validation vous-même.

Articles liés