
C2Bridges permitem que desenvolvedores criem novos protocolos de comunicação personalizados e os utilizem rapidamente dentro do Covenant.
As C2Bridges permitem que desenvolvedores criem novos protocolos de comunicação personalizados e os utilizem rapidamente no Covenant.
As C2Bridges são usadas para desenvolver um protocolo de comando e controle de saída sem editar nenhum código do Covenant. Para desenvolvedores que se sentem confortáveis em integrar novos listeners, um novo protocolo C2 deve ser adicionado como um novo tipo de listener de primeira classe que é totalmente integrado à interface. No entanto, pode ser mais rápido em algumas situações criar uma C2Bridge fora do Covenant e conectá-la com um BridgeListener para provas de conceito ou testar novos protocolos.
Os desenvolvedores podem usar o projeto C2Bridge como modelo para criar novas C2Bridges. Dentro do projeto C2Bridge, existe uma classe abstrata C2Bridge. Um desenvolvedor pode herdar dessa classe e implementar as funções necessárias para ler e escrever de implants para o BridgeListener usando o novo protocolo C2 escolhido.
using System.Threading;
using System.Threading.Tasks;
namespace C2Bridge
{
/// <summary>
/// IC2Bridge is an interface implemented by the C2Bridge class.
/// </summary>
public interface IC2Bridge
{
Task RunAsync(CancellationToken token);
}
/// <summary>
/// C2Bridge is an abstract class that new C2Bridges should inherit from.
/// </summary>
public abstract class C2Bridge : IC2Bridge
{
// The BridgeConnector handles communication between the Covenant server and the C2Bridge
protected BridgeConnector BridgeConnector { get; set; }
// The BridgeProfile handles parsing and formatting data passed between the implant and Covenant
protected BridgeProfile BridgeProfile { get; set; }
/// <summary>
/// The constructor for the C2Bridge. New C2Bridges should use their own constructor that accepts
/// any command line arguments needed for the C2Bridge to function.
/// </summary>
/// <param name="Connector">The BridgeConnector that handles communication with the Covenant server.</param>
/// <param name="Profile">The BridgeProfile that handles the parsing and formatting of data.</param>
protected C2Bridge(BridgeConnector Connector, BridgeProfile Profile)
{
this.BridgeConnector = Connector;
this.BridgeProfile = Profile;
BridgeConnector.OnReadBridge += OnReadBridge;
}
/// <summary>
/// The RunAsync function is the main function that should start the C2Bridge and continue to run until you
/// are done with your operation. C2Bridge developers should implement the logic to start and run the listener
/// within this function.
/// </summary>
/// <param name="Token">The CancellationToken that will cancel the C2Bridge if the source of the token is cancelled.</param>
/// <returns></returns>
public abstract Task RunAsync(CancellationToken Token);
/// <summary>
/// The OnReadBridge function is called each time data is read from the Covenant server meant for an implant.
/// C2Bridge developers should implement the logic to determine which implant this data is meant for and write
/// this data to the implant.
/// </summary>
/// <param name="sender">
/// Sender is the object that called the OnReadBridge function. C2Bridge developers can safely ignore this parameter.
/// </param>
/// <param name="args">Args contains the data that should be written from the Covenant server to the implant.</param>
protected abstract void OnReadBridge(object sender, BridgeConnector.ReadBridgeArgs args);
/// <summary>
/// The WriteToConnector function handles writing data from an implant to the Covenant server. This logic should be the
/// same for all C2Bridge types, but can be overloaded by the C2Bridge developer if custom logic is needed.
///
/// When calling this function, the returned GUID string should be used to track implant GUID values by the C2Bridge.
/// </summary>
/// <param name="Data">The data read from the implant that should be written to the Covenant server.</param>
/// <returns>
/// Returns the GUID value parsed out of the Data. This value should be used to track implant GUID values by the C2Bridge.
/// </returns>
protected virtual string WriteToConnector(string Data)
{
var parsed = this.BridgeProfile.ParseWrite(Data);
if (parsed != null)
{
_ = this.BridgeConnector.Write(this.BridgeProfile.FormatRead(parsed));
return parsed.Guid;
}
return null;
}
/// <summary>
/// The GetBridgeMessengerCode function should contain the code to be embedded in the implant for communication with
/// the C2Bridge. This function is not actually used anywhere within the project, but is here so that the necessary
/// implant code can be found along with the C2Bridge. C2Bridge developers should place the code here for use within
/// a BridgeProfile's BridgeMessengerCode property.
/// </summary>
/// <returns></returns>
protected abstract string GetBridgeMessengerCode();
}
}
O projeto C2Bridge contém uma classe de exemplo TCPC2Bridge que herda dessa interface e fornece um exemplo de como implementar uma.

