Cyril François

Faire la lumière sur le conducteur ABYSSWORKER

Elastic Security Labs décrit ABYSSWORKER, un pilote malveillant utilisé avec la chaîne d'attaque du ransomware MEDUSA pour désactiver les outils anti-malware.

Faire la lumière sur le conducteur ABYSSWORKER

Résumé

Les cybercriminels introduisent de plus en plus souvent leurs propres pilotes, soit en exploitant un pilote légitime vulnérable, soit en utilisant un pilote personnalisé pour désactiver les systèmes de détection et de réponse des points finaux (EDR) et échapper aux capacités de détection ou de prévention.

Elastic Security Labs a surveillé une campagne financièrement motivée déployant le ransomware MEDUSA à l'aide d'un chargeur HEARTCRYPT. Ce chargeur a été déployé en même temps qu'un pilote signé par un certificat révoqué d'un fournisseur chinois que nous appelons ABYSSWORKER, qu'il installe sur l'ordinateur de la victime et utilise ensuite pour cibler et réduire au silence différents fournisseurs d'EDR. Ce pilote EDR-killer a récemment été signalé par ConnectWise dans le cadre d'une autre campagne, utilisant un certificat différent et des codes de contrôle IO, et certaines de ses capacités ont alors été discutées. En 2022, Google Cloud Mandiant a divulgué un pilote malveillant appelé POORTRY qui, selon nous, est la première mention de ce pilote.

Dans cet article, nous examinons en profondeur ce pilote, en passant en revue ses différentes caractéristiques et techniques. Nous fournissons également des adresses virtuelles relatives (RVA) sous chaque capture d'écran de code inversé afin de relier la recherche à l'échantillon de référence, ainsi qu'un petit exemple de client que vous pouvez utiliser pour poursuivre vos expériences avec ce logiciel malveillant.

Analyse technique

En-tête PE

Le binaire est un pilote Windows PE 64 bits nommé smuol.sys, et imite un pilote CrowdStrike Falcon légitime.

Au moment de l'analyse, nous avons trouvé une douzaine d'échantillons sur VirusTotal, datant du 2024-08-08 au 2025-02-24. La plupart étaient emballés par VMProtect, mais deux d'entre eux - référencés dans les tableaux observables ci-dessous - n'étaient pas protégés.

Tous les échantillons sont signés à l'aide de certificats probablement volés et révoqués de sociétés chinoises. Ces certificats sont largement connus et partagés par différents échantillons et campagnes de logiciels malveillants, mais ne sont pas spécifiques à ce pilote. Les empreintes digitales des certificats sont énumérées ci-dessous :

fingerprintNom
51 68 1b 3c 9e 66 5d d0 b2 9e 25 71 46 d5 39 dcFoshan Gaoming Kedeyu Insulation Materials Co. Ltd
7f 67 15 0f bb 0d 25 4e 47 42 84 c7 f7 81 9c 4fFEI XIAO
72 88 1f 10 cd 24 8a 33 e6 12 43 a9 e1 50 ec 1dFuzhou Dingxin Trade Co, Ltd.
75 e8 e7 b9 04 3b 13 df 60 e7 64 99 66 30 21 c1Changsha Hengxiang Information Technology Co. Ltd
03 93 47 e6 1d ec 6f 63 98 d4 d4 6b f7 32 65 6cXinjiang Yishilian Network Technology Co, Ltd
4e fa 7e 7b ba 65 ec 1a b7 74 f2 b3 13 57 d5 99Shenzhen Yundian Technology Co. Ltd

Obfuscation

ABYSSWORKER utilise des fonctions qui renvoient toujours la même valeur, en s'appuyant sur une combinaison de prédicats opaques et d'autres fonctions de dérivation. Par exemple, la fonction à retour nul ci-dessous renvoie toujours une adresse 0 basée sur des valeurs dérivées codées en dur.

Vous trouverez ci-dessous l'une des fonctions de dérivation :

