RED TEAM
2026-03-15
15 min read

Building a C2 Framework from Scratch

Lessons from building Marrow C2 — a full command-and-control platform with a PHP dashboard, Python agent, AMSI bypass, and anti-analysis evasion. Architecture decisions, detection engineering, and what building offensive infrastructure teaches you about defense.

HT
Hussein Taha
Author

Building a C2 Framework from Scratch

Most security professionals learn C2 by reading about Cobalt Strike or Empire. I learned by building my own from the ground up — a full platform with a PHP web dashboard, a compiled Python agent, a payload binder, and a multi-stage evasion pipeline.

This was built for authorized red team practice and educational purposes in controlled lab environments.

Why Build a C2?

The gap between "I can run Metasploit" and "I understand offensive infrastructure" is enormous. You can study detection rules all day, but until you've built the thing those rules are designed to catch, you're pattern-matching without understanding.

Building Marrow C2 forced me to answer questions that no training course covers:

  • How do you make HTTP beaconing look like normal web traffic?
  • What does a detection engineer see when your agent calls home?
  • How does AMSI actually inspect PowerShell at the API level?
  • What makes a payload "burn" — and how do you avoid it?

Architecture Decisions

A C2 framework has three fundamental components: a controller (dashboard), an agent (runs on target), and a communication channel (how they talk). Each has tradeoffs.

Controller: PHP + MySQL

I chose PHP for the dashboard because I already had production PHP experience from EduAssess. This wasn't a "use what I know" convenience — it was strategic:

  • PHP renders server-side, so the dashboard has zero client-side JavaScript dependencies for core functionality
  • MySQL handles relational data naturally: targets → tasks → results are clean foreign-key relationships
  • Session-based auth is dead simple to reason about in a single-operator C2
  • Tailwind CSS gives the dashboard a professional appearance without custom CSS work

The controller provides:

  • Real-time target list: Heartbeat status (online/offline), last check-in timestamp, OS fingerprint, integrity level
  • Task queue system: Send commands to individual targets or broadcast to all. Each task includes command type, payload, and expected return
  • Module interfaces: Pre-built post-exploitation panels — screen capture, process list, system info, interactive shell, keylogger, persistence, privilege escalation
  • Payload builder: Generate configured agents with custom check-in intervals, C2 addresses, and evasion flags

Agent: Python → PyInstaller → OneDriveUpdater.exe

The agent runs on the target machine. Written in Python for rapid iteration, compiled to an executable via PyInstaller.

The name OneDriveUpdater.exe is deliberate — it mimics a legitimate Microsoft process. Combined with the correct icon (extracted from the real OneDrive binary), it blends into the process list.

Key agent behaviors:

  • HWID fingerprinting: Hardware ID uniquely identifies each target across sessions
  • Configurable check-in intervals: Default 30s, with jitter to avoid creating detectable network patterns
  • Integrity level detection: Automatically determines admin vs. standard user, adjusts available modules
  • Multi-interpreter execution: Python, PowerShell, CMD — selects the best execution method per command type

Communication: HTTP(S) Check-in

The agent polls the controller via standard HTTPS POST requests with JSON payloads. Two endpoints:

gate.php  →  Initial registration and heartbeat
sync.php  →  Task polling and result submission

The communication deliberately mimics a REST API. To a network monitor, it looks like a web application making periodic API calls — which is exactly what it is, just not the kind anyone expects.

Evasion: Where the Real Learning Happens

Writing a C2 that works on an unprotected machine is trivial. Making it survive modern endpoint protection is where you actually learn security.

Anti-Analysis Engine

Before executing anything sensitive, the agent checks its environment:

python
ANALYSIS_TOOLS = [ "procmon.exe", "wireshark.exe", "x64dbg.exe", "processhacker.exe", "fiddler.exe", "ollydbg.exe", "ida64.exe", "ghidra.exe", "dnspy.exe" ] def is_analyzed(): for proc in psutil.process_iter(['name']): if proc.info['name'].lower() in ANALYSIS_TOOLS: return True # VM detection via WMI model = subprocess.check_output( 'wmic computersystem get model', shell=True ).decode().lower() return any(vm in model for vm in ['virtual', 'vmware', 'vbox', 'qemu'])

If it detects a sandbox, debugger, or analysis environment, it exits silently. No crash, no error — just quiet termination. This is how real malware behaves, and understanding it is how you build better sandboxes.

AMSI Patching

Windows' Anti-Malware Scan Interface (AMSI) inspects PowerShell commands before execution. Every Invoke-Expression or script block passes through AmsiScanBuffer for analysis.

Marrow patches this function in memory. The technique:

  1. Locate amsi.dll in the process memory space
  2. Find the AmsiScanBuffer function address
  3. Overwrite the first bytes with a return instruction (0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3)
  4. All subsequent PowerShell commands bypass AMSI inspection

This is a well-documented technique in the red team community, but implementing it from scratch teaches you exactly how AMSI works — which is the first step to detecting when someone tampers with it.

Payload Binder

binder/bind.py wraps the agent inside a legitimate executable:

  1. Take a legitimate installer (e.g., VLC, 7-Zip)
  2. Extract its icon using PE resource parsing
  3. Inject that icon into the compiled agent
  4. Create a launcher that executes both binaries simultaneously
  5. User sees the expected application open while the agent runs silently in the background

The binder handles icon extraction, resource injection, and execution chaining — all through direct PE file manipulation.

Living-off-the-Land

Post-exploitation commands use built-in Windows binaries: PowerShell, certutil, wmic, net, reg. No custom tools are dropped to disk. This reduces the forensic footprint and avoids file-based detection entirely.

Detection Engineering: Catching Your Own C2

This is the most valuable section. Every offensive technique has a defensive counterpart. Building Marrow taught me exactly what to look for:

Network-Level Detection

Beaconing Analysis: Even with jitter, HTTP check-ins create a statistical pattern. If you plot the intervals between POST requests to the same endpoint, the distribution reveals periodicity. Detection rule: flag any internal host making >50 POST requests to the same external endpoint in 24 hours with a coefficient of variation <0.3.

TLS Certificate Analysis: Self-signed or newly-registered certificates on C2 servers are a strong IOC. JA3/JA3S fingerprinting can identify the TLS handshake pattern of PyInstaller-compiled executables.

Endpoint-Level Detection

Offensive TechniqueWhat to Monitor
AMSI patchingETW events for AmsiScanBuffer memory writes. Any process modifying amsi.dll in memory is hostile.
Registry persistenceSIEM alerts on HKCU\...\Run key modifications by non-standard processes
WMI model querieswmic computersystem get model from a script context is almost always sandbox detection
Process enumerationRapid process_iter() calls from a non-admin binary checking for security tools
OneDriveUpdater.exe in AppDataLegitimate OneDrive runs from Program Files, not AppData\Local\Temp

SIEM Rule Examples

# Detect AMSI patching attempt
rule: process_modified_amsi_dll
condition: any(target.module == "amsi.dll" AND event.type == "MEMORY_WRITE")
severity: CRITICAL

# Detect periodic HTTP beaconing  
rule: periodic_http_callback
condition: host_outbound_posts(same_endpoint, count > 50, interval_cv < 0.3, period = 24h)
severity: HIGH

What This Project Proves

Building offensive tools is the fastest path to understanding defense. You can't write effective detection rules if you don't know what you're detecting. You can't build resilient infrastructure if you don't understand how attackers move through it.

Every SIEM alert, every EDR policy, every firewall rule makes more sense when you've built the thing it's designed to catch.