Depois de escrever sua nova C2Bridge, a chamada do construtor para TCPC2Bridge dentro da função Main() pode ser substituída pelo novo construtor:

O método abstrato GetBridgeMessengerCode() não é realmente usado em nenhum lugar dentro do projeto C2Bridge, mas é usado para unir uma C2Bridge a um implant. Um implant precisa de código que seja capaz de ler e escrever para a C2Bridge de saída. Esse código é específico para uma determinada C2Bridge e deve ser colocado dentro do método herdado GetBridgeMessengerCode(). Um usuário do Covenant que utiliza uma C2Bridge pegará o BridgeMessengerCode deste método e o usará dentro de um BridgeProfile.
Usuários do Covenant que utilizam uma C2Bridge precisarão configurar um BridgeProfile que seja específico para a C2Bridge. Os implants Grunt precisam saber como ler e escrever para a C2Bridge de saída. A propriedade BridgeProfile.BridgeMessengerCode representa o código que será colocado no implant e é responsável por ler e escrever para a C2Bridge de saída. Esse código deve ser encontrado no método GetBridgeMessengerCode() de uma C2Bridge.
Os usuários podem criar um BridgeProfile totalmente novo ou editar o DefaultBridgeProfile com o BridgeMessengerCode correto. Para fazer isso, navegue até a página de navegação de listeners e selecione a aba "Profiles":

Para criar um novo perfil, clique no botão "Create". Para editar um perfil específico, clique no nome do perfil. Lembre-se de que você não pode editar perfis associados a listeners ativos.
Após clicar em "Create", selecione a aba "BridgeProfile":

As seguintes opções precisarão ser configuradas ao editar ou criar um perfil:
Nome do perfil que será usado em toda a interface. Escolha algo reconhecível!Descrição do perfil. Deve ser uma descrição detalhada do perfil que os operadores possam ler e entender facilmente como o perfil funciona e os casos de uso para os quais seria apropriado usar o perfil.MessageTransform é uma maneira única de especificar como os dados de comunicação serão transformados antes de serem colocados nos formatos especificados no ReadFormat e WriteFormat. Um MessageTransform deve ser uma classe C# estática chamada MessageTransform que inclui uma função pública Transform e uma função pública Invert. A classe pode transformar os dados da maneira que desejar, desde que as funções Transform e Invert sejam espelhadas (ou seja, data == MessageTransform.Invert(MessageTransform.Transform(data))). A classe MessageTransform deve ser compatível com várias plataformas e compilar em , e .Ao configurar essas opções, o usuário do Covenant tem total liberdade para configurar qualquer um desses valores da maneira que desejar, exceto pela propriedade BridgeMessengerCode. A propriedade BridgeMessengerCode deve ser obtida da C2Bridge.
Se um usuário do Covenant editar as propriedades ReadFormat e/ou WriteFormat, a C2Bridge deve ser informada dessa alteração ao iniciar a C2Bridge. O projeto C2Bridge aceita um parâmetro --profile <profile.yaml> que aceita um arquivo YAML de perfil, que pode ser opcionalmente usado quando essas propriedades são personalizadas.
O processo geral para desenvolver e utilizar uma C2Bridge é o seguinte:
C2Bridge do projeto C2Bridge. Consulte a classe TCPC2Bridge como exemplo.BridgeProfile que use o BridgeMessengerCode encontrado no método GetBridgeMessengerCode() da C2Bridge.BridgeProfile criado.ReadFormat e/ou WriteFormat do BridgeProfile, use o parâmetro opcional --profile <profile.yaml> da CLI para informar a C2Bridge dessas personalizações.GruntBridge ImplantTemplate e o BridgeListener que você iniciou.Net40Net35NetCore21ReadFormat é o formato de uma mensagem quando um Grunt lê dados de uma C2Bridge. O formato deve incluir um local para os dados e o GUID do Grunt serem colocados. Inclua a string "{DATA}" para indicar o local onde os dados devem ser colocados e a string "{GUID}" para indicar o local onde o GUID deve ser colocado.WriteFormat é o formato de uma mensagem quando um Grunt grava dados em uma C2Bridge. O formato deve incluir um local para os dados e o GUID do Grunt serem colocados. Inclua a string "{DATA}" para indicar o local onde os dados devem ser colocados e a string "{GUID}" para indicar o local onde o GUID deve ser colocado.BridgeMessengerCode é o código que será colocado no implant e é responsável por ler e gravar na C2Bridge de saída. Esse código deve ser encontrado no método GetBridgeMessengerCode() de uma C2Bridge.