
API Python per l'uso con la specifica External C2 di Cobalt Strike.
Framework Python per costruire e utilizzare interfacce per trasferire dati tra framework, con un focus specifico sul fungere da estensione per framework di Command and Control.
Attualmente, questo è pensato solo come un'implementazione della specifica External C2 di Cobalt Strike come descritto in questo documento di specifica, ma è soggetto a modifiche man mano che il progetto matura.
Un immenso ringraziamento va a xychix. Questo progetto non sarebbe stato affatto possibile senza i loro preziosi contributi. In pratica, questo progetto è una ricostruzione ed estensione del progetto External C2 di Outflank
Questo progetto è composto dalle seguenti parti principali:
Il builder legge un file di configurazione e usa le opzioni configurate per generare una build sostituendo i marker all'interno degli skeleton.
Il builder può essere usato con build_files.py. Una configurazione di esempio del builder è fornita come sample_builder_config.config.sample.
Gli skeleton sono i diversi "scheletri" di codice che il builder popola dinamicamente per generare una build completamente utilizzabile. Gli skeleton contengono marker che verranno sostituiti con valori utilizzabili dal builder. Ci sono tre diversi 'tipi' di skeleton:
Un marker può essere inserito in qualsiasi file all'interno di uno skeleton e verrà sostituito con un valore specificato nella configurazione del builder. Come buona pratica, i marker non dovrebbero mai essere usati per scrivere direttamente variabili, ma dovrebbero essere usati solo per impostare valori. Se il valore di un marker deve essere riutilizzato, si dovrebbe optare per memorizzare il valore in una variabile e fare riferimento ad essa, invece di riutilizzare lo stesso marker.
Il formato del marker è: ```[var:::identifier_for_the_marker]```
Le stringhe verranno scritte in uno skeleton direttamente racchiuse tra apici singoli, mentre i numeri verranno scritti così come sono.
Nel caso in cui una stringa nella configurazione sia racchiusa tra doppi apici, la stringa verrà scritta direttamente nel file racchiusa tra doppi apici, e gli apici singoli esterni verranno rimossi.
Questa relazione può essere dimostrata come segue:
#################
# 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"
I framework sono l'applicazione di base che determina quali dati vengono usati da transport ed encoder e come questi dati vengono usati. Ciò che un determinato framework fa effettivamente non ha molta importanza, purché esista una logica per importare e usare encoder e transport. La maggior parte delle parti essenziali di un framework (principalmente la logica del client) verrà salvata come skeleton, con un'interfaccia per interagire con la parte server salvata come oggetto framework di base.
Generalmente, un framework contiene un server e un client e usa encoder e transport per inoltrare dati tra loro.
Ci sono alcuni aspetti fondamentali da considerare quando si costruisce un framework:
framework è responsabile di garantire che l'encoder sia reso disponibile al transport per essere utilizzato.framework usa una relazione client-server, dovrebbero essere organizzati di conseguenza in modo appropriato.client di un framework, quindi se vuoi che le cose siano riconfigurabili sul client, questo deve essere in grado di farlo durante l'esecuzione senza alcuna interazione diretta.skeleton del server perché l'utente finale interagirà direttamente con il server di un framework. Invece, scegli sia di leggere le opzioni da una configurazione, sia di dare all'utente finale la possibilità di modificare le opzioni (come un timer di blocco o la verbosità) durante l'esecuzione.skeleton di un framework verrà elaborato dal builder, iterando attraverso ogni file al suo interno, quindi se un determinato argomento deve essere configurabile al momento della build, può essere fatto facilmente.server di un framework dovrebbe poter essere interfacciato da un comune framework_manager.Il server è l'applicazione che fa da intermediario nella comunicazione tra il client e il c2 server. La logica del server è principalmente statica. La logica del server per il framework cobalt_strike, indicato come third-party Client Controller nella specifica, è mostrata di seguito:
encodertransporttransportencodertransporttransportencoderUn server dovrebbe supportare la capacità di gestire più client (non ancora implementata) ed essere interfacciato da un framework_manager.
Il client è essenzialmente il payload che viene eseguito sull'endpoint. La logica del client per il framework cobalt_strike è principalmente statica ed è mostrata di seguito:
transporttransporttransport per nuovi tasktransportIl client utilizza l'encoder e il transport specificati per inoltrare dati tra sé e il rispettivo server.
Gli encoder ricevono dati e poi li modificano per prepararli all'invio tramite il transport, oppure decodificano i dati ricevuti tramite il transport riportandoli alla loro forma grezza per essere interpretati da qualsiasi componente del framework li stia utilizzando.
Gli encoder dovrebbero aspettarsi di essere interfacciati direttamente dal transport e gestire i dati in modo indipendente da framework e componente.
I transport hanno il ruolo di inviare e ricevere dati attraverso un canale di comunicazione e di interfacciarsi con l'encoder per garantire che i dati vengano trasformati nel formato necessario. I transport dovrebbero aspettarsi di ricevere dati da un componente del framework o tramite il canale di comunicazione e avere la capacità di inoltrare dati attraverso il canale di comunicazione. I transport sono responsabili di chiamare l'encoder per codificare o decodificare i dati quando necessario.
I transport dovrebbero aspettarsi di essere interfacciati direttamente dal componente framework e gestire i dati in modo indipendente da framework e componente.