Mastodon

Mélofée revient, et il a évolué ! Trois ans après sa découverte, de nouveaux échantillons de cet implant Linux se révèlent nettement plus sophistiqués : décomposition en modules, chargeables et déchargeables à chaud, des fonctionnalités de shell, de fichiers et d'exécution de commandes, reconfiguration à distance de l'infrastructure C2 et toujours une grande furtivité grâce à un rootkit noyau. Stormshield documente ici la rétro-ingénierie complète de ces variants, lève le voile sur deux clusters d'infrastructure armés de certificats factices, et établit avec une confiance élevée un lien de filiation avec d'autres familles d'implants ciblant Windows.

Contexte

Mélofée est une famille de malware ciblant les systèmes Linux, documentée pour la première fois en 2023 [1] et attribuée à des groupes d'attaquants avancés probablement liés à des intérêts étatiques chinois.

Lors d'une investigation, nous avons identifié de nouveaux échantillons liés à cette famille.

Nous présenterons dans cet article les nouvelles fonctionnalités des implants montrant une modularisation accrue du code, ainsi qu'une évolution des protocoles de communication supportés. Au travers de cet article, nous montrerons que ces nouveaux échantillons partagent des similitudes importantes avec des codes existants tels que CrowDoor, Hemigate, ou RatelS. Ces similitudes nous permettent d'évaluer que ces différents codes sont très probablement partagés entre plusieurs groupes d'attaquants liés à des intérêts étatiques chinois.

Mélofée depuis 2023 : contexte et état de l'art

Les chercheurs décrivaient alors une famille de malware ciblant les serveurs Linux, attribuée avec un niveau de confiance élevé à des groupes d'attaquants liés à la Chine, notamment des acteurs proches de APT41 [1, 15].

L'analyse de trois échantillons découverts en 2022 a mis en évidence l'évolution continue de l'implant, caractérisée par l'intégration progressive d'un rootkit noyau basé sur le projet open source Reptile, lequel a ensuite été examiné par AhnLab en juillet 2023 [3].

Dès cette première analyse, Mélofée présentait plusieurs fonctionnalités caractéristiques des implants Linux modernes :

  • Exécution de commandes à distance ;
  • Gestion de fichiers ;
  • Ouverture d'un shell interactif ;
  • Collecte d'informations système ;
  • Communication avec des serveurs de commande et contrôle (C2) ;
  • Mécanismes de persistance.

L'analyse de l'infrastructure liée à Mélofée a également permis d'identifier des liens avec plusieurs outils déjà observés dans des campagnes associées à des groupes APT chinois, notamment ShadowPad, HelloBot, PlugX, Spark, Cobalt Strike et StowAway [1, 11].

En novembre 2024, XLab (Qianxin) a publié une analyse [4] d'un nouveau variant ciblant les systèmes RHEL 7.9. Cette publication confirme la poursuite du développement de l'implant depuis sa découverte, avec plusieurs évolutions techniques détaillées dans la suite de ce document. Les chercheurs notent également le nombre limité d'échantillons disponibles, ce qui suggère un usage restreint à des cibles spécifiques.

Évolutions entre les versions 2023 et 2024

Le fonctionnement principal de l'implant reste similaire entre les différentes versions. Les mêmes commandes de base sont présentes, avec uniquement une renumérotation des identifiants de commande entre les versions.

Plusieurs évolutions apparaissent toutefois entre les échantillons de 2023 et le variant observé en 2024 :

  • Gestion des processus : la version 2024 ajoute des commandes permettant de lister et de terminer des processus ;
  • Persistance : le mécanisme évolue avec l'utilisation de tâches crontab, l'utilisation d'un fichier de service systemd se faisant passer pour un service légitime vmware ;
  • Chargement du driver noyau : le pilote est désormais chiffré en RC4 et directement intégré au binaire principal sous forme d'overlay, contrairement aux versions précédentes où il était fourni séparément par l'installateur ;
  • Modification du code interne : plusieurs éléments du code ont été renommés, ce qui suggère une réorganisation ou une reconstruction partielle du code source.

Nouveaux variants en 2026

Description

L'analyse de trois échantillons montre que Mélofée a évolué depuis les versions précédemment observées. Si les mécanismes de communication réseau et le chiffrement RC4 restent similaires, plusieurs changements ont été identifiés, notamment l’ajout de nouvelles capacités, la modularisation de certaines fonctionnalités sous forme de librairie dynamique et des modifications internes de l’implant. Ces évolutions sont détaillées dans les sections suivantes.

L'implant contient un module principal, capable d'exécuter quelques commandes de base. Il est également capable de supporter des fonctionnalités aditionnelles via le téléchargement de modules complémentaires depuis le C2.

Le tableau ci‑dessous récapitule l’ensemble des commandes supportées :

ID
Description
Remarques
Commandes de base de l'implant (Module embarqué 0x1000)
0x1004
Récupération de la configuration C2 active.

0x1005
Chargement d'une nouvelle configuration C2, mais ne remplace pas la configuration embarquée.
Cette commande prend en paramètre une structure de données contenant jusqu'à trois configurations C2.
0x1006
Désinstaller l’implant et supprimer tous les fichiers qu’il a créés.

0x1007
Proxy TCP.

0x100A
Inventaire des modules chargés.

0x100B
Commande déclarée mais non implémentée.

