Aikido

Une backdoor npm permet aux hackers de manipuler les résultats de jeux d'argent

Écrit par
Ilyas Makari

Nos pipelines de détection de malwares se sont récemment activés sur un petit groupe de packages sur npm qui semblaient... familiers.

Des packages comme json-bigint-extend, jsonfx, et jsonfb imitaient la populaire json-bigint bibliothèque : mêmes fonctionnalités, un fichier README identique, et même un nom d'auteur étrangement proche de celui du mainteneur original.

La plupart du temps, ce schéma indique des attaques courantes de la chaîne d'approvisionnement, telles que le typosquatting et la confusion de dépendances, conçues pour compromettre les systèmes et exfiltrer des secrets. Mais celle-ci a semblé différente presque immédiatement.

Elle n'essayait pas de cibler tout le monde. Elle essayait de cibler quelque chose.

Le détournement

À première vue, json-bigint-extend se comporte exactement comme la légitime json-bigint bibliothèque : elle exporte les fonctions parse/stringify familières utilisées pour prendre en charge les grands entiers en JSON. En fait, la plupart des développeurs et des organisations ne remarqueraient rien d'inhabituel. Cette charge utile est spécifiquement conçue pour rester discrète et ne se déclencher que lorsqu'elle détecte qu'elle s'exécute dans un environnement cible spécifique, en vérifiant la valeur d'une variable d'environnement spécifique nommée SERVICE_NAME.

Une fois qu'il détecte qu'il se trouve dans le bon environnement, il installe deux backdoors :

Premièrement, il installe un middleware Express ciblé, spécifiquement câblé sur une route de paiement (/v1/pay/purchase-goods). Ce middleware est conçu pour exécuter dynamiquement du code additionnel récupéré depuis un endpoint. Après une inspection plus approfondie du code récupéré, il semble s'agir d'un système complexe de réécriture de flux de trésorerie utilisé pour manipuler un jeu de hasard.

const routeInjectionRules = {
    '/v1/pay/purchase-goods': {
      identify: function (handlers, fn, index) {
        ...
      },
      position: 'after',
      extraMiddlewares: [function (req, res, next) {
       // Translation: [Plugin] Mount risk middleware as post-payment success logic.
log('[插件] 支付成功后的后置逻辑挂载risk'); 
       riskCode(req, res, next); // Executes dynamically fetched code
      }]
    }
  };

Deuxièmement, un middleware de niveau prototype qui modifie discrètement (monkey-patches) Express.js, ajoutant un middleware global à chaque route POST. Ce middleware écoute un en-tête secret x-operation et débloque quatre types de commandes pour l'opérateur :

  1. RunSQL : exécute du SQL arbitraire sur la base de données de production.
  2. RunFileList : liste les fichiers et répertoires côté serveur.
  3. RunFileContent : télécharger le contenu d'un fichier choisi.
  4. CompressDownload : télécharger un répertoire sous forme de fichier zip.

Le tableau de bord opérateur

Dans le package, il y a également une page HTML intégrée pour un « service de téléchargement de compression de répertoire » (titre chinois : 目录压缩下载服务).

Tableau de bord opérateur

Bien que cette page n'ait jamais été connectée nulle part dans le code de la backdoor que nous avons observé, elle semble être une interface utilisateur destinée à l'opérateur pour naviguer et exfiltrer des répertoires sous forme de fichiers zip.

Manipulation des résultats de jeu

Le plus inquiétant : cette riskCode(...) fonction appelée dans le middleware est contrôlée à distance et mise à jour toutes les 30 secondes.

Bien que la charge utile ne soit pas encore activement invoquée, nous avons observé une logique capable d'ajuster rétroactivement l'historique de jeu récent d'un utilisateur. Le composant le plus sophistiqué de cette porte dérobée est la fixFlow fonction, un moteur de manipulation de solde qui réécrit rétroactivement l'historique de jeu d'un utilisateur pour obtenir un changement de solde souhaité tout en conservant l'apparence d'un jeu légitime.

L'orchestration principale se déroule dans cette fixFlow, qui exécute un pipeline en quatre phases :

// Takes a desired amount as argument
async fixFlow(backupAmount) {


  // Phase 1: Load recent cashflow records
  const original = await this.getCashFlow();
  
  // Phase 2: Compute required adjustments to betting history
  const adjuster = new GameResultAdjuster({ debug: false });
  const adjustResult = adjuster.adjustDBData(original, backupAmount);
  const { backupRecords, adjustedResult } = adjustResult;
  const rewritten = this.writeBackFlowData(backupRecords);
  
  // Phase 3: Validate consistency
  const validation = this.validateCashFlowChain(...);
  if (!validation.isValid) {
    ...
  }
  
  // Phase 4: Persist to database
  await this.updateUserCashFlow(rewritten);
  await sendToUser(userId, { pop: false });
  await this.updateUserGameRoundFlow(gameTasks);
  
}

