SlowMist : JS malveillant d’Udesk vole des données et modifie les adresses de transfert

iconMetaEra
Partager
AI summary iconRésumé
SlowMist a découvert un script JS malveillant dans le système Udesk, lié à des nouvelles sur chaîne concernant des plateformes compromise telles que mgbx.com et bitxex.io. Le script, chargé via udeskApi.2026-10-16.cached.js et deeps.js, vole les données de session et modifie les adresses de rechargement. Les attaquants utilisent un chargeur distant pour injecter du code, exposant les fonds au risque de vol. Les données d'inflation et la volatilité du marché pourraient amplifier les pertes pour les utilisateurs concernés. Cette violation met en lumière des préoccupations urgentes en matière de sécurité sur plusieurs plateformes d'échange.
Comment une injection de script peut-elle cibler à la fois les données utilisateur et les fonds transférés ?

Auteur et source de l'article : Slow Mist Tech

Context

Récemment, les analystes de sécurité de SlowMist, lors de leur veille quotidienne sur les menaces, ont détecté une campagne d'attaque impliquant le poisonage de scripts liés au système de service client Udesk. L'équipe a ensuite obtenu deux échantillons JavaScript, udeskApi.2026-10-16.cached.js et deeps.js, et a entrepris une analyse pour déterminer comment le code malveillant s'infiltre dans les pages via les scripts de service client et quelles en sont les conséquences sur les données des utilisateurs et les opérations commerciales.

L'analyse progressive de l'échantillon a révélé deux types de risques : un script malveillant collecte simultanément les données de session et les informations sensibles soumises par les utilisateurs sur la page, tout en modifiant les adresses de dépôt et en copiant le contenu selon les règles du site. Le premier risque permettrait à un attaquant de se faire passer pour une session connectée ou de prendre le contrôle d'un compte, tandis que le second pourrait amener les utilisateurs à transférer leurs fonds de dépôt vers une adresse de réception remplacée. En partant du point d'entrée du chargement du script client, nous avons reconstitué la mise en œuvre et les conditions de déclenchement de ces deux comportements en suivant les chemins de lecture des données, d'envoi réseau et de modification de la page.

MistEye répond

MistEye est un système de veille sur les menaces Web3 et de surveillance de sécurité en temps réel, développé en interne par SlowMist, intégrant des capacités de surveillance sécurisée et d'agrégation de renseignements pour offrir aux utilisateurs des alertes de risque en temps réel et une protection de leurs actifs.

MistEye a synchronisé les risques via les notifications d'informations et les canaux d'alerte clients dès que possible.

Chargeur malveillant et mécanisme de chargement de charge à distance

Après avoir ouvert udeskApi.2026-10-16.cached.js, vous pouvez voir le code des fonctionnalités telles que le bouton de service client, l'iframe de chat, le suivi des visiteurs et la communication des messages. En continuant à examiner la fin du fichier, après la fin de la structure principale, un fonction indépendante est présente : elle décode deux chaînes via _ud(), crée des éléments et les insère dans l'head du document actuel.

Cette section ne mentionne pas directement le nom de l'étiquette ni l'URL distante. Si l'on ne recherche que le nom de domaine en clair, le point d'entrée de chargement risque d'être omis. En examinant l'implémentation de _ud(), nous constatons qu'elle récupère deux caractères hexadécimaux à chaque fois, puis les convertit en caractère ; une fois les constantes déroulées, la cible de chargement devient claire.

736372697074 décodé en script, une autre constante décodée en https[:]//udeskqlobal[.]com/deeps.js. Le domaine contient « qlobal » commençant par la lettre q ; cette orthographe doit être conservée lors de la recherche. Le chargeur définit le script comme asynchrone, puis l'insère dans la page ; lorsque l'exécution atteint cet emplacement et que les stratégies du navigateur et le réseau le permettent, le code distant peut être exécuté dans le document actuel.

La charge correspondante à cette URL est deeps.js. L'accent de l'enquête a été déplacé du SDK client vers la charge elle-même. Les scripts tiers classiques s'exécutent dans le contexte du document intégré ; les données auxquelles ils peuvent accéder dépendent de la page ou de l'iframe sur laquelle ils s'exécutent réellement. La fenêtre de chat client utilise un iframe et n'isole pas automatiquement les scripts chargés sur la page hôte.

Mécanisme de collecte de données web et périmètre d'impact

deeps.js lit d'abord window.location.hostname et génère la configuration C selon les règles des trois sites. Pour déterminer quelles pages seront collectées, cette configuration seule n'est pas suffisante : il faut déterminer si elle limite l'ensemble du script ou un seul de ses modules, et continuer à suivre l'emplacement de la vérification de sortie.