Ces fonctions à retour constant sont appelées à plusieurs reprises dans le binaire, ce qui entrave l'analyse statique. Cependant, il n'existe que trois fonctions de ce type, qui ne sont pas utilisées dans un prédicat mais simplement appelées. Nous pouvons facilement les identifier, ce qui rend ce système d'obscurcissement inefficace.

Initialisation

Lors de l'initialisation, le pilote commence par obtenir des pointeurs vers plusieurs modules du noyau et sa fonction de protection du client, qui seront abordés dans les sections suivantes.

Il crée ensuite un périphérique avec le chemin \\device\\czx9umpTReqbOOKF et un lien symbolique avec le chemin \\??\\fqg0Et4KlNt4s1JT.

Il complète l'initialisation en enregistrant des rappels pour ses principales fonctions.

Protection du client à l'ouverture de l'appareil

Lorsque le périphérique du pilote est ouvert, le rappel IRP_MJ_CREATE major est appelé. Cette fonction est chargée d'ajouter l'ID du processus à la liste des processus à protéger et de supprimer de la liste des processus en cours d'exécution tous les handles préexistants vers le processus cible.

La fonction récupère l'identifiant du processus dans le thread du noyau actuel, car le rappel du noyau est exécuté dans le contexte du processus client lors de l'ouverture du périphérique.

Avant d'ajouter l'ID du processus à la liste de protection, ABYSSWORKER recherche et supprime tous les handles existants vers le processus client dans d'autres processus en cours d'exécution.

Pour ce faire, le logiciel malveillant itère sur les processus existants en forçant brutalement leurs identifiants de processus (PID) afin de ne pas dépendre d'une quelconque API. Pour chaque processus, il parcourt leurs handles, également en utilisant la force brute, et vérifie si l'objet sous-jacent correspond au processus client. Si une correspondance est trouvée, les droits d'accès sont supprimés à l'aide de la valeur transmise en tant que paramètre (0x8bb).

Enfin, il ajoute le PID à la liste globale des processus protégés.

Comme indiqué précédemment, le pilote met en place sa fonction de protection au cours de la phase d'initialisation. Cette protection repose sur l'enregistrement de deux rappels pre-operation à l'aide de l'API ObRegisterCallback: l'un pour détecter l'ouverture des handles vers ses processus protégés et l'autre pour détecter l'ouverture des handles vers les threads de ces processus protégés.

Les deux rappels fonctionnent de la même manière : ils fixent l'accès souhaité pour le handle à zéro, refusant ainsi la création du handle.

Gestionnaires DeviceIoControl

Lorsqu'il reçoit une demande de contrôle des E/S d'un périphérique, ABYSSWORKER la transmet aux gestionnaires en fonction du code de contrôle des E/S. Ces gestionnaires couvrent un large éventail d'opérations, de la manipulation de fichiers à l'arrêt de processus et de pilotes, fournissant un ensemble complet d'outils qui peuvent être utilisés pour mettre fin ou désactiver de façon permanente les systèmes EDR.

Nous détaillons les différents contrôles des OI dans le tableau ci-dessous :

NomCode
Activer les logiciels malveillants0x222080
Copier le fichier0x222184
Supprimez les callbacks et les dispositifs par nom de module0x222400
Remplacer les fonctions principales du pilote par le nom du module0x222404
Tuer les threads du système par nom de module0x222408
Détachez les mini-filtres0x222440
Supprimer le fichier0x222180
Désactiver les logiciels malveillants0x222084
Charger l'api0x2220c0
Diminuer le compteur de référence de tous les conducteurs0x222100
Diminuer le compteur de référence de tous les appareils0x222104
Fin du processus0x222144
Terminer le fil0x222140
Suppression des crochets des principales fonctions des pilotes Ntfs et Pnp0x222444
Reboot0x222664

Activation du logiciel malveillant (0x222080)

Comme indiqué dans ce billet, le client doit activer le pilote en envoyant un mot de passe (7N6bCAoECbItsUR5-h4Rp2nkQxybfKb0F-wgbJGHGh20pWUuN1-ZxfXdiOYps6HTp0X) au pilote, dans notre cas par l'intermédiaire du contrôle IO 0x222080.

