Add new article on Cryptominers on CI
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
dfdac04fbc
commit
755c0394a1
202
content/post/post-mortem-cryptominer-on-CI-infrastructure.md
Normal file
202
content/post/post-mortem-cryptominer-on-CI-infrastructure.md
Normal file
@ -0,0 +1,202 @@
|
||||
---
|
||||
title: Post-mortem cryptominer on CI infrastructure
|
||||
date: !!timestamp '2022-03-06 19:32:00'
|
||||
tags:
|
||||
- security
|
||||
- cryptominer
|
||||
- continuous integration
|
||||
---
|
||||
|
||||
*[English version comming soon]*
|
||||
|
||||
Après les [grandes plateformes de
|
||||
cloud](https://stackoverflow.com/questions/63629722/gcp-has-suspended-my-machine-and-says-im-mining-cryptocurrency-how-do-i-fix-th),
|
||||
puis [d'intégration
|
||||
continue (CI)](https://therecord.media/github-investigating-crypto-mining-campaign-abusing-its-server-infrastructure/),
|
||||
se plaignant des cryptomineurs, il semble que la course à leur
|
||||
déploiement dans les petites installations de CI soit lancée.
|
||||
|
||||
Mon infrastructure a été la cible d'un hacker individuel hier, m'obligeant à
|
||||
changer moi aussi quelques éléments de configuration pour m'adapter à ce
|
||||
nouveau contexte.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
# Une sacrée surprise pour démarrer un beau samedi
|
||||
|
||||
Dans ma quête de séparation des GAFAM et de contrôle de mes données
|
||||
personnelles, j'héberge depuis de nombreuses années une forge qui
|
||||
devient de plus en plus centrale pour mes projets.
|
||||
|
||||
Il y a quelques années, je lui ai notamment ajouté une plateforme
|
||||
d'intégration continue : d'abord uniquement sur mon architecture de
|
||||
prédilection : arm64, puis voyant tous les bienfaits de la CI, j'ai
|
||||
ajouté une première machine amd64, pour les quelques déploiements que
|
||||
j'avais besoin avec cette architecture. Ma principale utilisation est
|
||||
la composition de documents avec `pandoc`, qui n'est pas disponible
|
||||
sur d'autres architectures.
|
||||
|
||||
|
||||
# Que s'est-il passé ?
|
||||
|
||||
Samedi matin, alors que je voulais lancer une tâche de composition de
|
||||
document, via Drone, je constatais que la tâche ne se lançait
|
||||
pas. Comme il s'agit d'un machine premier prix, elle a tendance à se mettre en défaut
|
||||
facilement. Je me connecte donc dessus et constate qu'une tâche est en
|
||||
cours, pourtant rien n'est affiché dans mon interface de DroneCI.
|
||||
|
||||
Je regarde directement les journaux du conteneur afin de déterminer de
|
||||
quel projet il s'agit mais je ne le reconnais pas : il s'agit d'un
|
||||
conteneur `ubuntu` (alors que la plupart de mes conteneurs utilisent
|
||||
`alpine`), et la dernière commande ne log rien et utilise `tensorflow`.
|
||||
Cela ne correspond vraiment à aucun des projets que j'héberge sur ma
|
||||
forge.
|
||||
|
||||
Comme j'ai déjà été victime de bots de spam sur mon instance Gitea,
|
||||
mon premier réflexe fut de consulter la liste des utilisateurs
|
||||
nouvellement inscrits, pour voir s'il n'y avait pas quelqu'un que je
|
||||
ne reconnaissais pas.
|
||||
|
||||
Sur la page listant les comptes utilisateurs, je constate la présence
|
||||
d'un utilisateur fraîchement enregistré, j'apprends qu'il s'est
|
||||
connecté via GitLab et qu'il s'est déjà créé un dépôt. En parcourant
|
||||
son dépôt, je m'aperçois que c'est bien le dépôt qui est en cours
|
||||
d'exécution.
|
||||
|
||||
|
||||
# Ma première réaction
|
||||
|
||||
Maintenant que l'origine du problème était établie, j'ai commencé par
|
||||
désactiver le compte de l'utilisation puis j'ai stoppé brutalement son
|
||||
conteneur, en utilisent `docker stop`.
|
||||
|
||||
Avant de désactiver le dépôt sur Drone, j'ai constaté que le pirate
|
||||
avait mis en place une tâche `cron` chaque heure pour relancer sa tâche
|
||||
de CI. Les tâches étant automatiquement arrêtées au bout de 60
|
||||
minutes, il avait donc toujours une tâche en cours d'exécution sur
|
||||
l'infrastructure.
|
||||
|
||||
|
||||
# Mon analyse minutieuse de la situation
|
||||
|
||||
![Dyn repository](repository.png)
|
||||
|
||||
Une fois le calme revenu, vient l'analyse du code et du contexte. Le
|
||||
code, qui est récupéré depuis un dépôt sur GitLab montre plusieurs
|
||||
scripts shell obfusqués par un simple encodage base64. Une fois
|
||||
décodé, on voit tout de suite que l'on a affaire à un cryptominer
|
||||
basique. Il ne semble pas y avoir un groupe bien rodé derrière cette
|
||||
intrusion, mais bien un individu qui explore les possibilités.
|
||||
|
||||
![Shell script](base64.png)
|
||||
|
||||
En remontant la piste jusqu'au portefeuille unique, on voit que les
|
||||
sommes perçues sont de l'ordre de 25$ par semaine, et cela fait une
|
||||
vingtaine de jours que notre protagoniste est actif.
|
||||
|
||||
Une rapide analyse des journaux a montré qu'il était arrivé sur mon
|
||||
instance en recherchant précisément sur Google un fichier
|
||||
`.drone.yml`. On le voit ensuite se connecter, via gitlab puis passer
|
||||
l'étape de liaison à un éventuel compte existant protégée par un
|
||||
captcha. Les temps entre chaque requête et les assets téléchargés
|
||||
laissent supposer qu'il s'agit bien d'un humain et que l'on a donc pas
|
||||
(encore) affaire à un script automatique. Voici le cheminement :
|
||||
|
||||
- arrivée sur git.nemunai.re depuis Google, sur un fichier `.drone.yml` ;
|
||||
- connexion via GitLab, via le lien OAuth2 disponible ;
|
||||
- création puis activation de son compte sur git.nemunai.re ;
|
||||
- recherche d'un dépôt sur la page d'accueil (mais apparemment j'en ai trop) ;
|
||||
- nouvelle arrivée depuis Google, sur un autre fichier `.drone.yml`, a priori le suivant dans la liste ;
|
||||
- passage sur la partie publique de DroneCI ;
|
||||
- connexion à Drone → création de l'utilisateur ;
|
||||
- retour sur Gitea pour créer le dépôt (il choisit la migration depuis GitLab) ;
|
||||
- synchronisation des dépôts sur Drone ;
|
||||
- activation de son dépôt ;
|
||||
- déclenchement manuel d'une synchronisation du dépôt afin de récupérer un nouveau commit, ce qui lance sa première tâche dans Drone ;
|
||||
- ajout de la tâche `cron` dans Drone pour relancer sa tâche chaque heure.
|
||||
|
||||
Tout cela s'est déroulé dans un intervalle de 6 minutes, ce n'était
|
||||
donc pas un robot, mais bien un humain qui savait ce qu'il faisait.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Mon instance Gitea est relié à un système de statistique de
|
||||
fréquentation (Umami, qui me permet de ne pas avoir de bandeau
|
||||
RGPD). Dessus, on voit effectivement la connexion depuis GitLab.com et
|
||||
l'accès au dépôt déclencheur, malgré le fait que l'utilisateur et le
|
||||
dépôt soient privés. D'après Umami, notre visiteur serait
|
||||
[indonésien](https://ipinfo.io/AS7713). Une rapide recherche sur le nom du compte GitLab qui a
|
||||
effectué la connexion montre également un profil LinkedIn indonésien
|
||||
d'un Deep Learning Specialist, ayant fait ses premiers pas sur GitLab
|
||||
il y a environ 2 semaines... Cependant le dépôt du crytominer n'est
|
||||
pas hébergé par son compte. On peut donc se demander s'il s'agit de la
|
||||
même personne ou si son compte GitLab a été compromis.
|
||||
|
||||
Dans le doute, j'ai fait un rapport d'abus sur GitLab pour cet utilisateur.
|
||||
|
||||
![Abuse filled to GitLab](abuse.png)
|
||||
|
||||
Je n'ai pas été contacté par GitLab suite à ce rapport, et je ne
|
||||
pensais d'ailleurs pas qu'il serait traité le jour même, considérant
|
||||
que d'une part il ne s'agit pas d'un problème critique comme de la
|
||||
pédo-pornographie ou similaire, et que, d'autre part, cela nécessitait
|
||||
sans doute de faire valider mon analyse par un ingénieur qualifié.
|
||||
|
||||
Toujours est-il que les deux comptes ont été bloqués dans les deux heures.
|
||||
|
||||
|
||||
# Remédiation
|
||||
|
||||
Estimant que j'avais une suffisamment bonne vision sur la situation et son
|
||||
contexte, je me suis enfin attelé à trouver les solutions pour remédier aux
|
||||
différents problèmes.
|
||||
|
||||
Tout d'abord mon instance Gitea a pour vocation d'être accessible à tous, afin
|
||||
que n'importe qui puisse facilement contribuer, notamment en faisant des
|
||||
rapports de bug ou des pull-requests sur mes projets.
|
||||
|
||||
Considérant le faible taux de création de fork, face aux problèmes que
|
||||
cela peut engendrer -- notamment via la création de pull-requests
|
||||
indésirables qui, en remplaçant la logique du `.drone.yml`, pourrait
|
||||
permettre de lancer un cryptominer jusqu'à l'expiration du délais
|
||||
d'exécution de la tâche -- j'ai donc décidé de changer le nombre maximal
|
||||
de dépôt que peut créer un nouvel utilisateur de 1 à 0. J'attendrai
|
||||
désormais qu'un utilisateur me sollicite afin de lui augmenter sa
|
||||
limite individuelle.
|
||||
|
||||
Concernant Drone, j'ai cherché une option permettant de limiter les
|
||||
utilisateurs autorisés à en faire usage.
|
||||
|
||||
Il se trouve qu'il y a justement une option :
|
||||
[`DRONE_USER_FILTER`](https://docs.drone.io/server/reference/drone-user-filter/)
|
||||
qui permet de lister les groupes d'utilisateurs et/on les utilisateurs
|
||||
individuels qui sont autorisés à se connecter.
|
||||
|
||||
Cette option répond exactement à mon besoin, je l'ai donc ajoutée aux options de
|
||||
déploiement de mon instance de DroneCI.
|
||||
|
||||
Pour être exhaustif, c'est également à ce moment que j'ai désactivé le
|
||||
dépôt du cryptominer et supprimé l'utilisateur malveillant de Drone,
|
||||
car il aurait encore pu s'y connecter.
|
||||
|
||||
|
||||
# Conclusion
|
||||
|
||||
J'ai été très agréablement surpris de la réactivité des équipes de
|
||||
GitLab : pour avoir désactivé les comptes contrevenants, malgré le
|
||||
fait que cela ne concernenait pas directement un abus des conditions
|
||||
d'utilisation de leur plateforme.
|
||||
|
||||
Je ne sais pas s'ils sont allés jusqu'à contacter tous les comptes
|
||||
pour lesquels l'utilisateur malveillant a fait usage de leur lien
|
||||
OAuth 2. D'autres infrastructures sont, en effet, toujours utilisées
|
||||
par ce hacker d'après les activités de son porte-feuille.
|
||||
|
||||
Une chose est sûre, plus que jamais, il faut bien faire attention aux
|
||||
options que l'on choisit lorsque l'on souhaite exposer un service au
|
||||
public, car on ne s'attend pas toujours à ce qui peut se passer. Et
|
||||
bien que cet utilisateur ait été bloqué, rien ne l'empêche de
|
||||
continuer de chercher de nouvelles forges à exploiter.
|
||||
|
||||
![Blocked users](blocked-users.png)
|
Binary file not shown.
After Width: | Height: | Size: 107 KiB |
Binary file not shown.
After Width: | Height: | Size: 192 KiB |
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
Binary file not shown.
After Width: | Height: | Size: 161 KiB |
Loading…
x
Reference in New Issue
Block a user