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