Why Reinvent the Wheel?
In the world of Red Teaming, "living off the land" is a mantra we all know. But sometimes, you need to bring your own tools to the party. While frameworks like Cobalt Strike, Sliver, and Mythic are powerful, they are also well-known signatures to EDRs. Building a custom Command and Control (C2) framework isn't just about having a tool that bypasses AV; it's about understanding the why and how of malware development.
This series will chronicle the journey of building C24U, a production-grade C2 framework, from a blank text editor to a fully weaponized platform. We'll dive into the architecture, the protocols, the evasion techniques, and the offensive capabilities.
The Blueprint: High-Level Architecture
Before writing a single line of code, I needed a plan. A C2 framework typically consists of three main components:
- The Teamserver: The brain of the operation. It manages listeners, stores data, and handles operator connections.
- The Client (Operator Console): The interface for the red teamer. It connects to the teamserver to issue commands.
- The Agent (Implant): The payload running on the target machine.
graph TD
subgraph "Operator"
Client[Client Terminal]
end
subgraph "Infrastructure"
Server[Python Teamserver]
DB[(SQLite Database)]
end
subgraph "Target Network"
Agent[C++ Agent]
end
Client <-->|HTTP API| Server
Server <-->|SQL| DB
Server <-->|Encrypted C2 Proto| Agent
The Tech Stack
For the Teamserver, I chose Python 3. It's rapid to develop, has excellent libraries for networking (Flask/Waitress) and cryptography, and runs on any Linux VPS.
For the Agent, the choice was clear: C++17.
- Performance: Low overhead and minimal dependencies.
- Control: Direct access to Windows APIs and memory.
- Stealth: Easier to obfuscate and manipulate than managed languages like C# or Go (which leave large artifacts).
For Persistence, I went with SQLite. It's lightweight, serverless, and perfect for handling the relational data of agents, tasks, and listeners without the overhead of PostgreSQL.
The Protocol: Designing the Language
How does the agent talk to the server? I wanted a polymorphic design. The core logic of the agent shouldn't care how the data gets there, only that it does.
I implemented a Transport interface in C++:
class Transport {
public:
virtual bool Connect() = 0;
virtual bool Send(const std::string& data, std::string& response) = 0;
virtual void Close() = 0;
};
This allows us to swap out communication channels easily. We started with HTTP/HTTPS because it blends in with normal web traffic. Later, we added DNS (for restricted networks) and SMB (for peer-to-peer pivoting).
The Heartbeat: Check-In Logic
The agent doesn't maintain a persistent connection (except for SMB). Instead, it "beacons" home at a set interval.
- Sleep: The agent sleeps for
Interval + Jitterseconds. Jitter is crucial to avoid a predictable pattern that network analysts can spot. - Check-In: The agent sends a POST request (or DNS query) to the server.
- Payload: Encrypted system info + any pending task output.
- Tasking: The server responds with a new task (e.g.,
shell whoami) or an empty "sleep" command.
Building Your Own
If you're following along and want to build your own, start small.
- Hello World: Write a C++ program that makes an HTTP request to a Python Flask server.
- Command Execution: Add a function to run a system command (e.g.,
popen) and send the output back. - Loop: Put it in a
while(true)loop with aSleep().
That's a C2 in its simplest form. Everything else-encryption, evasion, lateral movement-is just layering complexity on top of this foundation.
In the next post, we'll dive deep into the Communication Layer, implementing secure, encrypted channels and hiding our traffic in plain sight.