Slow Pisces (também conhecido como Jade Sleet, TraderTraitor, PUKCHONG) é um grupo de ameaças patrocinado pelo estado norte-coreano, cujo foco principal é gerar receita para o regime da RPDC, geralmente visando grandes organizações no setor de criptomoedas. Este artigo analisa a campanha do grupo, a qual acreditamos estar relacionada a roubos recentes de criptomoedas.
Nessa campanha, o Slow Pisces se envolveu com desenvolvedores de criptomoedas no LinkedIn, fazendo-se passar por possíveis empregadores e enviando malware disfarçado de desafios de programação. Esses desafios exigem que os desenvolvedores executem um projeto comprometido, infectando seus sistemas usando um malware que denominamos RN Loader e RN Stealer.
O grupo teria roubado mais de US$ 1 bilhão do setor de criptomoedas em 2023. Eles conseguiram isso usando vários métodos, incluindo plataformas falsas de negociação e trading, malware distribuído por meio do Node Package Manager (NPM) e comprometimentos da cadeia de suprimentos.
Em dezembro de 2024, o FBI atribuiu o roubo de US$ 308 milhões de uma empresa de criptomoedas com sede no Japão ao Slow Pisces. Mais recentemente, o grupo ganhou as manchetes por seu suposto envolvimento no roubo de US$ 1,5 bilhão de uma bolsa de criptomoedas de Dubai.
Compartilhamos nossa inteligência de ameaças com analistas do GitHub e do LinkedIn para derrubar as contas e os repositórios relevantes.
Eles forneceram a seguinte declaração em resposta:
O GitHub e o LinkedIn removeram essas contas maliciosas por violarem nossos respectivos termos de serviço. Em todos os nossos produtos, usamos tecnologia automatizada, combinada com equipes de especialistas em investigação e reportes de membros, para combater atores mal-intencionados e fazer cumprir os termos de serviço. Continuamos a evoluir e a melhorar nossos processos e incentivamos nossos clientes e membros a reportarem qualquer atividade suspeita.
Informações adicionais
- Os usuários do GitHub podem encontrar mais informações nas páginas Políticas de Uso Aceitável e reportar abuso e spam.
- Os usuários do LinkedIn podem saber mais sobre como identificar e denunciar abusos aqui: Reconhecer e denunciar spam, conteúdo inadequado e abusivo | Ajuda do LinkedIn.
Este relatório detalha como o Slow Pisces oculta o malware em seus desafios de programação e descreve as ferramentas subsequentes do grupo, com o objetivo de fornecer para a indústria em geral uma melhor compreensão dessa ameaça.
Os clientes da Palo Alto Networks estão mais bem protegidos contra as ameaças discutidas neste artigo por meio de nosso Firewall de próxima geração (“Next-Generation Firewall”) com assinaturas Advanced URL Filtering e Advanced DNS Security .
Se você acha que pode ter sido comprometido ou tem um assunto urgente, entre em contato com a equipe de resposta a incidentes da Unit 42.
Tópicos relacionados da Unit 42 | Criptomoedas, RPDC |
Análise técnica
Nossa visibilidade dessa campanha segue, em linhas gerais, três etapas, ilustradas abaixo na Figura 1.

Etapa 1 - Iscas em PDF
O Slow Pisces começou se passando por recrutadores no LinkedIn e se envolvendo com alvos em potencial, enviando-lhes um PDF benigno com uma descrição de emprego, conforme mostrado abaixo na Figura 2. Se os possíveis alvos se candidatassem, os atacantes apresentavam a eles um desafio de programação que consistia em várias tarefas descritas em uma folha de perguntas.

Observamos o Slow Pisces se passando por várias organizações com essas iscas, principalmente no setor de criptomoedas. As folhas de perguntas incluíam tarefas genéricas de desenvolvimento de software e um desafio de programação de um "projeto real", relacionado a um repositório do GitHub mostrado na Figura 3 abaixo.

Etapa 2 - Repositórios do GitHub
O Slow Pisces apresentou aos alvos os chamados desafios de programação como projetos de repositórios do GitHub. Os repositórios continham códigos adaptados de projetos de código aberto, incluindo aplicativos para visualização e análise:
- Dados do mercado de ações
- Estatísticas das ligas de futebol europeias
- Dados meteorológicos
- Preços de criptomoedas
O grupo usou principalmente projetos em Python ou JavaScript, provavelmente dependendo do fato de o alvo ter se candidatado a uma função de desenvolvimento de front-end ou back-end. Também vimos repositórios baseados em Java nessa campanha, embora fossem muito menos comuns, com apenas duas instâncias representando um aplicativo de criptomoedas chamado jCoin.
Essa escassez sugere que os invasores podem ter criado repositórios sob demanda, com base na linguagem de programação preferida do alvo. Consequentemente, o grupo usou com mais frequência as linguagens mais populares no setor de criptomoedas, como JavaScript e Python. Da mesma forma, repositórios não descobertos também podem existir para outras linguagens de programação.
Etapa 3a - Repositório Python
No final de 2024, o grupo usou um projeto mostrado abaixo na Figura 4, intitulado "Stocks Pattern Analyzer", adaptado de um repositório legítimo.