0x100C
Déchargement d'un module.
Cette commande prend en paramètre l'identifiant d'un module (par exemple 0x3000).
0x100D
Forward server.
Fonctionalité déjà existante
0x100E
Interrompt toutes les connexions et met l’implant en pause pour une durée définie.
Cette commande accepte un paramètre numérique représentant la durée en minutes de mise en veille de l'implant.
Identifiants utilisée en réponse (traités par le C2)
0x1001
Cette identifiant est utilisé dans un paquet contenant les informations relatives à la victime, puis attend en retour la valeur magic number transmise par le serveur C2 afin de poursuivre la communication.
Cet identifiant est généré de manière transparente par l'implant à chaque connexion au C2 pour amorcer le dialogue. L'attaquant n'a jamais à solliciter cette commande.
0x1002
Identifiant utilisé par le module pour demander un téléchargement de module complémentaire au C2
Cet identifiant est renvoyé après une demande de l'opérateur
0x1003
Signalement de présence (heartbeat)
L'implant envoie également l'identifiant unique basé sur le condensat JSHash des informations système.
Commande du module Shell (0x2000)
0x2001
Interactive shell interface.

Commandes du module pour la gestion des fichiers et répertoires (0x3000)
0x3002
Lister le contenu d'un dossier.

0x3003
Supprimer un fichier ou un dossier.

0x3004
Créer un nouveau dossier.

0x3005
Modifier les dates de création, d'accès et de modification des fichiers où des répertoires.

0x3006
Lancer le téléchargement d'un fichier depuis le serveur de l'attaquant vers la machine de la victime.

0x3007
Lancer l'exfiltration d'un fichier depuis la machine de la victime vers le serveur de l'attaquant.

Commande du module pour l'exécution de commande à distance (0x4000)
0x4001
Exécution de commande à distance.

Fonctionnement global

Phase d'initialisation de l'implant

L'implant prend plusieurs arguments en ligne de commande :

  • /t: assure une persistance via la crontab ;
  • /s: assure une persistance du malware via un service systemd ;
  • /r: lance le binaire en mode démon via un appel à daemon ;
  • /u: désinstalle l'implant et les fichiers de persistance associés ;
  • hide: active la fonction de camouflage du rootkit ;
  • show: désactive la fonction de camouflage du rootkit.

Lors de son exécution, l'implant effectue des actions préparatoires afin d'établir son environnement d'exécution afin de :

  • Auto-référencement : l'implant lit le fichier /proc/self/exe afin de récupérer son propre chemin d'exécution sur le système ;
  • Verrou d'instance unique : l'implant met en place un mutex non bloquant via l'appel flock() sur un fichier dédié (/tmp/lock_ax dans les échantillons analysés). Ce mécanisme empêche plusieurs instances de fonctionner simultanément sur une même machine ;
  • Vérification de compatibilité du noyau : avant de charger le rootkit, l'implant vérifie l'environnement d'exécution de la machine compromise. Il récupère la version du noyau Linux via l'appel uname(), puis s'assure qu'il est compatible avec le rootkit. Si ce n'est pas le cas, l'implant abandonne le chargement du rootkit et poursuit son exécution dans le cas contraire. Le rootkit fournit les capacités de dissimulation utilisées par l'implant. La communication entre le processus utilisateur et le module noyau repose sur une interface ioctl() utilisant le code de requête 0xE0E0E0E.

Le fonctionnement de ce rootkit ne sera pas détaillé dans cette analyse, celui-ci ayant déjà été documenté par XLab Qianxin [4].

Phase de collecte d'informations système

Avant tout contact avec le serveur C2, l'implant collecte plusieurs informations sur la machine compromise afin de constituer une fiche d'identification de l'hôte. Cette collecte permet à l'opérateur de disposer d'un contexte sur la machine infectée avant l'exécution des fonctionnalités opérationnelles de l'implant.

L'implant génère tout d'abord un identifiant unique de machine via l'algorithme JSHash (Justin Sobel [13]).

uint Calculate_JSHash(char *param_1, uint param_2)

{

uint local_c = 0x4e67c6a7;

for (uint i = 0; i < param_2; i++) {

local_c = local_c ^ (local_c >> 2) + local_c * 0x20 + (int)param_1[i];

}

return local_c;

}

Cette implémentation utilise la constante d'initialisation 0x4E67C6A7. La valeur hachée est calculée à partir de plusieurs attributs système récupérés par l'implant :

  • la plateforme cible, dans notre cas "Linux" ;
  • la distribution Linux ;
  • l'utilisateur courant ;
  • le nom d'hôte de la machine.

En complément de cet identifiant, l'implant collecte les informations suivantes :

  • Nom d'hôte : récupération du nom de la machine ;
  • Utilisateur courant : récupération du compte associé à la session active ;
  • Distribution Linux : identification du système d'exploitation (via la lecture du fichier /etc/issue ou /etc/redhat-release) ;
  • Adresses IPv4 locales : interrogation des interfaces réseau (via un appel à la fonction getifaddrs) ;
  • Plateforme cible : Valeur fixe indiquant l'environnement (linux) ;
  • Horodatage : récupération de la date et de l'heure système via un appel à localtime (et un formatage au format %m-%d-%Y %H:%M:%S).

L'ensemble des informations collectées est ensuite concaténé dans une chaîne texte séparée par des tabulations :

<Machine_ID_JSHASH>\t<Hostname>\t<Username>\t<Distribution>\t<Local_IPs>\t<Platform>\t<Timestamp>

Cette fiche d’identification est envoyée au serveur C2 à chaque connexion. L’implant ne se contente pas de l’envoyer au premier contact. Il refait automatiquement cette collecte et renvoie la fiche après :

  • Lors d'une reconnexion après coupure ;
  • En sortie de période de veille ;
  • Suite à un redémarrage de l'implant.

