
API Python para uso com a especificação External C2 do Cobalt Strike
# external_c2 framework Framework em Python para construir e utilizar interfaces para transferir dados entre frameworks, com um foco específico em servir como uma extensão para frameworks de Command and Control. Atualmente, isto é apenas uma implementação da especificação External C2 do Cobalt Strike, conforme descrito em [este documento de especificação](https://www.cobaltstrike.com/downloads/externalc2spec.pdf), mas está sujeito a mudanças conforme o projeto amadurece. ## Créditos Um crédito enorme vai para [xychix](https://twitter.com/xychix). Este projeto não teria sido possível sem as suas valiosas contribuições. Basicamente, este projeto é uma reconstrução e extensão do [projeto External C2 da Outflank](https://github.com/outflanknl/external_c2) # Arquitetura Este projeto consiste nas seguintes partes principais: - Builder - Skeletons - Frameworks - Transports - Encoders - Manager (ainda não implementado) ## Builder O builder lê um arquivo de configuração e usa as opções configuradas para gerar um build substituindo `markers` dentro de `skeletons`. O builder pode ser usado com `build_files.py`. Uma configuração de exemplo do builder é fornecida como `sample_builder_config.config.sample`. ## Skeletons `skeletons` são os diferentes "esqueletos" de código que o `builder` vai popular dinamicamente para gerar um build completamente utilizável. `skeletons` contêm `markers` que serão substituídos por valores utilizáveis pelo `builder`. Existem três 'tipos' diferentes de skeletons: ### Skeleton Markers Um marker pode ser colocado dentro de qualquer arquivo em um `skeleton` e será substituído por um valor especificado na configuração do builder. Na prática recomendada, **markers nunca devem ser usados para escrever variáveis diretamente, e só devem ser usados para definir valores**. Se o valor de um marker precisar ser reutilizado, deve-se optar por armazenar o valor em uma variável e referenciá-lo dessa forma, em vez de reutilizar o mesmo marker. O formato do marker é: ` ```[var:::identifier_for_the_marker]``` ` Strings serão escritas em um skeleton diretamente entre aspas simples, e números serão escritos como estão. **No caso de uma string na configuração estar entre aspas duplas**, a string será escrita diretamente no arquivo entre aspas duplas, e as aspas simples que a envolvem serão removidas. Essa relação pode ser demonstrada como: ```python ################# # Skeleton Code # ################# # Skeleton contains the following line of code: foo = ```[var:::bar]``` ############## # End Result # ############## # Stored in config as: # foo = bar # Written as: foo = 'bar' # Stored in config as: # foo = "bar" # Written as: foo = "bar" # Stored in config as: # foo = 2 # Written as: foo = 2 # Stored in config as: # foo = "2" # Written as: foo = "2" ``` ## Frameworks Frameworks são a aplicação base que determina quais dados estão sendo usados pelo `transport` e pelo `encoder`, e como esses dados são usados. O que um `framework` específico realmente faz não importa, desde que exista lógica para importar e usar o `encoder` e o `transport`. A maior parte das porções essenciais de um framework (principalmente a lógica do `client`) será armazenada como um `skeleton`, com uma interface para interagir com a parte do servidor, armazenada como um objeto base de `framework`. Geralmente, um framework contém um `server` e um `client`, e faz uso de `encoders` e `transports` para retransmitir dados entre eles. Há alguns fundamentos a considerar ao construir um `framework`: * O `framework` é responsável por garantir que o `encoder` esteja disponível para o `transport` ser usado. * Se o `framework` usa uma relação cliente-servidor, eles devem ser organizados adequadamente como tal. * Entenda que, na maioria dos casos, o usuário final nunca interagirá diretamente com o `client` de um framework; portanto, se você quiser que as coisas sejam reconfiguráveis no `client`, ele precisa ser capaz de fazer isso durante a execução, sem interação direta. * Deve haver pouca necessidade de criar um `skeleton` de `server`, porque o usuário final vai interagir diretamente com o `server` de um framework. Em vez disso, opte por ler opções de uma configuração e dar ao usuário final a capacidade de modificar opções (como um temporizador de bloco ou verbosidade) durante a execução. * Um skeleton de `framework` será processado pelo builder, iterando por todos os arquivos dele; portanto, se um determinado argumento precisar ser configurável no momento do build, isso pode ser feito facilmente. * Um `server` de `framework` deve poder ser interfaceado por um `framework_manager` comum. ### Servidor do Framework O servidor é a aplicação que intermedia a comunicação entre o `client` e o servidor C2. A lógica do servidor é principalmente estática. A lógica do servidor para o framework `cobalt_strike`, referido como `third-party Client Controller` na especificação, é mostrada abaixo: 1. Fazer o parse da configuração 2. Importar o módulo de codificação especificado 3. Importar o módulo de transporte especificado 4. Estabelecer uma conexão com o servidor C2 5. Solicitar um stager do servidor C2 6. Codificar o stager com o módulo `encoder` 7. Transportar o stager com o módulo `transport` 8. Aguardar uma resposta de metadados do client recebida via `transport` 9. Decodificar os metadados com o módulo `encoder` 10. Retransmitir os metadados para o servidor C2. 11. Receber uma nova tarefa do servidor C2. 12. Codificar a nova tarefa 13. Retransmitir a nova tarefa para o client via `transport` 14. Receber uma resposta do client recebida via `transport` 15. Decodificar a resposta através do módulo `encoder` 16. Retransmitir a resposta para o servidor C2. 17. Repetir os passos 11-16 ### Cliente do Framework O cliente é essencialmente o payload que é executado no endpoint. A lógica do cliente para o framework `cobalt_strike` é principalmente estática, e mostrada abaixo: 1. Executar quaisquer preparações necessárias para utilizar o `transport` 2. Receber o stager 3. Injetar o stager e abrir o handle para o beacon 4. Obter metadados do beacon 5. Retransmitir os metadados do beacon para o servidor C2 via `transport` 6. Observar o `transport` em busca de novas tarefas 7. Retransmitir novas tarefas para o beacon 8. Retransmitir respostas do beacon via `transport` 9. Repetir os passos 6-8. O cliente faz uso do `encoder` e do `transport` especificados para retransmitir dados entre si e o seu respectivo `server`. ## Encoders Encoders recebem dados e os modificam para prepará-los para serem enviados via transporte, ou decodificam dados recebidos via transporte de volta à sua forma bruta, para serem interpretados por qualquer componente do `framework` que esteja utilizando esses dados. Encoders devem esperar ser interfaceados diretamente pelo transport e lidar com dados de uma maneira agnóstica em relação a framework e componente. ## Transports Transports têm o papel de enviar e receber dados por meio de um canal de comunicação e de interfacear com o encoder para garantir que os dados sejam transformados no formato necessário. Transports devem esperar receber dados de um componente do framework, ou por meio do canal de comunicação, e ter a capacidade de retransmitir dados através do canal de comunicação. **Transports são responsáveis por chamar o `encoder` para codificar ou decodificar dados conforme necessário**. Transports devem esperar ser interfaceados diretamente pelo componente do `framework` e lidar com dados de uma maneira agnóstica em relação a framework e componente. # Como usar 1. Primeiro, determine qual módulo de transporte e codificação você gostaria de usar. Usaremos `transport_imgur` e `encoder_lsbjpg` no exemplo a seguir. 2. Em seguida, crie um `builder_config.config` adequado às suas necessidades; consulte a configuração de exemplo fornecida e o template para orientação sobre como fazer isso. 3. Gere um build com `build_files.py`. Como exemplo, pode-se gerar um build no diretório `builds` usando `encoder_lsbjpg` e `transport_imgur` para Cobalt Strike, de forma verbosa, com o seguinte comando: ```bash python build_files.py -b builds -f cobalt_strike -c sample_builder_config.config.sample -e encoder_lsbjpg -t transport_imgur -v ``` 4. Em seguida, comece a executar o servidor gerado e distribua o seu cliente. ### Cobalt Strike Na máquina que executa o servidor, execute: `python server.py` Para uma saída mais verbosa, você pode executar: `python server.py -v` Para uma saída mais verbosa e com saída adicional útil para depuração, você pode executar: `python server.py -d` Em seguida, execute o cliente no endpoint alvo. Se tudo funcionou, um novo beacon será registrado no console do Cobalt Strike, com o qual você poderá interagir. # FAQ **Por que você escreveria isto?**: Não havia muitas implementações publicadas da especificação do Cobalt Strike e, entre as que foram publicadas, ou não estão em uma linguagem que eu conheço ou não têm a modularidade e abstração que eu procurava. **Por que Python 2?**: Sou preguiçoso e é fácil implementar novos canais de transporte e codificação nele. **Seu código é ruim**: Isso não é uma pergunta. **Posso enviar novos módulos de transporte e/ou codificação?**: Sim, por favor! Envie um pull request e ficarei feliz em revisar. **Como compilo o cliente em um executável que posso distribuir?**: Testei isso com sucesso no Kali. Para recriar meu ambiente, apenas certifique-se de ter o [veil-evasion](https://github.com/Veil-Framework/Veil-Evasion) instalado e de ter concluído a configuração dele. Ele deve ter configurado um ambiente wine com Python instalado que possui todas as dependências de que você precisa. Você PODE precisar instalar o módulo `pefile` nesse ambiente também. Então, você pode ir até o diretório do cliente para o qual deseja gerar um executável e executar: ```bash chmod +x compile_dll.sh ./compile_dll.sh wine "C:\\Python27\\python.exe" /usr/share/veil/pyinstaller/pyinstaller.py -F -r c2file.dll -w --key "ayyyyyyylmao" client.py ``` Substitua o valor de `key` pelo que você quiser. Você deve ver o executável do cliente no diretório `dist/`. Se quiser gerar um executável que forneça um console que você possa usar para depuração, compile o executável com `wine "C:\\Python27\\python.exe" /usr/share/veil/pyinstaller/pyinstaller.py -F -r c2file.dll -c client.py`