En suivant le flux de contrôle, nous constatons que la lecture des cookies et du stockage web, l'envoi de captures d'écran de démarrage, ainsi que les hooks de formulaire et d'authentification apparaissent tous avant if (!C) return ; Ci-dessous, nous extrayons les instructions d'envoi de capture d'écran et le hook XHR final dans l'ordre du code source ; les commentaires indiquent le code omis entre les deux, correspondant à la vue formatée D:L100, D:L151–L166.

L'emplacement de la sortie a été clarifié : même si le hostname ne correspond pas aux trois règles de site, le script entrera d'abord dans le chemin de collecte de données et installera les crochets avant de quitter le module d'adresse. Lors de l'analyse des pages exposées, il ne faut pas se limiter à rechercher uniquement autour de ces trois configurations de site.

Au démarrage, la charge utile lit les cookies, le localStorage et le sessionStorage accessibles à la page actuelle. Ces emplacements peuvent stocker l'état de connexion, les profils utilisateurs et les données commerciales ; si l'un d'entre eux contient un jeton de connexion encore valide et réutilisable, un attaquant pourra l'exploiter pour usurper la session utilisateur. Le script inclut également l'URL de la page principale et l'origine du document actuel, afin de soumettre ces données avec la page d'origine.

Le processus de collecte est tolérant aux erreurs individuelles. Lorsque la lecture de l'URL de niveau supérieur provenant d'une source externe échoue, le script remplace la valeur par x et continue le traitement des autres champs ; les deux types de pages stockées sont sérialisés en JSON. La portée des cookies est limitée au contenu lisible via document.cookie ; les cookies HttpOnly n'en font pas partie.

Le script conserve également des points d'entrée pour les données soumises ultérieurement. Lorsqu'un utilisateur soumet le formulaire, il parcourt les éléments input, select et textarea, copie le contenu non vide, puis poursuit le processus de soumission original. Les champs réellement remplis sur la page, tels que le nom d'utilisateur, le mot de passe et le code de vérification, peuvent être acheminés vers des chemins d'envoi de données supplémentaires ; leur fuite pourrait être utilisée pour se connecter au compte ou en prendre le contrôle.

Ce chemin implémente le wrapping de HTMLFormElement.prototype.submit et l'écoute des événements submit en phase de capture. Pour les pages soumettant des données directement via une interface, le script enveloppe également fetch et XHR : lorsque la requête contient un corps et que l'URL contient des mots-clés tels que login|signin|auth|register|signup|password, le contenu correspondant est copié tout en conservant l'appel d'origine. La branche fetch exige en outre que l'URL soit une chaîne de caractères et que le corps provienne des options de la requête ; lors de la sérialisation JSON directe d'objets tels que FormData, les données collectées peuvent manquer certains champs réels.

Mécanisme de fuite de données et canal de secours

L'entrée de collecte ci-dessus appelle finalement la même fonction _send(). En poursuivant l'analyse de cette fonction, on constate que le destinataire fixe est https[:]//udeskqlobal[.]com/c.php. L'instantané de démarrage utilise les champs s, o, c, l, ss pour transporter respectivement l'adresse de la page, la source actuelle, les cookies lisibles et deux types de stockage web ; les formulaires et les demandes d'authentification suivants sont envoyés à chaque déclenchement.

La fonction extrait les paramètres après le point d'interrogation de l'URL et les insère dans le corps d'une requête POST asynchrone, en définissant Content-Type : application/x-www-form-urlencoded. Voici la fonction d'envoi entièrement conservée (D:L45–L60) :

Le chemin d'envoi inclut également un mécanisme de secours pour les demandes d'image : en cas d'erreur XHR ou d'anomalie synchrone pendant l'envoi, le script crée un objet Image, attribue l'URL avec les paramètres à la propriété src, et tente de réenvoyer les données via une requête GET. L'URL est tronquée aux 8 000 premiers caractères JavaScript ; les contenus plus longs peuvent être incomplets.

Lors de l'analyse de ce chemin, il est nécessaire de distinguer l'« envoi de la requête » et la « lecture de la réponse ». Le script ne lit pas la réponse de c.php ; une requête POST cross-origin répondant aux conditions d'une requête simple peut être envoyée avant que le navigateur ne limite la lecture de la réponse, ce qui signifie que l'erreur CORS peut se produire simultanément à l'envoi de la requête. Les journaux d'analyse doivent vérifier à la fois le corps POST et les paramètres GET de l'image, tandis que les stratégies de sortie doivent vérifier séparément connect-src et img-src. Un formulaire classique déclenchant une navigation de document peut également affecter la capacité d'une requête asynchrone à s'achever.

