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 :
fingerprint | Nom |
---|---|
51 68 1b 3c 9e 66 5d d0 b2 9e 25 71 46 d5 39 dc | Foshan Gaoming Kedeyu Insulation Materials Co. Ltd |
7f 67 15 0f bb 0d 25 4e 47 42 84 c7 f7 81 9c 4f | FEI XIAO |
72 88 1f 10 cd 24 8a 33 e6 12 43 a9 e1 50 ec 1d | Fuzhou Dingxin Trade Co, Ltd. |
75 e8 e7 b9 04 3b 13 df 60 e7 64 99 66 30 21 c1 | Changsha Hengxiang Information Technology Co. Ltd |
03 93 47 e6 1d ec 6f 63 98 d4 d4 6b f7 32 65 6c | Xinjiang Yishilian Network Technology Co, Ltd |
4e fa 7e 7b ba 65 ec 1a b7 74 f2 b3 13 57 d5 99 | Shenzhen 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 :
Nom | Code |
---|---|
Activer les logiciels malveillants | 0x222080 |
Copier le fichier | 0x222184 |
Supprimez les callbacks et les dispositifs par nom de module | 0x222400 |
Remplacer les fonctions principales du pilote par le nom du module | 0x222404 |
Tuer les threads du système par nom de module | 0x222408 |
Détachez les mini-filtres | 0x222440 |
Supprimer le fichier | 0x222180 |
Désactiver les logiciels malveillants | 0x222084 |
Charger l'api | 0x2220c0 |
Diminuer le compteur de référence de tous les conducteurs | 0x222100 |
Diminuer le compteur de référence de tous les appareils | 0x222104 |
Fin du processus | 0x222144 |
Terminer le fil | 0x222140 |
Suppression des crochets des principales fonctions des pilotes Ntfs et Pnp | 0x222444 |
Reboot | 0x222664 |
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.
- Modifications des autorisations au niveau du fichier et du répertoire
- Désactiver ou modifier des outils
- Signature du code
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 :
Observable | Type | Référence | Date |
---|---|---|---|
6a2a0f9c56ee9bf7b62e1d4e1929d13046cd78a93d8c607fe4728cc5b1e8d050 | SHA256 | ABYSSWORKER échantillon de référence | VT vue pour la première fois : 2025-01-22 |
b7703a59c39a0d2f7ef6422945aaeaaf061431af0533557246397551b8eed505 | SHA256 | ABYSSWORKER échantillon | VT vue pour la première fois : 2025-01-27 |
Références
- Google Cloud Mandiant, Mandiant Intelligence. Je jure solennellement que mon chauffeur ne fait rien de bon : à la recherche de logiciels malveillants portant une signature d'attestation. https://cloud.google.com/blog/topics/threat-intelligence/hunting-attestation-signed-malware/
- Unit42, Jerome Tujague, Daniel Bunce. Crypted Hearts : Exposing the HeartCrypt Packer-as-a-Service Operation, décembre 13, 2024. https://unit42.paloaltonetworks.com/packer-as-a-service-heartcrypt-malware/
- ConnectWise, Blake Eakin. "Attackers Leveraging Microsoft Teams Defaults and Quick Assist for Social Engineering Attacks", January 31 2025. https://www.linkedin.com/pulse/attackers-leveraging-microsoft-teams-defaults-quick-assist-p1u5c/
- wavestone-cdt, Aug 30, 2024. https://github.com/wavestone-cdt/EDRSandblast/tree/master
- myzxcg, mai 24, 2024. https://github.com/myzxcg/RealBlindingEDR