Le gestionnaire compare simplement la saisie de l'utilisateur avec le mot de passe codé en dur. S'il est correct, il attribue la valeur true (1) à un indicateur global. Cet indicateur est vérifié dans tous les autres gestionnaires afin d'autoriser ou de refuser l'exécution.

Chargement de l'API (0x2220c0)

La plupart des gestionnaires des logiciels malveillants s'appuient sur des API du noyau qui doivent être chargées à l'aide de ce gestionnaire. Ce gestionnaire charge ces globaux ainsi que plusieurs structures, en utilisant les pointeurs de modules du noyau précédemment chargés lors de l'initialisation. Une fois le chargement terminé, un drapeau global est activé pour signaler la disponibilité de ces API.

Ce gestionnaire a deux modes de fonctionnement : un mode complet et un mode partiel. En mode complet, il charge les API à l'aide d'une structure de mappage des noms de fonctions et des RVA fournis par l'utilisateur en tant qu'entrée de la commande IO. En mode partiel, il recherche lui-même certaines API, mais ne charge pas toutes les API qui sont chargées en mode complet, d'où le terme de mode partiel. Si l'utilisateur opte pour le mode partiel en raison de l'impossibilité de fournir cette structure de mappage, certains gestionnaires ne s'exécuteront pas. Dans ce chapitre, nous ne couvrons que le mode de fonctionnement complet.

Nous détaillons ci-dessous les structures utilisées :

#define AM_NAME_LENGTH 256
typedef struct _struct_435
{
   uint64_t rva;
   char name[AM_NAME_LENGTH];
} struct_435_t;

#define AM_ARRAY_LENGTH 1024
typedef struct _struct_433
{
   struct_435_t array[AM_ARRAY_LENGTH];
   uint32_t length;
} struct_433_t;

Vous trouverez ci-dessous un bref exemple d'utilisation :

struct_433_t api_mapping = {
    .length = 25,
    .array = {
        [0] = {.rva = 0xcec620, .name = "PspLoadImageNotifyRoutine"},
        [1] = {.rva = 0xcec220, .name = "PspCreateThreadNotifyRoutine"},
        [2] = {.rva = 0xcec420, .name = "PspCreateProcessNotifyRoutine"},
        // (...)
        [24] = {.rva = 0x250060, .name = "NtfsFsdShutdown"},
}};

uint32_t malware_load_api(HANDLE device)
{
    return send_ioctrl(device, IOCTRL_LOAD_API, &api_mapping, sizeof(struct_433_t), NULL, 0);
}

Pour charger son API, la fonction commence par charger trois "listes de rappels" à partir de différents types d'objets du noyau. Ils sont utilisés par le gestionnaire qui supprime les rappels de notification enregistrés appartenant à un module spécifique.

Ensuite, il charge les pointeurs vers les fonctions en utilisant la structure fournie, en recherchant simplement le nom de la fonction et en ajoutant le RVA associé à l'adresse de base du module.

C'est le cas pour les fonctions suivantes : 25 :

  • PspLoadImageNotifyRoutine
  • PspCreateThreadNotifyRoutine
  • PspCreateProcessNotifyRoutine
  • CallbackListHead
  • PspSetCreateProcessNotifyRoutine
  • PspTerminateThreadByPointer
  • PsTerminateProcess
  • IopInvalidDeviceRequest
  • ClassGlobalDispatch
  • NtfsFsdRead
  • NtfsFsdWrite
  • NtfsFsdLockControl
  • NtfsFsdDirectoryControl
  • NtfsFsdClose
  • NtfsFsdCleanup
  • NtfsFsdCreate
  • NtfsFsdDispatchWait
  • NtfsFsdDispatchSwitch
  • NtfsFsdDispatch
  • NtfsFsdFlushBuffers
  • NtfsFsdDeviceControl
  • NtfsFsdFileSystemControl
  • NtfsFsdSetInformation
  • NtfsFsdPnp
  • NtfsFsdShutdown