La fonction charge les enregistrements de flux de trésorerie récents et les convertit en journaux de jeu structurés. Ensuite, la adjustDBData méthode génère des résultats de paris de remplacement conçus pour produire un changement de solde falsifié. Ce qui est intéressant, c'est qu'au lieu de s'appuyer sur un seul algorithme, elle exécute deux stratégies concurrentes (greedy vs backtracking), et sélectionne l'approche avec le score de "réalisme" le plus élevé. Après avoir vérifié la cohérence de la chaîne de solde réécrite, updateUserCashFlow et updateUserGameRoundFlow écrit les enregistrements modifiés via Prisma dans la base de données de production en direct, tandis que sendToUser pousse l'événement de solde mis à jour vers l'appareil de l'utilisateur.

Stratégie de fraude gloutonne

L'approche gloutonne répartit le montant cible souhaité de manière égale sur les tours disponibles. Elle est rapide mais génère des schémas suspects. Imaginez que vous deviez distribuer 300 pièces fictives sur 3 tours de jeu. L'approche gloutonne divise simplement de manière égale : 100 par tour. Pour chaque tour, elle fixe le gain pour atteindre le résultat souhaité pour ce tour. Cependant, cela a tendance à paraître artificiel. Les jeux réels n'obtiennent pas de résultats constants à chaque tour.

Recherche par retour sur trace

L'approche par retour sur trace explore l'espace complet des solutions, en essayant différentes combinaisons de mises/gains jusqu'à en trouver une qui atteint la cible souhaitée avec une marge d'erreur de 0,01 %. Au lieu de prendre des décisions immédiates, elle explore l'arbre complet des possibilités. Essayez un montant de mise, voyez où cela mène, et si cela ne fonctionne pas, annulez et essayez une mise différente. C'est comme résoudre un labyrinthe en essayant chaque chemin jusqu'à trouver la sortie. Cette approche trouve une chaîne de résultats réaliste qui tient compte des contraintes, telles que l'argent disponible de l'utilisateur au moment de la mise.

Cela fonctionne à peu près comme ceci :

  1. Générer une liste de montants de mise possibles
  2. Filtrer les mises que l'utilisateur ne peut pas se permettre
  3. Évaluer chaque option selon son « atteignabilité » par rapport à la cible souhaitée
  4. Essayer d'abord l'option la mieux notée
  5. Si cela mène au succès, nous avons terminé
  6. Si cela mène à l'échec, annuler et essayer la mise suivante

L'algorithme utilise plusieurs optimisations, telles que la mémoïsation pour éviter de réexplorer les tentatives échouées, et l'élagage par atteignabilité pour ignorer les branches qui ne peuvent pas atteindre mathématiquement la cible souhaitée.

Évaluation de la qualité : Rendre la fraude naturelle

Après l'exécution des deux stratégies, le système applique un mécanisme sophistiqué d'évaluation de la qualité qui évalue à quel point un historique de jeu falsifié semble « réaliste ». Ce score détermine quelle sortie de stratégie de fraude est utilisée et sert de métrique pour le succès de l'attaque.

// Générer un score de qualité
const overallQuality = this.evaluateLogsQuality(adjustedGameLogs, completeness.actualNetGain);

Le evaluateLogsQuality La fonction commence à 100 points et déduit des pénalités pour les schémas suspects. Certaines de ces pénalités incluent :

  • Mises impossibles (Pénalité : -100) : Une mise qui dépasse le solde disponible est impossible dans un jeu réel. Elle serait rejetée par le serveur de jeu.
  • Gains orphelins (Pénalité : -2) : Un gain sans mise correspondante dans le même tour est structurellement invalide. Dans un jeu légitime, chaque gain est le résultat d'une mise. Un gain apparaissant de nulle part suggère une falsification des enregistrements.
  • Tours entrelacés (Pénalité : -1 à -40 selon la gravité) : Les joueurs réels terminent généralement un tour avant d'en commencer un autre. Un entrelacement excessif, c'est-à-dire le démarrage de plusieurs tours simultanément, ressemble à un comportement de bot ou à une manipulation.
  • Multiplicateurs irréalistes (Pénalité : -15) : Gagner à 100x ou plus est rare. Si plus de 10 % des tours affichent des multiplicateurs extrêmes, le schéma semble fabriqué et une pénalité est appliquée. Le simple algorithme glouton a tendance à produire ce schéma.
  • Jeu monotone (Pénalité : -10) : Un joueur qui perd chaque tour sur de nombreuses parties est suspect, même les joueurs malchanceux gagnent occasionnellement.

