Building My Own C2 - Part 0 - Goals and Architecture
Table of Contents
A surprise to nobody, making your own C2 is a lot of work. This post is about the goals and overall architecture of my own lightweight C2 framework, the Diet-C2.
Goals#
The main purpose of making my own C2 framework was to have a platform to learn redteam techniques in-depth by implementing them myself. For example, learning about reflective DLL injection and then implementing it myself as new functionality in the Diet-C2.
Along the way I hoped to get a better idea of how to use WinAPI, as well as how to build an easily scaleable (in the way of adding more commands / functionality) C2 framework built on a solid foundation. It’s very much a learning tool and not one I tried to make with the intention of actually being useful in real operations right away, but rather something to build on and learn with until it’s “ready”.
Source Code#
The source code can be found on my github, updated regularly.
Languages and Structure#
Getting into the structure of Diet-C2, there are 3 parts: the operator client with a terminal user interface, a server that takes in the client commands and serves them up to the implant which processes them and replies with some output.
A brief description of some the larger libraries / technologies used for each part of the Diet-C2.
Server
- Written in python
- Uses Flask to serve an HTTPS server
Implant
- Written in C++
- Uses WinAPI and WinHTTP to reach out and execute commands from the server
Client
- Written in python
- Uses Textual for the terminal user interface
- Uses requests library to interact with the server
- Also hosts a small threaded Flask server to receive updates from the server
The listener is based on HTTPS, and thats how commands get transmitted to the implant. I wanted to support multiple clients, as well as multiple implants - so I built out the foundation of the framework with that in mind. I thought about supporting multiple listeners on the same server, but for the time being since there is just the one HTTPS type listener, I am putting it off until I write a different listener.
Big Picture Plan for the Diet-C2#
I decided to break down the development of Diet-C2 into parts to simplify implementation and maintain consistent progress.
I tried to allow for some flexibility to be able to work on different aspects of the C2 without feeling obligated to complete a feature entirely before moving on, letting me switch between tasks and revisit them later while staying on track with the overall development progress. The milestones are described below.
- Designing the Architecture - Making a plan for how a command will be sent from the C2 to the server to the implant, and how output will be returned
- Foundational Features - Coding up the UI, command processing and overall file structure in such a way that it is easy to add a new command or feature
- New Commands - Implementing different commands used by other C2s and anything new I can think of
- AV Avoidance - Using encryption, WinAPI call obfuscation and other methods to avoid getting flagged by selected AntiVirus solutions
- Documentation - Self explanatory, but very difficult to write good docs, will be done throughout the development (including these posts)
- Obfuscation - Make the implant hard to detect when looking for it, having believable communications with the server for example
- New Server / Implant Modules - New type of listener for example, a staged implant payload, or an implant written in powershell and so on
- Operationalize and Polish - Proper error logging etc.
As of writing this post I have just finished the architecture and foundational features completely, also touching on some AV avoidance, obfuscation and new commands along the way for fun.
I’ll now get into the first milestone, planning out the architecture of Diet-C2.
Milestone 0: Designing the Architecture#
My goal for this milestone was pretty simple: be able to fully describe how I was going to send a command from the client, to the server and on to the implant, and how the output of the command was going to come back to the client.
I used the client, server and implant starting up as the begginning of my plan.
On startup, I wanted the implant to reach out to the server to login. I also wanted the server to update any connected clients that a new implant has connected. I wrote out my ideas using excalidraw below
This doesn’t count as a implant command though, this is just a pre-requisite for being able to process and send a command from the client to the server and then to the implant.
As everyone knows though, trying to design a something perfect off rip without implementing anything only leads to more work as you inevitably end up rewriting the component when you run into a fundamental problem with your design. To avoid this, I created these diagrams as I went.
This is one example out of 10 or so diagrams I made while trying to plan out how everything would work together to send and process commands on the implant. Instead of pasting all of them here, I’ll summarize it hopefully a little simpler in the following sections below.
URL Endpoints and Requests#
Here I’ll describe the what each endpoint is used for on the server, for who it is for, and how it works. Then in the next section, using the endpoints described below, Ill outline the general structure of how the whoami
command is sent to the server, how the implant gets the command, and how the response arrives back to the client.
This is not an exhaustive list of endpoints, but these are all that are required to just send a simple shell command.
Endpoints for implants
/login
- Receives implant login requests, and saves implant data in implant database
- Sends an update to the currently connected operators that a new implant connected
- Replies to the implant with a generated ID, unique to each implant
/recipes
- Where the implant goes to check if there is a command in it’s queue
- If there is a command, the server sends it back as a response
/comment
- Where the implant goes to POST a commands output
- The command output is forwarded along to the operator that sent the command
Endpoints for operators
/admin/login
- Where the server processes operator client logins, and saves them to the operator database
- It responds with a copy of the implant database in json to sync up with the client on login
/admin/management
- Where the operator sends commands
- Commands are processed, encrypted, and stored in the associated implant’s command queue for retrieval by the implant
/admin/update/implants
- Where the operator goes to get a live sync of the implant database
- The server responds with a copy of the implant database in json
There is additionally a Flask server running on the client with one endpoint, /update
, created to receive updates from the server, such as a new implant, or a response to a command to update the client UI and client database copies accordingly.
Simple Command Flow#
Starting from the startup of the server, implant and client, a whoami
command gets executed on the implant as follows.
For reference, shell
specifies a cmd.exe command, executing everything following shell
on the implant using cmd.exe on the implant, and then returns the output back to the client. For this example, the appropriate diet-client command would be shell whoami
. The storage methods are also abstracted away, for simplicity.
The simplified flow is as follows
- Server is started
- Client logs in to the server at
/admin/login
- Implant logs in to the server at
/login
- Server gets the request, adds the implant to the implant database
- Server updates all connected clients by sending a copy of the implant data to their
/update
endpoints - Server responds to the implant with a generated unique implant ID
- Implant starts the infinite loop of checking
/recipes
on the server for a command every X seconds
- Client receives the new implant update, and selects the implant they would like to send commands to with
select IMPLANT_ID
- The command reaches out to
/admin/update/implants
to sync the implant databases between the client and server to make sure the implant exists - If the provided
IMPLANT_ID
is valid, selects the implant
- The command reaches out to
- Client then inputs the command
shell whoami
- A command ID is created, unique to this specific command
- A command string is created, and consists of
- Command ID
- Command type
- Command parameters
- The command string is ::: delimited, and looks like this
ID_UNIQUECMD:::CMD_SHELL:::whoami
- Command string and what implant the command is for is sent to the server at
/admin/management
- The server processes the command string, and encrypts it using AES
- Then adds the command string to the command queue of the associated implant
- Implant reaches out to
/recipes
to find a command, this time it finds one- The server looks for a command in the implants queue, and finds one, popping it off the queue and returns it to the implant
- The implant decrypts the command, and identifies it is as a shell command
- The implant runs the
whoami
command, and captures the output - The implant then sends the command ID and the output of the command back to the server at
/comment
- For example, if the output was
win10_pc\USER
, the POST request to the server would beID_UNIQUECMD:::win10_pc\USER
- The server forwards the message along to the client that made the command to their
/update
endpoint - The server responds with a success message to the implant
- The implant goes back to querying
/recipes
for new commands every X seconds
- For example, if the output was
- The client recieves the response to it’s command on
/update
, and prints the output of the command to the UI
I hope that made some sense.
Conclusion#
Hopefully the structure of my framework is easy to understand, it is a fairly common implementation but the details are different of course. This article is a bit of a confusing read so I will update it in the future if I figure out a better way to describe my planning process.
As a sneak-peek here is a screenshot of what the UI looks like on the client side with one implant connected, as well as how a shell command looks.
If you want to see the source code and my progress, you can find it on my github here
The next blog post won’t walk through all the code, but just some things I learned the way, and some (in my opinion) interesting design decisions I made for the implant, server and client as I was building foundational features of the Diet-C2.
Thanks for reading.