À ce stade, la destination des données de session web et du contenu soumis ultérieurement est claire. Toutefois, après la détection de la sortie du site, la charge utile conserve un autre ensemble d'opérations : identifier le format d'adresse, modifier le texte de la page et prendre le contrôle du chemin de copie. Ces codes orientent davantage l'enquête vers les opérations de dépôt.

Remplacement d'adresse de dépôt et mécanisme de hijacking du presse-papiers

Après if (!C) return; , le script entre dans le module d'adresse. Les trois règles hostname initiales prennent désormais un rôle précis : faire correspondre mgbx[.]com, bikingex[.] et bitxex[.]io afin de sélectionner les éléments pour les adresses et les boutons de copie sur différentes pages.

Ces expressions régulières ne possèdent pas d'ancres de début et de fin, ce qui élargit la plage de correspondance par rapport à une comparaison exacte de nom de domaine. Les règles de page vérifient également l'ensemble de location.href, et les paramètres de requête ou fragments contenant des mots-clés tels que deposit, recharge, assets participent également à la détection. Une fois que la page, l'élément ou le format d'adresse correspond, le script analyse toutes les 200 millisecondes et remplace les adresses de dépôt identifiées par la valeur configurée. Si l'utilisateur effectue un virement valide en utilisant l'adresse remplacée, le destinataire du paiement deviendra cette adresse.

Trois sites filiaux partagent six valeurs de configuration, marquées dans le code comme BTC, EVM, TRON, Solana, LTC et DOGE. L'identification des adresses repose uniquement sur la forme des caractères et l'ordre de correspondance, sans vérifier le réseau sélectionné par l'utilisateur ni le code de vérification de l'adresse, et peut également identifier incorrectement des identifiants commerciaux ordinaires.

Continuez à vérifier le code copié ; vous pouvez voir que l’échantillon intervient à la fois sur l’affichage de la page et sur le contenu du presse-papiers. Ainsi, même si l’utilisateur compare l’adresse de la page avec le résultat copié, il peut obtenir des valeurs altérées cohérentes. Les trois chemins sont :

  • Emballage de l'API du presse-papiers. navigator.clipboard.writeText appelle d'abord R() pour remplacer les paramètres, puis les transmet à la méthode d'origine. Ce chemin n'est soumis qu'à la restriction de configuration de l'hôte, ne vérifie plus les conditions de la page de dépôt, et ne dépend pas de l'analyse DOM des éléments déjà ciblés.
  • Remplacez le bouton de copie. Le script clone le bouton correspondant, installe un nouvel écouteur de clic et remplace le nœud d'origine. Le nouvel écouteur empêche l'action par défaut et la propagation de l'événement, puis tente de copier l'adresse stockée dans la variable partagée _cur.
  • Intercepter l'événement copy. Lorsque _cur n'est pas vide et que la règle de page est remplie, le écouteur sur document remplace le contenu copié par _cur, sans vérifier si le texte sélectionné par l'utilisateur est une adresse.

Parmi ceux-ci, l'interface API du presse-papiers et le écouteur de copie sont adjacents dans le code source (D:L260–L275), mais les conditions de déclenchement sont différentes :

_cur enregistre l'adresse remplacée la dernière fois. Lorsqu'il y a plusieurs adresses sur la même page, le remplacement suivant écrase l'enregistrement précédent, et plusieurs boutons peuvent copier la même valeur. Si l'adresse de configuration est déjà affichée au chargement de la page, le scan n'initialisera pas _cur, et les nouveaux écouteurs d'événements n'écriront donc rien ; les attributs d'événements intégrés HTML conservés par clonage peuvent toujours participer au traitement des événements.

L'analyse a également révélé un défaut d'implémentation affectant le jugement du comportement : le script tente de lire le descripteur de propriété propre de textContent depuis Element.prototype, alors que la définition standard de la propriété se trouve sur Node.prototype. Dans la relation de prototype standard, un accès ultérieur à .set lèvera une exception. Après l'échec de cette branche, le catch ignore l'exception ; en examinant le code avant et après, on constate que le polling est déjà enregistré et que les crochets de copie seront installés selon leurs conditions respectives. Une erreur dans une seule branche n'a pas interrompu le module d'usurpation d'adresse.

Résumé

Après avoir suivi le chargeur en bas du script du service client, nous avons déterminé que la cible de l'attaque consiste à collecter des données permettant d'usurper des sessions ou de se connecter à des comptes, tout en remplaçant les adresses de réception dans les pages de dépôt et les opérations de copie par des valeurs configurées. La première cible n'est pas soumise aux restrictions de configuration des trois sites, tandis que la seconde est activée en fonction du site et du chemin de copie spécifique ; il est donc nécessaire d'analyser différents scénarios d'exposition lors de l'enquête.

