2025-12-03

Evasive C2: Why and How

C2Malware DevArchitecture

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:

  1. The Teamserver: The brain of the operation. It manages listeners, stores data, and handles operator connections.
  2. The Client (Operator Console): The interface for the red teamer. It connects to the teamserver to issue commands.
  3. 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.

  1. Sleep: The agent sleeps for Interval + Jitter seconds. Jitter is crucial to avoid a predictable pattern that network analysts can spot.
  2. Check-In: The agent sends a POST request (or DNS query) to the server.
    • Payload: Encrypted system info + any pending task output.
  3. 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.

  1. Hello World: Write a C++ program that makes an HTTP request to a Python Flask server.
  2. Command Execution: Add a function to run a system command (e.g., popen) and send the output back.
  3. Loop: Put it in a while(true) loop with a Sleep().

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.