2026 Bash Linux Sécurité Discord GitHub Actions Open Source

bq-watchdog

Un script d'audit de sécurité Linux open-source né de l'infection XMRig — détection d'IoCs, alertes Discord, installable en une commande.

bq-watchdog

Genèse

Après avoir trouvé et nettoyé un mineur XMRig sur mon VPS (voir l'article dédié), une question s'est posée naturellement : comment faire en sorte que ça ne se reproduise plus silencieusement pendant 5 mois ?

J'aurais pu mettre en place un monitoring générique — Wazuh, Falco, AIDE. Mais ces outils sont lourds, compliqués à configurer, et ne connaissent pas les IoCs spécifiques de la campagne Diicot/color1337 que j'avais tracée.

J'ai préféré écrire un script ciblé, léger, modulaire, et open-source.

Ce que ça fait

bq-watchdog est un moniteur de sécurité Linux en bash pur. Il tourne en cron toutes les 30 minutes, effectue une série de checks, et n'envoie une alerte Discord que si quelque chose de suspect est détecté. Silencieux quand tout va bien.

Installation

root@serveur:~
×
curl -fsSL https://github.com/BugQuest/bq-watchdog/releases/latest/download/install.sh | sudo bash

Architecture modulaire

Chaque check est un fichier bash indépendant dans `checks/`. Le script principal les charge tous dans l'ordre numérique et appelle automatiquement la fonction correspondante. Ajouter un check = créer un fichier.

checks/08-mon-check.sh
×
check_mon_check() {
    # finding <warning|critical> "titre" "détail"
    if [[ -f /fichier/suspect ]]; then
        finding critical "Fichier suspect" "Chemin: /fichier/suspect"
    fi
}

Checks inclus

**01 — IoC color1337/Diicot** : clé SSH backdoor ElPatrono1337, fichiers `.ladyg0g0` `.pr1nc35` `.b4nd1d0`, connexions vers les C2 connus (195.24.237.240, digital.digitaldatainsights.org), binaires hex obfusqués, service `myservices.service`, compte `node`.

**02 — Config SSH** : `PasswordAuthentication yes` dans la config effective (incluant les overrides cloud-init), `PermitRootLogin yes`, `PermitEmptyPasswords yes`, le piège classique `/etc/ssh/sshd_config.d/50-cloud-init.conf`.

**03 — Crontabs** : patterns `curl | bash`, exécution depuis `/tmp`, encodage base64, IPs C2 hardcodées, scripts perl/python one-liners.

**04 — Fichiers temp** : binaires ELF dans `/tmp`, `/var/tmp`, `/dev/shm`, répertoires cachés dans ces emplacements.

**05 — Users/clés SSH** : comptes système avec shell interactif, clés SSH au nom suspect (`1337`, `h4x`, `backdoor`...), nouveaux comptes sudo créés récemment.

**06 — Réseau** : connexions vers IPs/ranges malveillants connus, ports stratum mining (3333, 4444, 5555...), processus depuis `/tmp` avec connexions actives.

**07 — Processus** : noms hexadécimaux à 8 caractères (pattern Diicot), exécution depuis répertoires temporaires, binaires supprimés toujours en mémoire, mineurs connus (`xmrig`, `kswapd0`...).

Alertes Discord

Chaque finding produit un embed Discord coloré — rouge pour critique, jaune pour avertissement. L'embed de résumé donne l'hostname, le timestamp UTC, et le nombre total de findings. Les détails suivent avec le contexte complet.

lib/notify.sh — embed Discord
×
# Extrait : construction du payload Discord
discord_send() {
    local embeds="$1"

    # Couleur selon la sévérité (rouge critique / jaune warning)
    if [[ $SEVERITY -ge 2 ]]; then
        status_color=15158332; status_emoji="🚨"
    else
        status_color=16776960; status_emoji="⚠️"
    fi

    # Embed de résumé + liste des findings
    curl -fsSL -X POST "$DISCORD_WEBHOOK" \
        -H "Content-Type: application/json" \
        -d "$(jq -n --argjson e "$all_embeds" \
            '{username:"bq-watchdog",embeds:$e}')"
}

Releases GitHub Actions

Un workflow GitHub Actions génère automatiquement une release à chaque tag git. La release contient le tarball compressé et le script d'installation. L'URL d'installation reste stable via `/releases/latest/`.

.github/workflows/release.yml
×
# Déclenché par: git tag v1.1.0 && git push --tags
on:
  push:
    tags: ["v*"]

steps:
  - name: Create tarball
    run: |
      tar -czf dist/bq-watchdog-${{ steps.version.outputs.VERSION }}.tar.gz \
        --exclude='.git' --exclude='dist' --exclude='.github' .

  - name: Create GitHub Release
    uses: softprops/action-gh-release@v2
    with:
      files: |
        dist/bq-watchdog-*.tar.gz
        install.sh

Mises à jour

Le script gère ses propres mises à jour. Une vérification automatique tourne chaque lundi à 3h via cron — si une nouvelle release est disponible sur GitHub, elle est téléchargée, installée, et le fichier `config` est préservé. Une notification Discord confirme la mise à jour.

root@serveur:~
×
# Mise à jour manuelle
sudo /opt/bq-watchdog/watchdog.sh --update

# Version installée
sudo /opt/bq-watchdog/watchdog.sh --version

Faux positifs rencontrés

Déployer sur un vrai serveur, c'est le meilleur test. Trois faux positifs ont remonté dès le premier audit, et ont donné lieu à trois corrections ciblées.

**certbot + perl** — Le cron de renouvellement Let's Encrypt contient `perl -e 'sleep int(rand(43200))'` pour randomiser l'heure de renouvellement. Le pattern `perl.*-e` était trop large. Corrigé pour n'alerter que si le one-liner perl fait appel à des fonctions réellement dangereuses : `socket`, `exec`, `system`, `eval`, `base64`, `chr()`.

**vmail** — Le compte de messagerie virtuelle (Dovecot) tourne avec `/bin/sh`, ce qui est attendu sur un serveur mail. Corrigé avec une whitelist des comptes de service connus : `vmail`, `dovecot`, `postfix`, `www-data`, `git`, `postgres`...

**kswapd0** — Le thread kernel `[kswapd0]` (gestion du swap) a le même nom qu'un mineur XMRig connu. La distinction est simple : un thread kernel n'a pas de symlink `/proc/<pid>/exe`, contrairement à un processus userspace. Le bug venait de `readlink -f` qui retourne le chemin littéral quand la cible n'existe pas — remplacé par `readlink` sans `-f`, qui retourne vide dans ce cas.

Roadmap

La v1 couvre les IoCs connus de la campagne Diicot et les vecteurs génériques. La suite envisagée :

- Ajout d'autres familles de malwares Linux documentées
- Option `--report` pour sortie JSON machine-readable
- Uninstaller propre
- Support des webhooks Slack et ntfy en plus de Discord

realitynauts@bugquest:~
×
Realitynauts
RN

$ cat auteur.txt

Realitynauts — Dev PHP / Python / Hardware
Autiste. Intensément focalisé. Toujours en prod.

$ cat opportunites.txt

Ce projet te parle ? Tu as un projet similaire
ou complémentaire ? Parlons-en.

$ ./contact.sh