
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 framework deve poder ser interfaceado por um framework_manager 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.