
modelo para desenvolver canais C2 personalizados para o Cobalt Strike usando hooks de IAT aplicados por um carregador refletivo.
Este é um PoC simples e um modelo (template) para desenvolver canais C2 personalizados para o Cobalt Strike usando hooks de IAT aplicados por um carregador reflexivo.
Post do blog: https://codex-7.gitbook.io/codexs-terminal-window/red-team/cobalt-strike/building-custom-c2-channels-by-hooking-wininet
Gif de demonstração do canal TCP

os arquivos customCallback.h, hook.c, hash.h e hook.h são modelos brutos, mas podem ser colocados no Crystal kit para serem usados como estão, como um PoC. Coloque-os na pasta udrl/src, substituindo as cópias existentes. O tcg.h permanece inalterado (do tradecraft garden de Raphael Mudge).
Os exemplos na pasta examples/ podem ser usados como estão, mas são exemplos e considerações operacionais não foram levadas em conta ao escrevê-los. Use com cautela.
Os exemplos atuais são:
Observe que os recursos originais de evasão do Crystal kit, como a implementação do Draugr e o mascaramento de sleep, foram removidos do hook.c para manter este código limpo e portável. Se você quiser manter esses recursos, pode adicioná-los novamente a partir das cópias originais no Crystal kit.
Você deve selecionar a biblioteca http apropriada, winhttp/wininet, como a biblioteca http a ser usada ao gerar o beacon dll.
O perfil malleable com o qual isso foi testado está disponível no repositório — em teoria, (a maioria) dos outros perfis malleable deve funcionar, mas este modelo foi testado usando este perfil (retirado do GraphStrike).
A interface oficial do ExternalC2 é um saco de usar — ela exige o staging de um beacon SMB ATRAVÉS do canal externalc2 e (até a versão 4.10) não suportava a comunicação com um beacon SMB já pronto sem antes fazer staging por meio do agente externalc2. Mesmo no estado atual, ela ainda está presa à arquitetura de:
smb beacon --named pipe--> externalc2 agent --custom channel--> externalc2 handler --> teamserver
Sim, estou ciente de que o UDC2 foi adicionado no Cobalt Strike 4.12. Isto é apenas um experimento divertido para replicar essa capacidade no UDRL.
O conceito é simples: se você pode fazer hook das WinAPIs usadas para o callback, você já tem todos os dados necessários para um callback. Esse método de implementar canais C2 personalizados já foi feito antes, como visto no GraphStrike.
No entanto, a implementação do GraphStrike ainda está bastante ligada ao próprio protocolo HTTP. O objetivo deste repositório é fornecer um modelo fácil de usar, modificar e estender para implementar qualquer canal personalizado, HTTP ou não, com pouca modificação no código ao redor.
Obviamente, este modelo não foi feito para ser usado em seu estado padrão. Portanto, para implementar o canal C2 de sua escolha, você precisa modificar o seguinte:
customCallback função para executar as seguintes ações:
Você pode usar o host e a porta originais da requisição http (que você define a partir do cobalt) por meio dos argumentos:
const char *host
INTERNET_PORT port
e modifique a função handleCallback() no broker.py para fazer o seguinte:
É só isso.
Como exemplo, o PoC na função customCallback() no customCallback.h atualmente faz o seguinte:
O PoC na função handleCallback no broker.py atualmente faz o seguinte:
process_encoded_request(encoded_request)usage: broker.py [-h] --host HOST --port PORT
onde o host e a porta apontam para o listener http no seu teamserver. O broker analisa a requisição http e a envia para o listener real.
Esta é provavelmente a maneira mais "preguiçosa" de fazer isso, mas também uma das mais estáveis. Basicamente, ela apenas empacota toda a requisição HTTP e a envia para o broker da maneira que você quiser, que então a envia para o teamserver de verdade, para executar como um beacon HTTP, totalmente transparente para o teamserver.
Há uma implementação (tecnicamente) mais limpa que tentei, que envolvia usar um perfil Malleable C2 mínimo (na verdade, usei o do graphstrike) e analisar os diferentes componentes de dados do beacon de acordo com a especificação malleable dentro dos próprios hooks do wininet (id, metadata, output etc). Isso teria deixado os tamanhos dos blobs de callback ligeiramente menores, mas essa implementação foi abandonada porque o código ficou desnecessariamente bagunçado devido ao parsing da requisição e também exigia o uso daquele perfil específico (o que não é grande coisa, mas é bem hacky).
Eu senti que esta implementação era melhor por depender menos do perfil malleable hacky (tecnicamente, ela ainda exige que a saída da resposta do beacon seja enviada no corpo da resposta, mas esse é o único requisito) e o código era 10x mais fácil de ler.
Também vale notar que não há ofuscação ou criptografia no json da requisição http além da codificação base64 — você é livre para adicionar a sua própria, mas como os callbacks do Beacon já são criptografados, tecnicamente nenhum dado corre risco de ser descriptografado. O pior que pode acontecer é que o blob base64, se for recuperado, possa ser identificado como uma requisição HTTP. Faça o que quiser com essa informação.
Sim, usei um LLM para escrever parte do código e dos comentários. Ninguém gosta de escrever documentação ou de escrever parsing de json boilerplate em C do zero. Pode me processar.
Aviso usual: não sou responsável por nenhum crime contra a humanidade que você possa cometer ou por qualquer guerra nuclear que você possa causar usando este pedaço de código mal escrito.