A maior parte do código no repositório é benigna. Quando os alvos tentam executar o projeto de acordo com a folha de perguntas, dados são obtidos de três locais remotos:
- hxxps://en.wikipedia[.]org/wiki/List_of_S%26P_500_companies
- hxxps://en.wikipedia[.]org/wiki/Currency_pair
- hxxps://en.stockslab[.]org/symbols/sp500
Duas das URLs extraem dados da Wikipedia. A terceira URL usa um domínio controlado pelo Slow Pisces. Esse padrão — usar várias fontes de dados, a maioria legítima, mas uma maliciosa — é comum nos repositórios Python do grupo.
O servidor mal-intencionado de comando e controle (C2) é configurado para imitar o formato das fontes legítimas. Nesse caso, ele usa o subdomínio .en e o domínio de nível superior (TLD) .org , como vemos no domínio legítimo da Wikipédia acima.
Desserialização de YAML
O Slow Pisces poderia simplesmente colocar o malware diretamente no repositório ou executar o código do servidor C2 usando as funções internas eval ou exec do Python. No entanto, essas técnicas são facilmente detectadas, tanto por inspeção manual quanto por soluções antivírus.
Em vez disso, o Slow Pisces primeiro garante que o servidor C2 responda com dados válidos do aplicativo. Por exemplo, o repositório mencionado acima espera uma lista de símbolos de empresas S&P 500. A URL C2 responde inicialmente com esses dados em uma lista formatada em JSON.
Os atores de ameaça enviam apenas uma carga maliciosa para alvos validados, provavelmente com base no endereço IP, na geolocalização, no horário e nos cabeçalhos de solicitação HTTP. O foco em indivíduos contatados via LinkedIn, em oposição a campanhas amplas de phishing, permite que o grupo controle rigidamente os estágios posteriores da campanha e entregue payloads apenas às vítimas esperadas.
Para evitar as funções suspeitas eval e exec , o Slow Pisces usa desserialização YAML para executar sua carga útil, conforme mostrado na Figura 5.