Protocoles de communication

Afin d'assurer sa liaison, l'implant intègre une configuration de secours comprenant jusqu'à trois serveurs C2 (définis par un hôte, un port et un type de protocole). Il tente de se connecter à chacun d'eux successivement jusqu'à ce qu'une connexion aboutisse, ce qui fige le canal actif et stoppe les autres tentatives. L'analyse du code a mis en évidence plusieurs mécanismes de communication distincts pour ces entrées.

Le tableau suivant présente les caractéristiques de chaque type identifié :

Type
Nom
Description
1 / 5
TcpConn
Mode TCP classique. L'implant résout l'adresse du serveur via getaddrinfo(), crée un socket IPv4 TCP puis établit la connexion avec connect(). Aucun échange préalable supplémentaire n'est réalisé.
2
HttpConn
Mode TCP avec une poignée de main applicative au format HTTP minimal. Après l'établissement de la connexion TCP, l'implant envoie POST / HTTP/1.1rn et attend la réponse HTTP/1.1 200 OKrn avant d'initialiser la session.
3
HttpsConn
Mode reposant sur une connexion TLS suivie d'une Handshake HTTP minimale similaire à celle du type 2, utilisée pour valider le canal de communication avant l'initialisation de la session C2.
4
TlsConn
Mode établissant une connexion TCP encapsulée dans TLS. Les captures réseau réalisées en environnement contrôlé confirment l'utilisation de TLS 1.2.
Format du protocole applicatif

Chaque message applicatif suit une structure à deux parties :

┌───────────────────────────┬─────────────────────────┐
│ HEADER (16 octets, clair) │ PAYLOAD (N octets, RC4) |
└───────────────────────────┴─────────────────────────┘

L'en-tête, transmis en clair, possède la structure suivante :

struct PacketHeader {

uint32_t type; // Command Opcode

uint32_t length; // Payload Size

uint32_t param1; // Usage varying with command type

uint32_t param2; // Usage varying with command type

};

Les deux derniers champs servent d'opérandes pour certaines instructions. À titre d'exemple, lors d'une demande de téléchargement, le quatrième champ véhicule l'identifiant du module requis.

La charge utile (payload) optionnelle est, quand elle est présente, chiffrée en RC4 au moyen d'une clé statique embarquée dans les implants (Trbv5245GTF dans les échantillons analysés).

Exemple 1 : Demande de téléchargement de module (0x1002)

Le paquet réseau prend la forme suivante :

00 00 10 02  00 00 00 00  00 00 00 02  00 00 30 00
└───type───┘└───length─┘ └───param1─┘  └───param2─┘
   0x1002         0            2           0x3000