En conclusion, l'objectif n'est pas seulement la fraude. Il s'agit d'une fraude qui survit aux contrôles de cohérence internes, fabriquant des gains et des pertes tout en maintenant une comptabilité cohérente grâce à un mécanisme astucieux d'anti-détection.

Bappa Rummy

Nous ne pouvons pas dire avec certitude quel jeu est ciblé, mais les références environnantes suggèrent qu'il pourrait s'agir d'une application de jeu d'argent appelée Bappa Rummy. L'un des endpoints référencés dans le fichier est gameland.myapptest.top/v1, et une recherche rapide de transparence des certificats SSL pour ce domaine révèle des hôtes associés tels que gali.web.test.myapptest.top. Son inspection révèle ce qui semble être une page de destination cassée liée à Bappa Rummy, ce qui en fait une cible plausible de la porte dérobée. L'application semble être largement promue en ligne via des programmes de parrainage et des magasins d'applications alternatifs, mais n'est plus répertoriée sur le Google Play Store officiel.

Détection et prévention

Bien que nous ne sachions pas qui est derrière cette backdoor, le plus inquiétant est ce qu'elle fait une fois qu'elle atterrit dans le bon environnement. Il ne s'agit pas « seulement » d'un implant de dépendance typique qui exfiltre le code source, les secrets ou les données client.

Au lieu de cela, elle s'accroche directement à la logique métier, exécute du code contrôlé à distance sur le trafic réel et peut réécrire l'historique financier stocké dans la base de données. Si votre surveillance suppose que les journaux de base de données sont fiables, ce type de manipulation peut rester invisible pendant longtemps.

Si vous utilisez déjà Aikido, ce package serait signalé dans votre flux comme une découverte critique de 100/100.

Pas encore sur Aikido ? Créez un compte gratuit et liez vos dépôts. Le plan gratuit inclut notre couverture de détection de malwares (aucune carte de crédit requise).

Enfin, disposer d'un outil capable d'arrêter les malwares en temps réel dès leur apparition peut prévenir une infection grave. C'est l'idée derrière Aikido Safe Chain, un outil gratuit et open source qui s'intègre avec npm, npx, yarn, pnpm et pnpx et utilise à la fois l'IA et des chercheurs en malwares humains pour détecter et bloquer les derniers risques de la chaîne d'approvisionnement avant qu'ils n'entrent dans votre environnement.

Indicateurs de compromission

Packages et auteurs :

  • jsonfb (par sidoraress)
  • jsonfx (par sidoraress)
  • json-bigint-extend (par sidoraress et infinitynodestudio)

La backdoor communique avec un hôte distant pour les mises à jour de payload et le logging.

Endpoints observés :

  • https://payment[.]y1pay[.]vip/v1/risk/get-risk-code
  • https://payment[.]y1pay[.]vip/v1/risk/log
  • https://payment[.]snip-site[.]cc
  • https://gameland[.]21game[.]live
  • https://gameland[.]myapptest[.]top/v1
  • https://gameland[.]nbzysp1[.]com/v1
  • https://gameland[.]21game[.]live/v1

Autres IOCs et comportements traçables :

  • Requêtes contenant en-tête x-operation avec l'un des quatre jetons d'opération :
    • RunSQL (token : cfh2DNITa84qpYQ0tdCz)
    • RunFileList (token : m3QiEkg8Y1r9LFTI5e4f)
    • RunFileContent (token : Y3SrZjVqWOvKsBdpTCh7)
    • CompressDownload (token : SJQf31UJkZ1f88q9m361)
  • Modifications runtime apportées à express.Route.prototype.post

Partager :

https://www.aikido.dev/blog/npm-backdoor-lets-hackers-hijack-gambling-outcomes

Abonnez-vous pour les actualités sur les menaces.

4,7/5
Fatigué des faux positifs ?
Essayez Aikido, comme 100 000 autres.
Commencez maintenant
Obtenez une démonstration personnalisée

Approuvé par plus de 100 000 équipes

Réserver maintenant
Analysez votre application à la recherche d'IDORs et de chemins d'attaque réels

Approuvé par plus de 100 000 équipes

Démarrer l'analyse
Découvrez comment le pentest IA teste votre application

Approuvé par plus de 100 000 équipes

Démarrer les tests

Sécurisez votre environnement dès maintenant.

Sécurisez votre code, votre cloud et votre environnement d’exécution dans un système centralisé unique.
Détectez et corrigez les vulnérabilités rapidement et automatiquement.

Aucune carte de crédit requise | Résultats en 32 secondes.