Copie et suppression de fichiers (0x222184, 0x222180)

Pour copier ou supprimer des fichiers, ABYSSWORKER s'appuie sur une stratégie qui, bien qu'elle ne soit pas nouvelle, reste intéressante. Au lieu d'utiliser une API commune comme NtCreateFile, un paquet de demandes d'E/S (IRP) est créé à partir de zéro et envoyé directement à l'unité de disque correspondante contenant le fichier cible.

Création d'un fichier

La fonction de création de fichiers est utilisée pour illustrer le fonctionnement de ce mécanisme. La fonction commence par obtenir l'unité d'entraînement à partir du chemin d'accès au fichier. Ensuite, un nouvel objet fichier est créé et lié au lecteur cible, en veillant à ce que le nouvel objet soit correctement lié au lecteur.

Il crée ensuite un nouvel objet IRP et définit toutes les données nécessaires pour effectuer l'opération de création de fichier. La fonction principale visée par cette IRP est spécifiée dans la propriété MajorFunction, qui, dans ce cas, est définie sur IRP_MJ_CREATE, comme prévu pour la création de fichiers.

Le logiciel malveillant envoie ensuite l'IRP à l'unité de disque cible. Il aurait pu utiliser l'API IoCallDriver pour ce faire, mais il envoie l'IRP manuellement en appelant la fonction principale de l'appareil correspondant.

À ce stade, l'objet fichier est valide pour une utilisation ultérieure. Le gestionnaire termine son travail en incrémentant le compteur de référence de l'objet fichier et en l'affectant à son paramètre de sortie pour une utilisation ultérieure.

Copier un fichier

Pour copier un fichier, ABYSSWORKER ouvre les fichiers source et destination, puis lit (IRP_MJ_READ) dans le fichier source et écrit (IRP_MJ_WRITE) dans le fichier destination.

Suppression d'un fichier

Le gestionnaire de suppression attribue à l'attribut de fichier la valeur ATTRIBUTE_NORMAL pour déprotéger un fichier en lecture seule et attribue à la disposition du fichier la valeur delete (disposition_info.DeleteFile = 1) pour supprimer le fichier à l'aide de l'IRP IRP_MJ_SET_INFORMATION.

Suppression des callbacks de notification par nom de module (0x222400)

Les clients malveillants peuvent utiliser ce gestionnaire pour masquer les produits EDR et leur visibilité. Il recherche et supprime tous les rappels de notification enregistrés. Les rappels ciblés sont ceux qui sont enregistrés avec les API suivantes :

  • PsSetCreateProcessNotifyRoutine
  • PsSetLoadImageNotifyRoutine
  • PsSetCreateThreadNotifyRoutine
  • ObRegisterCallbacks
  • CmRegisterCallback

En outre, il supprime les rappels enregistrés par l'intermédiaire d'un pilote MiniFilter et, éventuellement, les dispositifs appartenant à un module spécifique.

Pour supprimer ces rappels de notification, le gestionnaire les localise à l'aide de diverses méthodes, telles que les trois listes globales de rappels précédemment chargées dans le gestionnaire de l'API de chargement, qui contiennent des rappels enregistrés auprès de ObRegisterCallbacks et CmRegisterCallback. Il les supprime ensuite à l'aide des API correspondantes, comme ObUnRegisterCallbacks et CmUnRegisterCallbacks.

L'aveuglement de la RDE par ces méthodes mériterait un article de blog à part entière. Pour rester concis, nous ne donnerons pas plus de détails ici, mais nous invitons le lecteur à explorer ces méthodes dans deux projets bien documentés qui mettent en œuvre ces techniques :

Remplacer les fonctions principales du pilote par le nom du module 0x222404

Une autre façon d'interférer avec un pilote est d'utiliser ce gestionnaire pour remplacer toutes ses fonctions principales par une fonction factice, désactivant ainsi toute interaction avec le pilote, à partir d'un nom de module cible.