Esse código obtém dados do servidor C2 via HTTPS e verifica o cabeçalho de resposta Content-Type. Se o cabeçalho indicar dados JSON (application/json), o código analisará e retornará o JSON para o aplicativo.
Se a resposta indicar dados YAML (application/yaml), o código usará a função yaml.load() da biblioteca PyYAML para analisar os dados. Essa função é inerentemente insegura e a documentação do PyYAML recomenda explicitamente yaml.safe_load() para entrada não confiável.
O YAML é normalmente usado para arquivos de configuração, como o exemplo mostrado abaixo:
1 2 3 4 5 6 7 8 9 |
username: slow password: pisces api: key: supersecret url: example.com |
No entanto, yaml.load() pode serializar e desserializar objetos Python arbitrários, não apenas dados YAML válidos. Por exemplo, o código Python a seguir imprime os números de 0 a 4:
1 |
range(0, 5) |
Se esse código fosse serializado usando yaml.dump(), ele se tornaria o seguinte:
1 2 3 4 5 6 7 |
!!python/object/apply:builtins.range - 0 - 5 - 1 |
Por fim, quando esses dados forem passados para yaml.load() , ele executará o código original: range(0, 5).
Isso destaca um ponto de detecção em potencial, pois os payloads para o repositório Python e o malware que usa desserialização YAML em geral contêm !!python/object/apply:builtins se a carga útil usar uma função Python embutida.
Os seguintes estágios da Tabela 1 existem primariamente na memória e geralmente não deixam traços no disco. Para ajudar a comunidade na detecção e conscientização, fizemos o upload desses payloads para o VirusTotal. O payload de desserialização YAML executa o malware que chamamos de RN Loader e RN Stealer com base no formato de token C2 que observamos no RN Stealer, que discutiremos nas seções a seguir.
Estágio | Hash SHA256 |
Payload de desserialização YAML | 47e997b85ed3f51d2b1d37a6a61ae72185d9ceaf519e2fdb53bf7e761b7bc08f |
RN Loader | 937c533bddb8bbcd908b62f2bf48e5bc11160505df20fea91d9600d999eafa79 |
RN Stealer | e89bf606fbed8f68127934758726bbb5e68e751427f3bcad3ddf883cb2b50fc7 |
Tabela 1. Payloads do repositório Python.
O payload de desserialização YAML do Slow Pisces começa criando a pasta Public no diretório pessoal da vítima e criando um novo arquivo nesse diretório chamado __init__.py. Os dados Base64 incorporados são decodificados e escritos nesse arquivo, contendo o próximo estágio de infecção (RN Loader), que é então executado.
RN Loader
Esse arquivo recém-criado para o RN Loader em ~/Public/__init__.py se exclui após a execução, garantindo que ele exista somente na memória. Ele envia informações básicas sobre o computador e o sistema operacional da vítima por HTTPS para o mesmo C2 em en.stockslab[.]org, seguido por um loop de comando com as seguintes opções na Tabela 2.
Código | Descrição |
0 | Dormir (Sleep) por 20 segundos |
1 | Decodifica em base64 o conteúdo enviado e o salva no arquivo init.dll para Windows ou init para todos os outros sistemas operacionais.
Define uma variável de ambiente X_DATABASE_NAME como uma cadeia de caracteres (string) vazia. Carrega e executa a DLL baixada usando ctypes.cdll.LoadLibrary. |
2 | Decodifica em base64 o conteúdo enviado e o executa usando o exec incorporado ao Python. |
3 | Decodifica em base64 o conteúdo enviado e um parâmetro. O conteúdo é salvo no arquivo dockerd, enquanto o parâmetro é salvo como docker-init.
O dockerd é então executado em um novo processo, com o docker-init fornecido como argumento de linha de comando. |
9 | Termina a execução. |
Tabela 2. Tabela de comandos do RN Loader.
Os payloads do loop de comando da Tabela 2 usando as opções 1 e 3 são atualmente desconhecidos e provavelmente são acionados por condições específicas. No entanto, recuperamos um infostealer baseado em Python fornecido pela opção 2, e rastreamos esse malware como RN Stealer.
RN Stealer
O RN Stealer primeiro gera um ID de vítima aleatório, usado posteriormente como um cookie em todas as comunicações com o servidor C2. Em seguida, ele solicita uma chave XOR do servidor para encriptar os dados exfiltrados.
A comunicação com o servidor C2 ocorre por HTTPS, usando tokens codificados em Base64 para identificar os tipos de solicitação e resposta. O payload analisado inclui quatro tipos de token:
- R0 - solicitando a chave XOR
- R64 - exfiltrando dados
- R128 - exfiltrando dados compactados
- R256 - infostealer completo
O formato desses tipos de token (a letra R seguida de um número inteiro N) levou a nossos nomes para esse payload. Chamamos o payload de RN Stealer e o estágio anterior de RN Loader.
Recuperamos o script para essa amostra do RN Stealer de um sistema macOS. Dessa forma, os autores da ameaça adaptaram essa amostra para roubar informações específicas de dispositivos macOS, incluindo:
- Informações básicas sobre a vítima: nome de usuário, nome da máquina e arquitetura
- Aplicativos instalados
- Uma lista de diretórios e o conteúdo de nível superior do diretório inicial da vítima
- O arquivo login.keychain-db, que armazena as credenciais salvas em sistemas macOS
- Chaves SSH armazenadas
- Arquivos de configuração para AWS, Kubernetes e Google Cloud
Os dados coletados pelo RN Stealer provavelmente determinam se acesso persistente é necessário. Em caso afirmativo, podemos inferir as seguintes etapas para essa cadeia de infecção do Python:
- O servidor C2 verifica vítimas que estejam enviando sinais (beaconing) com relação a um conjunto desconhecido de critérios. As vítimas válidas recebem um payload de desserialização YAML. Vítimas inválidas recebem dados JSON benignos.
- O payload de desserialização estabelece um loop de comando com o servidor C2, exfiltrando informações básicas da vítima e fornecendo um infostealer Python personalizado por meio do código de opção 2 na Tabela 2.
- O infostealer reúne informações mais detalhadas sobre a vítima, que os atacantes provavelmente usaram para determinar se precisavam de acesso contínuo.
- Se o acesso contínuo for necessário, o servidor C2 entrega um payload por meio dos códigos de opção 1 ou 3.
- Se o acesso não for mais necessário, o código de opção 9 encerra a execução do malware, removendo todo o acesso, já que o payloadreside apenas na memória.
Estágio 3b - Repositório JavaScript
Se as vítimas se candidatassem a uma função JavaScript, elas poderiam por sua vez encontrar um projeto "Cryptocurrency Dashboard", semelhante ao exemplo da Figura 6 abaixo.