Les entreprises qui continuent de charger des scripts associés doivent cesser leur distribution, révoquer les identifiants exposés et vérifier les objectifs de dépôt via des systèmes fiables et les historiques de transactions.

Suggestion

  1. Sécurisez et arrêtez la distribution des ressources contaminées. Si la production charge toujours les scripts associés, bloquez-les dès que possible après avoir enregistré la réponse réelle, la chaîne de demande, les informations de cache et l'heure de collecte ; récupérez à partir d'une version fiable vérifiée indépendamment, puis vérifiez les configurations d'hébergement du site, de CDN, de build et de gestion des balises.
  2. Recherche du chargement et des enregistrements d'exfiltration. Requête conjointe pour les domaines malveillants, deux chemins et les hachages de fichiers ; les enregistrements d'exfiltration vérifient à la fois le corps POST et les paramètres GET. Lors de l'analyse des scripts compressés, recherche également les URL hexadécimales dans le chargeur pour éviter de manquer les exfiltrations en texte clair.
  3. Fermez le document contaminé et gérez les identifiants exposés. Fermez complètement les pages ou documents WebView associés pour éviter le rechargement de ressources anciennes. Selon l'étendue réelle de l'exposition, annulez les sessions, faites pivoter les jetons et les mots de passe depuis un environnement propre ; supprimer les balises script ne désactive pas les rappels et les temporisateurs déjà installés.
  4. Vérifiez indépendamment l'adresse de dépôt et la transaction. Pour la page exécutant la charge, vérifiez l'adresse de dépôt originale, la transaction associée et la destination réelle du virement via des canaux fiables, et évaluez l'impact selon les registres de transaction.
  5. Vérifier les scripts et les restrictions d’exportation de données. En combinant les paramètres métier, contrôler la source et l’intégrité des scripts, et vérifier séparément les politiques telles que connect-src et img-src, en limitant le chargement de scripts et l’exportation de données pour udeskqlobal[.]com.

IOC

Domain name

udeskqlobal[.]com

URL

https[:]//udeskqlobal[.]com/deeps[.]js

https[:]//udeskqlobal[.]com/c[.]php

Adresse du portefeuille

BTC : bc1qhzppfj69ywp22swj50jwcfxdamg3ym245srng9

EVM : 0x9B6EcD0023a28E1391c5124eCe578A9751A337b2

TRON : TH4L5YAhhxtC8qALbZKbPZZgausbGi25nF

Solana : AFmCUe3nNbjTaC585CcdA3vvC9iWVR8YunooFKpaQx2Z

LTC : ltc1qwz0zp39ywl34kahvn9lyk7j5x63wsy9tcxhm3w

DOGE : DAoy2w8uvFtZ92q87s9qKEoHbWgV7mVThj

Fichier malveillant

filename: udeskApi.2026-10-16.cached.js

SHA256 : 45b4e8f160d8d5177819f6c1dc67acc7abbdc65eeaffc909e668799dc778bcf6

fichier : deeps.js

SHA256 : 57c1441c4f5bb3260981a2df64f9ff35f117abde0d8667c8c66a047880074c59

À propos de MistEye

MistEye est une plateforme de renseignement sur les menaces Web3 et de surveillance de sécurité dynamique développée en interne par SlowMist, offrant via une API la détection d'activités malveillantes et des alertes sur les risques de la chaîne d'approvisionnement pour l'écosystème de paquets open source.

Tous les paquets malveillants et les IOC impliqués dans cette action ont été intégrés au moteur de détection de menaces MistEye. Les développeurs peuvent effectuer une détection automatisée des dépendances de projet via l’API pour déterminer rapidement s’ils correspondent à des paquets malveillants connus et obtenir des recommandations de traitement.

Clause de non-responsabilité : les informations sur cette page peuvent avoir été obtenues auprès de tiers et ne reflètent pas nécessairement les points de vue ou opinions de KuCoin. Ce contenu est fourni à titre informatif uniquement, sans aucune représentation ou garantie d’aucune sorte, et ne doit pas être interprété comme un conseil en investissement. KuCoin ne sera pas responsable des erreurs ou omissions, ni des résultats résultant de l’utilisation de ces informations. Les investissements dans les actifs numériques peuvent être risqués. Veuillez évaluer soigneusement les risques d’un produit et votre tolérance au risque en fonction de votre propre situation financière. Pour plus d’informations, veuillez consulter nos conditions d’utilisation et divulgation des risques.