Lors de cette demande, les paramètres de la structure sont définis ainsi :

  • Type : 0x1002 (identifiant de réponse utilisé par l'implant) ;
  • Taille du payload : 0 (aucun contenu additionnel dans le payload) ;
  • Paramètre 1 : 2 (code fixe de sous-commande) ;
  • Paramètre 2 : Identifiant du module demandé (ici, 0x3000 pour la gestion de fichiers) ;
  • Payload : Vide
Exemple 2 : Suppression de fichier (0x3003)

Le paquet réseau prend la forme suivante :

00 00 30 03  00 00 00 0d  00 00 00 00  00 00 00 00
└──type──┘   └─length─┘   └─param1─┘   └─param2─┘
 0x3003      13 octets       0           0
(DelFile)
Payload (13 octets, RC4(`/tmp/test.txt`))
Hexdump:
00000000  00 00 30 03 00 00 00 0d  00 00 00 00 00 00 00 00  |..0.............|
00000010  2f 74 6d 70 2f 74 65 73  74 2e 74 78 74           |/tmp/test.txt   |

Une fois le module chargée en mémoire, l'appel de ses fonctionnalités internes s'effectue selon une structure de données spécifique. Dans ce cas précis, les champs paramètre 1 et paramètre 2 restent à zéro, car la donnée utile est entièrement déportée dans le payload. Ce dernier contient le chemin du fichier à cibler (par exemple /tmp/test.txt), chiffré en RC4.

Exemple 3 : Le paquet d'enregistrement

Le paquet réseau prend la forme suivante :

Header (16 bytes, clear):
00 00 10 01 00 00 00 8e 00 00 00 00 00 00 00 00
└──type──┘  └─length──┘ └─param1─┘  └─param2──┘
 0x1001      142(0x8e)       0           0
Payload (142 bytes, RC4 encryption), once decrypted :
1496026344	localhost.localdomain	root	Red Hat Enterprise Linux Server release 7.9 (Maipo)	127.0.0.1 192.168.100.128 	linux	08-02-2026 10:53:21
Hexdump:
00000000  00 00 10 01 00 00 00 8e  00 00 00 00 00 00 00 00  |................|
00000010  31 34 39 36 30 32 36 33  34 34 09 6c 6f 63 61 6c  |1496026344.local|
00000020  68 6f 73 74 2e 6c 6f 63  61 6c 64 6f 6d 61 69 6e  |host.localdomain|
00000030  09 72 6f 6f 74 09 52 65  64 20 48 61 74 20 45 6e  |.root.Red Hat En|
00000040  74 65 72 70 72 69 73 65  20 4c 69 6e 75 78 20 53  |terprise Linux S|
00000050  65 72 76 65 72 20 72 65  6c 65 61 73 65 20 37 2e  |erver release 7.|
00000060  39 20 28 4d 61 69 70 6f 29  09 31 32 37 2e 30 2e  |9 (Maipo).127.0.|
00000070  30 2e 31 20 31 39 32 2e 31  36 38 2e 31 30 30 2e  |0.1 192.168.100.|
00000080  31 32 38 20 09 6c 69 6e 75  78 09 30 38 2d 30 32  |128 .linux.08-02|
00000090  2d 32 30 32 36 20 31 30 3a  35 33 3a 32 31        |2026 10:53:21   |

Ce message permet à l'implant de remonter les caractéristiques de chaque hôte compromis ainsi que son identifiant unique.

Authentification des liaisons auprès du serveur C2

L'implant valide sa communication avec le serveur C2 en une ou deux étapes selon le mode de communication sélectionné. Ces contrôles sont strictement bloquants : si une validation échoue, l'implant reste inactif et n'entre jamais dans sa boucle d'exécution de commandes.

Phase de pré-connexion HTTP (Spécifique au modes HttpConn et HttpsConn)

Cet échange applicatif constitue le tout premier contact réseau sous le protocole HTTP. L'implant commence par envoyer une chaîne statique de 17 octets :

50 4f 53 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a
POST / HTTP/1.1\r\n   (17 bytes)
Hexdump:
00000000 50 4f 53 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d |POST / HTTP/1.1.|
00000010 0a |.|

Le serveur C2 doit obligatoirement répondre par exactement 17 octets contenant la chaîne :

48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d 0a
HTTP/1.1 200 OK\r\n   (exactly 17 bytes)
Hexdump:
00000000  48 54 54 50 2f 31 2e 31  20 32 30 30 20 4f 4b 0d  |HTTP/1.1 200 OK.|
00000010  0a                                                |.               |

La vérification dans le binaire est particulièrement stricte. L'analyse du code montre que l'implant contrôle d'abord la taille exacte reçue (0x11 en hexadécimal, soit 17 octets), puis effectue une comparaison octet par octet en mémoire :

if (bytes_received == 0x11) {

iVar1 = memcmp(&http_buffer, "HTTP/1.1 200 OK\r\n", 0x11);

if (iVar1 == 0) {

Si la taille reçue est différente, ou si le contenu ne correspond pas exactement, l'implant interrompt la connexion. Ce mécanisme basique lui permet de s'assurer qu'il dialogue bien avec son infrastructure.

Authentification de la liaison (commune à tous les modes)

Obligatoire pour l'ensemble des mécanismes de communication, cet échange intervient immédiatement après la transmission du paquet d'enregistrement initial (voir l'exemple de structure 0x1001 ci-dessous). Le serveur C2 doit alors répondre par exactement 4 octets bruts, sans aucun en-tête ni chiffrement :

11 22 33 44 (value 0x11223344 en network byte order)

Le code correspondant :

BaseConn::recvn(pBVar5, &local_ec, 4, 60000); // Reads exactly 4 bytes, timeout 60s

if ((iVar3 == 0) && (my_htonl(local_ec) == 0x11223344))

Si les 4 octets reçus ne correspondent pas strictement à cette constante, ou si le délai de 60 secondes est dépassé, la connexion est immédiatement rejetée par l'implant.

Récapitulatif du fonctionnement global

L'architecture globale demeure inchangée par rapport aux versions de 2023 et 2024, mais les implants se distinguent désormais par l'ajout de nouvelles fonctionnalités qui sont détaillées dans le prochain paragraphe.

Présentation des nouvelles fonctionnalités

Récupération de la configuration réseau active

Cette fonctionnalité, identifiée par l'ID 0x1004, permet de récupérer la configuration réseau active de l'implant et de la transmettre au serveur de commande et de contrôle (C2).

Chargement d'une nouvelle configuration réseau

Cette fonctionnalité, identifiée par l'ID 0x1005, permet de charger dynamiquement une nouvelle configuration C2 sans modifier la configuration réseau embarquée.

Le diagramme ci-dessous illustre la logique du chargement d'une nouvelle configuration réseau au sein de l'implant :

L'implant effectue une vérification stricte du fichier de configuration qui doit être :

  • Taille égale à 228 octets (0xe4) : l'implant valide le fichier externe et applique la nouvelle configuration.
  • Taille différente ou fichier absent : le fichier est jugé invalide. L'implant rejette la modification et utilise la configuration embarquée.

Gestion dynamique de modules

L'implant utilise un système de module lui permettant de charger dynamiquement des fonctionnalités complémentaires.

Cette modularisation est basée sur les éléments suivants:

  • Un identifiant de module calculé dynamiquement par un masque de bits : dwModuleID = dwCommandId & 0xF000 ;
  • Une liste chaînée contenant les modules déjà chargés (initialisée avec le module central embarqué 0x1000) ;
  • L'exposition par chaque module d'une interface commune (détaillée ci après).

L'implant supporte 11 modules (en complément du module central), identifiés de 0x2000 à 0xC000.

L'analyse statique des implants a également permis d'identifier plusieurs références à des chemins temporaires utilisés lors du mécanisme de chargement dynamique des modules.

En effet, lors du déploiement d'un module par l'attaquant, celui-ci est stocké dans un fichier nommé au format /tmp/PLGID.

Par ailleurs, deux commandes supplémentaires ont été ajoutées dans l'implant permettant de gérer ces plugins :

  • La commande 0x100A, permettant d'obtenir la liste des modules actifs au sein de l'implant ;
  • La commande 0x100C permettant le déchargement d'un module. Elle offre à l'opérateur la possibilité de retirer un module actif sans nécessiter de redémarrage.
Téléchargement et chargement de module

Le diagramme ci-dessous retrace les différentes étapes du chargement d'un module par l'implant :

Modules connus

Nos investigations ont permis d'identifier trois modules, associés aux identifiants 0x2000, 0x3000 et 0x4000. Les modules associés aux identifiants 0x5000 à 0xc000 n'ont pas pu être identifiés.

Identifiant
Nom de fichier
SHA256
Fonctionnalité
0x2000
/tmp/2000
cbce7dc8649b37c5db411dfee3a74e8cace2b48fdc8350bfa5b4846c5d9ed2ae
Shell interactif
0x3000
/tmp/3000
9d01724193d9570a6c75bbcbb171d29df290207007d004cf75d133dd9aa475f4
Gestion de fichiers
0x4000
/tmp/4000
129349a221e6efcc9a047efdfd81197f796fcf38877344f17f85651b21e33427
Exécution de commande à distance
Interface de plugin (commun à tous les modules)

Les modules analysés présentent une structure commune et exportent tous une fonction nommée xmain servant de point d'entrée.

Chaque module implémente une interface identique (GetPlgInterface), composée de trois fonctions :

  • GetPlugId : Cette fonction retourne l'identifiant du module.
  • WritePlugin : Cette fonction est appelée par l'orchestrateur en lui fournissant la commande demandée par l'attaquant ainsi que ses paramètres éventuels.
  • SetBackCall : Cette fonction permet à l'orchestrateur de fournir un certain nombre de fonctions utilitaires à chaque module (telle que l'exécution de shell interactif ou la gestion de tunnels réseau).

Par ailleurs, l'ensemble des modules analysés utilisait les mêmes classes pour gérer ses communications : BaseConn, elle même utilisant une classe utilitaire SocketApi exposant une surcouche des fonctions standard de la libc.

Il est également à noter que le module principal de l'implant expose une interface identique.

Module de shell interactif (CShell)

Contrairement aux versions de 2023 et 2024 où elle était directement intégrée, cette capacité d'exécution de commandes à distance est désormais externalisée sous forme de module.

Son fonctionnement repose sur la création d'un pseudo-terminal (PTY) pour offrir un shell classique. Le module y redirige les flux d'entrée, de sortie standard et d'erreur, puis tente de lancer l'interpréteur /bin/bash (avec un repli automatique vers /bin/sh en cas d'échec). Avant le lancement du shell, l'environnement d'exécution est configuré notamment via les variables TERM et HISTFILE afin de contrôler l'affichage et de désactiver l'enregistrement de l'historique des commandes.

Module de gestion des fichiers et répertoires (CFile)

Auparavant intégrées directement au cœur de l'implant Mélofée (versions 2023-2024), les capacités de manipulation de fichiers sont désormais externalisées.

Ce module permet à l'opérateur de charger dynamiquement les fonctionnalités suivantes :

  • Énumération des fichiers et répertoires ;
  • Suppression de fichiers ;
  • Création de répertoires ;
  • Modification des horodatages des fichiers ;
  • Envoi de fichiers vers la machine compromise ;
  • Téléchargement de fichiers depuis la machine compromise.
Module d'exécution de commandes à distance (CCmd)

Autrefois implémenté dans l'implant principal de Mélofée, cette capacité d'exécution a été externalisée dans un module. Le composant reçoit une commande, l'exécute localement sur l'hôte via un appel au binaire /bin/sh, puis récupère sa sortie standard ainsi que sa sortie d’erreur avant de transmettre le résultat au serveur C2.

Contrairement au module de session shell, cette fonctionnalité est strictement limitée à des lancements de commandes unitaires et n'offre aucun terminal interactif.

Infrastructure

Lors de nos investigations, nous avons pu identifier deux clusters d'infrastructure liés à ces implants, sans toutefois pouvoir identifier d'autres implants communiquant avec cette infrastructure.

L'ensemble des IOCs identifiés est disponible dans les annexes de cet article.

Cluster Symantec

L'ensemble des adresses IP et des domaines utilisés en C2 dans les samples les plus récents exposaient le même certificat autosigné possédant un champ Issuer usurpant l'identité de Symantec (C=US, ST=California, L=California, O=Symantec Corp).

En utilisant l'empreinte SHA1 de ce certificat (dbbcb279a1f1a258832174ce63c295c5080de249) comme point de pivot, il a été possible d'identifier plusieurs serveurs C2 très probablement liés à cette menace.

Il est à noter que les TTPs consistant à utiliser des certificats se faisant passer pour des produits liés à l'informatique, ainsi que des noms de domaines contenant update ont déjà été documentés pour des serveurs liés à l'outil ShadowPad [6].

Cluster Xdevops

Lors de l'analyse de l'échantillon 4400096e8b39cb2641abec17c6dddad845ed6415e79d60adc2af1dcbd2837c59, nous avons identifié que l'adresse IP résolue par son serveur de C2 exposait également un certificat autosigné (Issuer: /C=US/ST=MI/L=Southfield/O=xdevops/OU=xdevops/CN=update.miss-soft[.]com, SHA-1: b6df18f66cf4364be2946d6b981e69763aafde68).

En utilisant ce certificat comme point de pivot, nous avons identifié un deuxième cluster d'activité, enregistrant des domaines ressemblants à des domaines Amazon.

Similarités avec des familles de codes malveillants existantes

Les échantillons Mélofée étudiés lors de notre investigation présentent de nombreuses similarités avec plusieurs familles d'implants existants, bien que ceux-ci ciblent le système d'exploitation Windows.

Les familles qui ressortent de cette analyse sont les suivantes :

  • SparrowDoor / CrowDoor, une famille d'implants Windows découverts par ESET en 2025, et lié au groupe d'attaquants FamousSparrow [5] ;
  • TernDoor, une famille d'implants possiblement dérivés de CrowDoor, documenté par Cisco Talos [7] ;
  • Hemigate: une famille d'implants partagée par plusieurs groupes d'attaquants [8] ; [8] ;
  • LibreCoin/Ratels, une famille d'implants probablement liée à Hemigate [8, 11, 12].

En particulier, les similarités suivants ont été confirmées lors de notre analyse :

  • Utilisation de l'algorithme RC4 avec une clé fixe pour déchiffrer la configuration et chiffrer les communications [5, 7, 8, 9, 11, 12] ;
  • Mécanisme similaire de mise à jour de la configuration dans un fichier chiffré [5, 11] ;
  • Similarités fonctionnelles dans l'architecture globale des implants : utilisation de threads pour paralléliser les commandes et utilisation d'évènements pour la synchronisation [5, 8] ;
  • Similarités dans les protocoles de communication entre Mélofée, CrowDoor et RatelS [5, 8, 12] ;
  • Mécanisme de gestion des modules par application d'un masque de bits sur l'ID de commande et liste chaînée [5, 12] ;
  • Le nom d'export utilisé pour le chargement des modules est très similaire : xmain pour Mélofée, fmain pour RatelS et CrowDoor [5, 8, 11, 12] ;
  • Exposition d'une interface à chaque module (PluginInterface vs PlgInterface) [5].

Par ailleurs, comme le montre le tableau suivant, les noms de classes utilisés dans les modules de CrowDoor sont identiques :

Identifiant CrowDoor
Identifiant Mélofée
Nom de classe utilisée dans les modules CrowDoor
0x60000
0x2000
CShell
0x30000
0x3000
CFile
0x20000
0x4000
CCmd

Avec un niveau de confiance élevée, nous estimons que Mélofée est très probablement lié à ces différentes familles de codes malveillants, et est potentiellement développé par les mêmes équipes.

Par ailleurs, ces similarités montrent un probable partage d'outils entre différents groupes d'attaquants, ce qui est déjà évoqué par plusieurs publications étudiant ces implants [5, 7, 8, 9, 10, 11, 12].

Détection via les firewalls Stormshield

Au sein de nos firewalls Stormshield Network Security (SNS), plusieurs signatures dédiées à la détection de l'activité Mélofée sont déjà en place :

  • ssl:server:certificate.107 : signature permettant de détecter l'utilisation de certificats utilisés par Mélofée ;
  • tcp:client:port.68 : signature détectant l'envoi d'un paquet d'enregistrement par un implant Mélofée à un serveur C2.

Selon le niveau d'IDS/IPS configuré, les firewalls SNS lèveront des alarmes voire bloqueront les paquets que ces signatures permettront d'identifier.

Sur l'interface de votre SNS, vous pourrez retrouver les alarmes levées dans la partie Monitoring - Logs/Journaux d'audit - Alarmes [14].

Conclusion

L'analyse des nouveaux échantillons de Mélofée montre que cette famille d'implants est toujours active et en évolution continue, près de trois ans après sa découverte.

Les évolutions majeures identifiées, telle que la modularisation de fonctions historiquement intégrées à l'implant et l'ajout de commandes permettant de reconfigurer à distance l'infrastructure C2 montrent une maturation de l'outillage utilisé par les attaquants.

Par ailleurs, les similarités structurelles du code soutiennent l'hypothèse d'un partage de code entre plusieurs groupes d'attaquants probablement liés à des intérêts étatiques chinois , ce qui complexifie la tâche des défenseurs et brouille l'attribution basée uniquement sur les outils.

Références

Annexes

Mapping des techniques observées (MITRE ATT&CK)

IOCs

Type
Usage
Valeur
Description and remarque
Implants
HASH
Backdoor
8255d5a423ce79c69e42685cbf8e708b4239650c01b2898a7efe856582bb3aaf
Implant Linux 
HASH
Backdoor
dd081c130d7c3a3c18e06a26300dcfd65ee70e6b23b729edb54cb0f0a5829df9
Implant Linux
HASH
Backdoor
50daadd49b8df4f0190d0badfde89f8e6a6e61af664ef1444984028a005f40d8
Implant Linux
HASH
Backdoor
4400096e8b39cb2641abec17c6dddad845ed6415e79d60adc2af1dcbd2837c59
Implant Linux (ancienne version)
Rootkits
HASH
Rootkit
aa2fd252a6f5a09b8d8422fbd1037febefe2aa99317e3c3973889384a80e29b7
Rootkit embarqué au sein de l'implant "50daadd49b8df4f0190d0badfde89f8e6a6e61af664ef1444984028a005f40d8"
HASH
Rootkit
6678216ace0ff224d6a5ae6bf1ca29e66fba9fceaa715e27440e85d5bfb06069
Rootkit embarqué au sein de l'implant "dd081c130d7c3a3c18e06a26300dcfd65ee70e6b23b729edb54cb0f0a5829df9"
Modules
HASH
Module
129349a221e6efcc9a047efdfd81197f796fcf38877344f17f85651b21e33427
Module d’exécution de commandes à distance
HASH
Module
9d01724193d9570a6c75bbcbb171d29df290207007d004cf75d133dd9aa475f4
Module de gestion des fichiers et des répertoires
HASH
Module
cbce7dc8649b37c5db411dfee3a74e8cace2b48fdc8350bfa5b4846c5d9ed2ae
Module pour le Shell interactif

Infrastructure Cluster "Symantec"
Domaine
C2
windefender[.]net:443
Associé à l'implant "50daadd49b8df4f0190d0badfde89f8e6a6e61af664ef1444984028a005f40d8"
Domaine
C2
www[.]windefender[.]net:443
IP
C2
103[.]215[.]216[.]117:443
Associé à l'implant "8255d5a423ce79c69e42685cbf8e708b4239650c01b2898a7efe856582bb3aaf"
IP
C2
89[.]44[.]198[.]107:443
IP
C2
92.38.169[.]152
Résolution DNS du domaine windefender[.]net
IP
C2
38.54.71[.]107
Résolution DNS du domaine windefender[.]net (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
IP
C2
130.94.91[.]53
Résolution DNS du domaine windefender[.]net (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
IP
C2
38.54.71[.]56
Résolution DNS du domaine windefender[.]net (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
Domaine
C2
windowsupdates[.]us:80
Associé à l'implant "dd081c130d7c3a3c18e06a26300dcfd65ee70e6b23b729edb54cb0f0a5829df9"
IP
C2
45[.]80[.]208[.]232:80
IP
C2
185.163.2[.]100
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
Domaine
C2
microsoftupdates[.]top
Domaine résolvant vers 185.163.2[.]100
IP
C2
180.149.44[.]115
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
Domaine
C2
windowsupdates[.]us
Domaine résolvant vers 180.149.44[.]115
IP
C2
103.215.216[.]181
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
Domaine
C2
microupdate[.]top
Domaine résolvant vers 103.215.216[.]181
IP
C2
103.215.216[.]75
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
IP
C2
103.215.216[.]117
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
IP
C2
103.215.216[.]116
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
IP
C2
103.215.216[.]27
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
Domaine
C2
microupdate[.]me
Domaine résolvant vers 103.215.216[.]27
IP
C2
46.246.98[.]131
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
IP
C2
185.163.2[.]34
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
IP
C2
185.163.2[.]79
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
IP
C2
185.163.2[.]81
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
IP
C2
23.95.34[.]184
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
IP
C2
172.86.114[.]201
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
IP
C2
130.94.91[.]165
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
IP
C2
103.87.9[.]148
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
IP
C2
45.67.230[.]185
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
IP
C2
154.205.137[.]88
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
IP
C2
146.71.85[.]33
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
IP
C2
103.215.216[.]162
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
IP
C2
103.215.216[.]61
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)
IP
C2
180.149.44[.]43
Probable C2 (ayant exposé le certificat dbbcb279a1f1a258832174ce63c295c5080de249)

Infrastructure Cluster "xdevops"
Domaine
C2
awsclouds.awspull[.]com
Associé à l'implant "4400096e8b39cb2641abec17c6dddad845ed6415e79d60adc2af1dcbd2837c59"
IP
C2
192.3.55[.]220
Associé à l'implant "4400096e8b39cb2641abec17c6dddad845ed6415e79d60adc2af1dcbd2837c59", ayant résolu le domaine awsclouds.awspull[.]com et ayant exposé le certificat 0c1666b274ddb44dd5c7a11f03c8c041f6bccf0c7dae924626ef9d15b1cc41d5
IP
C2
38.54.110[.]44
Probable C2 (ayant exposé le certificat b6df18f66cf4364be2946d6b981e69763aafde68)
IP
C2
45.61.148[.]33
Probable C2 (ayant exposé le certificat b6df18f66cf4364be2946d6b981e69763aafde68)
IP
C2
2.56.166[.]139
Probable C2 (ayant exposé le certificat b6df18f66cf4364be2946d6b981e69763aafde68)
Domaine
C2
update.wwwubuntu[.]com
Probable C2 (ayant résolu vers plusieurs adresses IP résolu vers 38.54.110[.]44, 45.61.148[.]33, 2.56.166[.]139)
IP
C2
2.56.166[.]84
Probable C2 (ayant exposé le certificat b6df18f66cf4364be2946d6b981e69763aafde68)
Domaine
C2
amzaonaws[.]org
Probable C2 (ayant résolu vers 2.56.166[.]84)

Fichiers
Path
IOC
/tmp/lock_ax
Mutex utilisé par l'implant
Path
IOC
/tmp/[ID]
Fichier temporaire utilisé pour stocker les modules (ID étant compris entre 2000 et c000)
Path
IOC
/etc/systemd/system/vmware.service
Fichier utilisé pour la persistance
Path
IOC
/tmp/.mpwzlof
Fichier utilisé pour installer la persistance
Process Name
IOC
[md]
Nom de processus (hors kernel)

Signatures Yara, Sigma et SNS (Stormshield)

Signatures Yara

rule UNK_APT_MelofeeImplant_2026

{

meta:

description = "Detects Melofee Linux implant"

author = "romain.carette - (Stormshield CTI)"

family = "Melofee"

hashes = "50daadd49b8df4f0190d0badfde89f8e6a6e61af664ef1444984028a005f40d8,dd081c130d7c3a3c18e06a26300dcfd65ee70e6b23b729edb54cb0f0a5829df9,8255d5a423ce79c69e42685cbf8e708b4239650c01b2898a7efe856582bb3aaf"

created = "2026-08-15"

modified = "2026-09-02"

score = 100

tlp = "TLP-CLEAR"

strings:

$sym_xmain = "xmain" ascii

// This functionality allows incoming TCP traffic to be redirected to a local port and is used by Melofee to implement a network redirection mechanism.

$iptables_add = "iptables -t nat -A PREROUTING -p tcp -s %s --dport %d -j REDIRECT --to-port %d" ascii
$iptables_del = "iptables -t nat -D PREROUTING -p tcp -s %s --dport %d -j REDIRECT --to-port %d" ascii

$pevents_src = "pevents.cpp" ascii
$pevents_ns1 = "neosmart::CreateEventx" ascii
$pevents_ns2 = "neosmart::WaitForMultipleEvents" ascii

$code_initmod = {

b9 ?? ?? ?? ??
48 89 da
48 89 c6
bf af 00 00 00
b8 00 00 00 00
e8

}

$code_selfdelete = {

bf ?? ?? ?? ??
e8 ?? ?? ?? ??
89 45 ??
83 7d ?? 00
0f 8e ?? ?? ?? ??
8b 45 ??
48 98
c6 84 05 ?? ?? ?? ?? 00

}

condition:

uint32(0) == 0x464c457f and
3 of them

}

rule UNK_APT_MelofeeImplantLib
{

meta:

description = "Detects plugin libraries (.so) of the Melofee Linux implant family"
author = "romain.carette - Stormshield CTI"
date = "2026-08-24"
malware_family = "Melofee"
hashes = "9d01724193d9570a6c75bbcbb171d29df290207007d004cf75d133dd9aa475f4,cbce7dc8649b37c5db411dfee3a74e8cace2b48fdc8350bfa5b4846c5d9ed2ae,129349a221e6efcc9a047efdfd81197f796fcf38877344f17f85651b21e33427"
created = "2026-08-15"
modified = "2026-09-02"
score = 100
tlp = "TLP-CLEAR"

strings:

// SocketApi network layer
$sock1 = "_ZN9SocketApi7ConnectEiPK8sockaddri" ascii
$sock2 = "_ZN9SocketApi6SocketEiiiRi" ascii
$sock3 = "_ZN9SocketApi6ListenEii" ascii
$sock4 = "_ZN9SocketApi6AcceptEiP8sockaddrPiRi" ascii
$sock5 = "_ZN9SocketApi4SendEiPKviRi" ascii
$sock6 = "_ZN9SocketApi4RecvEiPviRi" ascii
$sock7 = "_ZN9SocketApi4PeekEiPviRi" ascii
$sock8 = "_ZN9SocketApi4BindEiPK8sockaddri" ascii
$sock9 = "_ZN9SocketApi13GetAddrInfo_xEPKcS1_PK8addrinfoPPS2_" ascii
$sock10 = "_ZN9SocketApi18FreeAddrInfoWrap_xEP8addrinfo" ascii
$sock11 = "_ZN9SocketApi11GetsocknameEiP8sockaddrPi" ascii
$sock12 = "_ZN9SocketApi11GetpeernameEiP8sockaddrPi" ascii
$sock13 = "_ZN9SocketApi5CloseEi" ascii

// BaseConn connection wrapper
$conn1 = "_ZN8BaseConn5sendnEPvi" ascii
$conn2 = "_ZN8BaseConn5recvnEPvim" ascii
$conn3 = "_ZN8BaseConn4peekEPviRim" ascii
$conn4 = "_ZTI8BaseConn" ascii
$conn5 = "_ZTV8BaseConn" ascii

// Packet handling + RC4
$pkt1 = "_Z11WritePacketSt10shared_ptrI8BaseConnEP9_PkHeaderSs" ascii
$pkt2 = "_Z10ReadPacketSt10shared_ptrI8BaseConnEP9_PkHeaderRSs" ascii
$rc4_1 = "_Z6re_RC4PhPc" ascii
$rc4_2 = "_Z7re_SboxPhS_" ascii
$rc4_3 = "_Z5RC4_2Pci" ascii
$rc4_4 = "_Z3RC4PciS_" ascii

// Network helpers
$hton1 = "_Z9my_htonllm" ascii
$hton2 = "_Z8my_htonst" ascii
$hton3 = "_Z8my_htonlj" ascii

// Shared internal string utilities
$util1 = "_Z5splitRKSsS0_RSt6vectorISsSaISsEE" ascii
$util2 = "_Z4joinRKSt6vectorISsSaISsEERKSs" ascii

// Plugin entry point symbol
$entry = "xmain" ascii fullword

condition:

uint32(0) == 0x464c457f and // ELF magic
uint16(16) == 3 and // e_type == ET_DYN (shared object)
(

6 of ($sock*)
or 2 of ($conn*)
or 10 of them
or ($entry and 2 of ($sock*, $conn*))

)

}

Signature Sigma

title: Detection of temporary files used by Mélofée for module storage

id: 8e6f2a3c-4b7d-4e1a-9c2f-5d8a1b3e7f90

status: experimental

description: Detects the creation, in /tmp/, of temporary files whose names correspond to a hexadecimal identifier. These files are used to store modules/plugins that will subsequently be loaded via dlopen. The filename corresponds to a hexadecimal identifier ranging from 0x2000 to 0xC000.

references:

- Stormshield CTI Internal Analysis – Mélofée Linux Implant

author: romain.carette - (Stormshield CTI)

date: 2026-08-26

tags:

logsource:

product: linux
category: file_event

detection:

selection:

TargetFilename|re: '^/tmp/[2-9a-cA-C]000$'

condition: selection

falsepositives:

- Legitimate temporary files with randomly generated hexadecimal names (rare but possible)

level: medium

Partager sur

[juiz_sps buttons="facebook, twitter, linkedin, mail"]