
API Python para uso com a especificação External C2 do Cobalt Strike
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, mas está sujeito a mudanças conforme o projeto amadurece.
Um crédito enorme vai para 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
Este projeto consiste nas seguintes partes principais:
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 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:
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:
#################
# 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 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:
framework é responsável por garantir que o encoder esteja disponível para o transport ser usado.framework usa uma relação cliente-servidor, eles devem ser organizados adequadamente como tal.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.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.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.server de deve poder ser interfaceado por um comum.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:
encodertransporttransportencodertransporttransportencoderO cliente é essencialmente o payload que é executado no endpoint. A lógica do cliente para o framework cobalt_strike é principalmente estática, e mostrada abaixo:
transporttransporttransport em busca de novas tarefastransportO cliente faz uso do encoder e do transport especificados para retransmitir dados entre si e o seu respectivo server.
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 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.
Primeiro, determine qual módulo de transporte e codificação você gostaria de usar. Usaremos transport_imgur e encoder_lsbjpg no exemplo a seguir.
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.
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:
python build_files.py -b builds -f cobalt_strike -c sample_builder_config.config.sample -e encoder_lsbjpg -t transport_imgur -v
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.
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 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:
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
frameworkframework_manager