
Na semana passada, recebi uma mensagem de um recrutador de uma pequena startup de criptomoedas pelo LinkedIn. Após algumas trocas de mensagens, a recrutadora descreveu um protótipo quebrado que precisava de um engenheiro líder e, em seguida, me enviou um repositório público do GitHub para eu revisar. Ela pediu especificamente para eu "verificar o problema dos módulos Node obsoletos".
Embora não seja incomum solicitar a revisão de um código existente, algo me deixou desconfiado e acendeu um alerta em minha cabeça. Decidi, então, adotar uma abordagem mais cautelosa.
Em vez de clonar e instalar as dependências diretamente, criei uma VPS descartável na Hetzner, clonei o repositório lá e apontei uma ferramenta chamada Pi para ele em modo somente leitura, com apenas ferramentas de leitura de arquivo ativadas:
pi --tools read,grep,find,ls
Pedi ao agente para revisar o código e sinalizar qualquer coisa suspeita. Ele parou quase imediatamente em app/test/index.js.
Aparentemente, o repositório consistia em um frontend React com um backend Node. A armadilha estava em app/test/index.js, um arquivo com cerca de 250 linhas disfarçadas como uma suíte de testes. Dentro dele, uma URL era montada a partir de fragmentos:
const protocol = "https" , domain = "store" , separator = "://" , path = "/icons/" , token = "77" , subdomain = "rest-icon-handler" , bearrtoken = "logo" ;
Esses fragmentos se combinam em https://rest-icon-handler.store/icons/77.
Em seguida, enterrado entre paredes de testes comentados, o payload executa qualquer coisa que o servidor enviar de volta para sua máquina. O payload na linha 225 estava escondido à vista de todos entre os testes comentados.
O arquivo não espera os testes serem executados. O próprio app/index.js executa const test = require('./test'), que carrega e executa app/test/index.js. O package.json conecta app/index.js ao processo de inicialização:
prepare executa app:pre, que é node app/index.js.
O script prepare é crucial. O npm executa prepare automaticamente após o npm install, então, apenas instalar as dependências aciona a porta dos fundos. A instrução para "verificar o problema dos módulos Node obsoletos" era uma isca para me fazer rodar o npm install.
Poderia ter deixado o payload rodar no sandbox e observar o que o servidor enviava como segunda etapa, mas parei por ali. Um repositório que executa tudo o que um servidor lhe entrega era evidência suficiente.
Os commits no repositório estavam atribuídos a um desenvolvedor real, um engenheiro full-stack com um perfil comum no LinkedIn, um site pessoal e uma conta GitHub com um longo histórico. Enviei uma mensagem a ele, fingindo que havia herdado o código e tinha algumas perguntas sobre a implementação, para ver como ele reagiria.
Ele me disse que nunca trabalhou para eles. Ele já havia sido impersonado no GitHub antes e teve um repositório removido devido a isso, e não tinha nada a ver com este. Ele estava denunciando esses repositórios também.
Toda a história de commits, 39 no total, atribuídos a um desenvolvedor que nunca tocou no repositório.
A identidade emprestada da recrutadora
O perfil da recrutadora pertencia a uma jornalista de artes real, bem conhecida, que pesquisei depois, com um longo histórico cultural e nada técnico. Quando brinquei e disse que não consegui instalar o projeto, a jornalista rapidamente se transformou em uma especialista em versões de npm e Node. Foi bastante engraçado, eu diria.
Essa situação pode acontecer com qualquer um
Já ouvi falar desses ataques e li sobre eles na HN, mas quando um ocorreu comigo, ainda me pegou um pouco de surpresa. Suspeitei de algo desde as primeiras mensagens, mas em um dia mais cansativo ou apressado, eu poderia facilmente ter executado o npm install sem pensar. Portanto, se você receber uma mensagem no LinkedIn pedindo para revisar um repositório, um pouco de paranoia e bons hábitos de segurança nunca são demais.
Outro aprendizado foi que revisar o código com um agente somente leitura se mostrou mais produtivo do que ler eu mesmo. O backdoor estava disfarçado como código de iniciante descuidado, mas o agente o sinalizou em segundos.
Eu denunciei o repositório ao GitHub e a recrutadora ao LinkedIn. Até agora, nada mudou e o código ainda está disponível.
Confira os últimos vídeos publicados no canal