O que é um backdoor? Vamos criar um com Node.js
19 de março de 2020
0 minutos de leituraUm backdoor no nosso código capaz de executar uma injeção no sistema operacional é um dos cenários mais assustadores que existem. Atualmente, o npm tem mais de 1,2 milhão de pacotes públicos disponíveis. Nos últimos três anos, nossas dependências se tornaram o alvo perfeito para cibercriminosos. Vimos muitos ataques novos entrarem em ação, como o ataque de typosquatting e o incidente do event-stream, confirmando que nosso ecossistema pode ser muito frágil se não construirmos uma comunidade mais forte. Neste post, vou mostrar como podemos combinar algumas técnicas e acabar criando um backdoor malicioso no ecossistema.
O que é um backdoor?
No mundo da cibersegurança, backdoor é qualquer método que permite que usuários autorizados ou não contornem as medidas de segurança normais e obtenham acesso de alto nível (também chamado de acesso root) a um sistema de computador, rede ou aplicativo de software. Depois de entrar, os cibercriminosos podem usar um backdoor para roubar dados pessoais e financeiros, instalar outros malwares e sequestrar dispositivos.
Todo backdoor tem dois componentes principais: o malware injetado e executado na máquina da vítima e um canal de comunicação aberto que permite ao invasor enviar comandos e controlar o host remoto.
Depois que um backdoor é instalado, é preciso enviar comandos específicos para que sejam executados na máquina-alvo. Esses comandos podem exfiltrar informações confidenciais, como variáveis de ambiente ou dados de bancos de dados. Além disso, podem alterar outros processos na máquina ou na rede, dependendo das permissões de execução que nosso aplicativo Node infectado tiver.
Para criar uma versão simplificada desse ataque, usaremos a biblioteca nativa child_process para executar código e um servidor HTTP como canal de comunicação. Neste caso, sugiro usar Express, por ser o framework mais difundido, mas a técnica pode ser adaptada a outros frameworks.
Não se esqueça de testar seus projetos para encontrar backdoors conhecidos!
Como o child_process pode nos ajudar?
Podemos usar o módulo nativo child_process do Node.js para executar processos filhos. A ideia é executar um comando (chamamos essa entrada de stdin), como pwd ou ping snyk.io, e integrar a resposta (chamada de stdout) e os possíveis erros (chamados de stderr) ao programa principal.
Há várias maneiras de executar processos filhos. Para este ataque, a mais simples é exec, que executa um callback com stdout e stderr quando o processo termina, como em cat passwords.txt.Vale lembrar que exec não é a melhor opção para tarefas longas, como ping snyk.io.
Como podemos conectar esse exec ao servidor HTTP?
Desenvolvi um pacote de middleware simples e aparentemente inofensivo que redireciona qualquer usuário que não esteja usando o Chrome para outra URL, como browsehappy.com. Vou incluir o payload malicioso no middleware.
O código esperado no pacote seria mais ou menos assim:
Para a vítima, o único requisito é instalar a biblioteca browser-redirect e adicioná-la ao aplicativo Express como um middleware comum:
Vale lembrar que, neste caso, mesmo usando o Helmet, seu aplicativo continua vulnerável.
Como implementar o payload malicioso
A implementação é muito simples:
Como o backdoor funciona? Você precisa entender três desafios:
É preciso ter uma forma de se autenticar para impedir que outros cibercriminosos usem seus próprios backdoors. Neste caso, usamos um hash MD5 (a palavra
p@ssw0rd1234gera este hash:c4fbb68607bcbb25407e0362dab0b2ea). Você deve incluir esse hash como valor do cabeçalhoknock_kockpara validar a autenticação.É preciso identificar os servidores que contêm o código malicioso. Como não queríamos coletar métricas, adicionamos um cabeçalho extra à resposta do servidor:
Content-Sec-Policy— parece muito comContent-security-policy, mas não é a mesma coisa. Mais uma vez, estamos tirando proveito do ataque de typosquatting. Agora, podemos encontrar nossos alvos no Shodan usando esta consulta:/search?query=Content-Sec-Policy%3A+default-src+%27self%27Podemos usar o parâmetro de consulta ?cmd para executar comandos no servidor infectado, em qualquer rota — por exemplo,
victim.com/?cmd=whoamiou?cmd=cat .env— e receberemos todas as informações em formato JSON.
Agora que o payload malicioso está pronto, é preciso definir uma forma inteligente de distribuir o pacote malicioso.
Vetores de propagação
O primeiro passo é enviar o pacote malicioso para a internet. Acabei de publicar o pacote malicioso no npm usando npm install browser-redirect@1.0.2. No entanto, não é possível ver o código malicioso no GitHub — confira a branch Master e a versão 1.0.2. Isso acontece porque o npm não verifica o conteúdo no GitHub nem em outros repositórios de controle de versão.
Por enquanto, as chances de espalhar esse backdoor pelo ecossistema são muito baixas, pois o pacote malicioso precisa ser divulgado e instalado por outras pessoas.
Outra forma de espalhá-lo é incluir o módulo malicioso como dependência de outros pacotes. Se o invasor tiver permissão para publicar outros pacotes importantes, poderá lançar uma nova versão que inclua diretamente uma dependência do pacote malicioso (confira a análise posterior do event-stream). Como alternativa, pode tentar enviar uma solicitação de pull para um projeto, incluindo recursos maliciosos no arquivo de lock, conforme descrito na pesquisa sobre segurança de arquivos de lock e dependências no package-lock.
Outro fator importante é saber se os invasores conseguem acessar as credenciais (usuário e senha) de algum mantenedor relevante. Se conseguirem, podem publicar facilmente novas versões de pacotes populares, como já aconteceu com o eslint.
Mesmo que o mantenedor use autenticação de dois fatores (2FA) para autenticar e publicar, ainda há riscos. Quando um mantenedor opta por usar CI para publicar novas versões, é preciso desativar a 2FA para publicação. Assim, se o invasor roubar um token npm válido usado pela CI (por exemplo, em logs compartilhados ou acessíveis publicamente, vazamentos de dados etc.), poderá publicar novas versões com código malicioso.
Vale lembrar que o npm lançou uma nova API (em beta privada) que mostra se o pacote foi publicado de um endereço IP do TOR e se a 2FA foi usada.
Além disso, os invasores podem adaptar o payload malicioso para funcionar como script de pré-instalação ou pós-instalação em qualquer pacote npm. Por padrão, os pacotes npm têm um hook de ciclo de vida que permite executar código na sua máquina em diferentes momentos. Por exemplo, o pacote de testes de navegador Puppeteer usa esse hook para instalar o Chromium na máquina host. Ryan Dahl já explicou essas falhas de segurança na JSConf EU 2018. É preciso proteger o Node.js para evitar esse vetor e muitos outros.
Lembre-se:
78% das vulnerabilidades são encontradas em dependências indiretas, o que torna a correção complexa
As vulnerabilidades em bibliotecas de aplicativos aumentaram 88% em dois anos
81% acreditam que os desenvolvedores devem ser responsáveis pela segurança, mas não têm os recursos necessários
Fonte: The State of Open Source Security – 2019Confira nosso novo relatório sobre segurança de código aberto – 2020
Como mitigar esse ataque?
Nem sempre é fácil controlar nossas dependências, mas estas dicas podem ajudar:
Use bibliotecas conhecidas e mantidas ativamente.
Participe da comunidade e ajude os mantenedores contribuindo com código ou apoiando financeiramente os projetos e seus mantenedores.
Use o NQP para ajudar a avaliar novos pacotes para seu projeto.
Use o Snyk para acompanhar novas vulnerabilidades e monitorar seus projetos.
Revise o código das dependências no npm, e não apenas no GitHub, Bitbucket ou em outros sistemas de controle de versão.
Lições aprendidas com incidentes anteriores
Crie hoje mesmo uma conta gratuita na Snyk e teste seus projetos para encontrar backdoors conhecidos.
Comece a jogar Capture the Flag
Aprenda a resolver desafios de Capture the Flag assistindo à gravação sob demanda do nosso workshop virtual introdutório.