Esse aplicativo contém um arquivo .env com o C2 e um datasource legítimo:
- PORT=3000
- COINGECKO_API_URL=hxxps://api.coingecko[.]com/api/v3
- JQUERY_API_URL=hxxps://update.jquerycloud[.]io/api/v1
O valor COINGECKO_API_URL é usado para buscar dados para o Cryptocurrency Dashboard, enquanto o valor JQUERY_API_URL representa um servidor C2 controlado pelo Slow Pisces. Semelhante ao repositório Python, o servidor JavaScript C2 fornece payloads apenas para alvos validados; caso contrário, ele responde com um número de versão.
O repositório usa a ferramenta de modelagem Embedded JavaScript (EJS), passando as respostas do servidor C2 para a função ejs.render(), mostrada abaixo na Figura 7.

Assim como o uso de yaml.load(), essa é outra técnica que o Slow Pisces utiliza para ocultar a execução de código arbitrário de seus servidores C2, e esse método talvez só seja aparente ao visualizar um payload válido.
A função de renderização EJS aceita vários parâmetros, um dos quais é chamado de view options. Dentro disso, um código JavaScript arbitrário pode ser fornecido e executado por meio da chave escapeFunction.
Um pesquisador de Taiwan, conhecido como Huli, discutiu os detalhes técnicos de como isso resulta em execução arbitrária de código em um post de um CTF. No entanto, podemos entender suficientemente que um payload estruturado conforme mostrado na Figura 8 resultará na execução do código contido em escapeFunction quando passado para ejs.render().

Infelizmente, não conseguimos recuperar a parte total desse payload. Dessa forma, podemos apenas supor que um novo diretório .jql é criado no diretório inicial do usuário, onde um arquivo chamado helper.js é baixado (dropped), contendo dados codificados em Base64.
Infraestrutura
A linha do tempo abaixo, na Figura 9, detalha a infraestrutura de C2 usada nessa campanha de fevereiro de 2024 a fevereiro de 2025, agrupada pelo tipo de repositório servido (JavaScript ou Python).