Pour ce faire, ABYSSWORKER parcourt les objets pilotes dans les répertoires d'objets Driver et Filesystem. Pour chaque objet pilote, il compare le nom du module sous-jacent au module cible et, s'ils correspondent, il remplace toutes ses fonctions principales par IopInvalidDeviceRequest.

Détacher les mini-filtres (0x222440)

Ce gestionnaire passe en revue tous les objets pilotes trouvés dans les répertoires d'objets Driver et FileSystem. Pour chaque pilote, il explore son arborescence de périphériques et détache tous les périphériques associés au pilote du mini-filtre : FltMgr.sys.

La fonction fonctionne en parcourant les périphériques du pilote via les pointeurs AttachedDevice et NextDevice, en récupérant le nom du module du pilote associé à chaque périphérique et en le comparant au nom du module cible transmis en tant que paramètre (”FltMgr.sys”). Si les noms correspondent, il utilise la fonction IoDetachDevice pour dissocier l'appareil.

Tuer les threads système par nom de module (0x222408)

Ce gestionnaire itère sur les threads en forçant brutalement leur ID et les tue si le thread est un thread système et que son adresse de départ appartient au module ciblé.

Pour mettre fin au thread, le logiciel malveillant met en attente un APC (appel de procédure asynchrone) afin d'exécuter un code dans le contexte du thread ciblé. Une fois exécuté, ce code appellera à son tour PsTerminateSystemThread.

Terminer le processus et le thread (0x222144, 0x222140)

Grâce à ces deux gestionnaires, vous pouvez mettre fin à un processus ou à un thread par son PID ou son Thread ID (TID) en utilisant PsTerminateProcess et PsTerminateThread.

Suppression des crochets des fonctions principales des pilotes Ntfs et Pnp (0x222444)

En plus d'enregistrer des rappels de notification, certains EDR aiment accrocher des fonctions majeures des pilotes NTFS et PNP. Pour supprimer ces crochets, le logiciel malveillant peut appeler ce pilote afin de rétablir les principales fonctions d'origine de ces pilotes.

ABYSSWORKER passe simplement en revue chaque fonction majeure enregistrée, vérifie si la fonction appartient au module pilote, et si ce n'est pas le cas, cela signifie que la fonction a été accrochée, et il la remplace donc par les fonctions d'origine.

Reboot 0x222664

Pour redémarrer la machine, ce gestionnaire utilise la fonction non documentée HalReturnToFirmware.

Exemple de mise en œuvre par le client

Dans ce billet de blog, nous vous proposons un petit exemple de mise en œuvre par un client. Cet exemple fonctionne avec l'échantillon de référence et a été utilisé pour le déboguer, mais il n'implémente pas tous les IOCTRL du pilote et il est peu probable qu'il soit mis à jour à l'avenir.

Cependant, il contient toutes les fonctions permettant de l'activer et de charger son API. Nous espérons donc que tout lecteur motivé pourra, à l'aide des informations contenues dans cet article, l'étendre et poursuivre ses expériences avec ce logiciel malveillant.

Le référentiel du projet est disponible ici.

Logiciels malveillants et MITRE ATT&CK

Elastic utilise le cadre MITRE ATT& CK pour documenter les tactiques, techniques et procédures communes que les menaces utilisent contre les réseaux d'entreprise.

Tactiques

Techniques

Les techniques représentent la manière dont un adversaire atteint un objectif tactique en effectuant une action.

Atténuations

YARA

Elastic Security a créé les règles YARA suivantes en rapport avec cet article :

Observations

Les observables suivants ont été examinés dans le cadre de cette recherche :

ObservableTypeRéférenceDate
6a2a0f9c56ee9bf7b62e1d4e1929d13046cd78a93d8c607fe4728cc5b1e8d050SHA256ABYSSWORKER échantillon de référenceVT vue pour la première fois : 2025-01-22
b7703a59c39a0d2f7ef6422945aaeaaf061431af0533557246397551b8eed505SHA256ABYSSWORKER échantillonVT vue pour la première fois : 2025-01-27

Références

Partager cet article