Como mencionado anteriormente, os domínios na infraestrutura desta campanha podem imitar o formato das fontes legítimas usadas junto com eles, frequentemente usando subdomínios como .api ou .cdn. Até o momento da publicação deste artigo, descobrimos a infraestrutura associada a essa campanha.
Conclusão
Este relatório abordou a campanha mais recente do Slow Pisces, que se fez passar por recrutadores no LinkedIn para atingir desenvolvedores do setor de criptomoedas com desafios de programação maliciosos. Embora não tenhamos conseguido recuperar a cadeia de ataque completa para os repositórios JavaScript, a versão Python da campanha forneceu dois novos payloads que chamamos de RN Loader e RN Stealer.
O uso do LinkedIn e do GitHub dessa maneira não é exclusivo. Vários grupos afiliados à RPDC usaram táticas semelhantes, como Alluring Pisces e Contagious Interview.
Esses grupos não apresentam sobreposições operacionais. No entanto, é digno de nota o fato de essas campanhas usarem vetores de infecção inicial semelhantes.
O Slow Pisces se destaca das campanhas de seus pares em segurança operacional. A entrega de payloads em cada estágio é fortemente protegida, existindo apenas na memória. E o ferramental de estágio posterior do grupo só é implantado quando necessário.
Em particular, o grupo fez uso de duas técnicas para ocultar a funcionalidade:
- Desserialização de YAML
- EJS escapeFunction
Essas duas técnicas dificultam muito a análise, a detecção e a caça de ameaças. Da mesma forma, desenvolvedores relativamente novos ou inexperientes no setor de criptomoedas teriam dificuldade em identificar esses repositórios como maliciosos.
Com base em relatórios públicos de roubos de criptomoedas, essa campanha parece ser altamente bem-sucedida e provavelmente persistirá em 2025. Embora este artigo tenha destacado duas possíveis oportunidades de detecção para desserialização de YAML e payloads de EJS escapeFunction, a mitigação mais eficaz continua sendo a segregação rigorosa de dispositivos corporativos e pessoais. Isso ajuda a evitar o comprometimento de sistemas corporativos por campanhas de engenharia social direcionadas.
Proteção e mitigação da Palo Alto Networks
Os clientes da Palo Alto Networks estão mais bem protegidos contra as ameaças discutidas acima por meio dos seguintes produtos:
Se você acha que pode ter sido comprometido ou tem um assunto urgente, entre em contato com a equipe de resposta a incidentes da Unit 42 ou ligue para:.
- América do Norte: ligação gratuita: +1 (866) 486-4842 (866.4.UNIT42)
- Reino Unido: +44.20.3743.3660
- Europa e Oriente Médio: +31.20.299.3130
- Ásia: +65.6983.8730
- Japão: +81.50.1790.0200
- Austrália: +61.2.4062.7950
- Índia: 00080005045107
A Palo Alto Networks compartilhou essas descobertas com nossos colegas membros da Cyber Threat Alliance (CTA). Os membros da CTA usam essa inteligência para implantar rapidamente proteções para seus clientes e para interromper sistematicamente os atores cibernéticos mal-intencionados. Saiba mais sobre a Cyber Threat Alliance.
Indicadores de comprometimento
Domínio | Endereço IP | Visto pela primeira vez | Visto pela última vez | Repositório |
getstockprice[.]com | 70.34.245[.]118 | 2025-02-03 | 2025-02-20 | Python |
cdn[.]clubinfo[.]io | 5.206.227[.]51 | 2025-01-21 | 2025-02-19 | Python |
getstockprice[.]info | 131.226.2[.]120 | 2025-01-21 | 2025-01-23 | Python |
api[.]stockinfo[.]io | 136.244.93[.]248 | 2024-10-30 | 2024-11-11 | Python |
cdn[.]logoeye[.]net | 54.39.83[.]151 | 2024-10-29 | 2024-11-03 | Python |
en[.]wfinance[.]org | 195.133.26[.]32 | 2024-10-12 | 2024-11-01 | Python |
en[.]stocksindex[.]org | 185.236.231[.]224 | 2024-09-11 | 2024-10-04 | Python |
cdn[.]jqueryversion[.]net | 194.11.226[.]16 | 2024-08-23 | 2024-09-23 | JavaScript |
en[.]stockslab[.]org | 91.103.140[.]191 | 2024-08-19 | 2024-09-12 | Python |
update[.]jquerycloud[.]io | 192.236.199[.]57 | 2024-07-03 | 2024-08-22 | JavaScript |
cdn[.]soccerlab[.]io | 146.70.124[.]70 | 2024-08-07 | 2024-08-21 | Python |
api[.]coinpricehub[.]io | 45.141.58[.]40 | 2024-05-06 | 2024-08-06 | Java |
cdn[.]leaguehub[.]net | 5.133.9[.]252 | 2024-07-15 | 2024-07-21 | Python |
cdn[.]clublogos[.]io | 146.19.173[.]29 | 2024-06-24 | 2024-07-12 | Python |
api[.]jquery-release[.]com | 146.70.125[.]120 | 2024-06-10 | 2024-06-28 | JavaScript |
cdn[.]logosports[.]net | 185.62.58[.]74 | 2024-05-08 | 2024-06-23 | Python |
skypredict[.]org | 80.82.77[.]80 | 2024-05-06 | 2024-06-16 | JavaScript |
api[.]bitzone[.]io | 192.248.145[.]210 | 2024-04-25 | 2024-05-13 | Python |
weatherdatahub[.]org | 194.15.112[.]200 | 2024-04-05 | 2024-05-03 | JavaScript |
api[.]ethzone[.]io | 91.234.199[.]90 | 2024-04-16 | 2024-04-24 | Python |
api[.]fivebit[.]io | 185.216.144[.]41 | 2024-04-08 | 2024-04-14 | Python |
blockprices[.]io | 91.193.18[.]201 | 2024-03-15 | 2024-04-09 | JavaScript |
api[.]coinhar[.]io | 185.62.58[.]122 | 2024-03-26 | 2024-04-09 | Python |
mavenradar[.]com | 23.254.230[.]253 | 2024-02-21 | 2024-03-26 | JavaScript |
indobit[.]io | 146.70.88[.]126 | 2024-03-19 | 2024-03-20 | Python |
api[.]thaibit[.]io | 79.137.248[.]193 | 2024-03-07 | 2024-03-09 | Python |
chainanalyser[.]com | 38.180.62[.]135 | 2024-02-23 | 2024-03-06 | JavaScript |
Recursos adicionais
- North Korea Responsible for $1.5 Billion Bybit Hack – Internet Crime Complaint Center (IC3)
- FBI, DC3, and NPA Identification of North Korean Cyber Actors, Tracked as TraderTraitor, Responsible for Theft of $308 Million USD from Bitcoin.DMM.com – FBI
- Security alert: social engineering campaign targets technology industry employees – GitHub Blog
- North Korea Leverages SaaS Provider in a Targeted Supply Chain Attack – Mandiant, Google Cloud