In the 2024 Volt Typhoon campaign, attackers lived inside US critical infrastructure for five years before detection. Their secret? A lightweight C2 architecture that blended into normal traffic and re-established footholds every time defenders thought they’d cleaned up. That pattern — initial compromise, C2 callback, persistence — repeats across nearly every major breach. Understanding how it works is the first step to stopping it.
How C2 Beaconing Actually Works
A C2 framework (Command and Control) is the attacker’s remote management layer. Once malware lands on a host, it “beacons” home — sending periodic check-ins to a server the attacker controls. Modern frameworks like Sliver, Havoc, and Cobalt Strike make this trivially easy to set up.
Here’s what a Sliver implant beacon looks like on the wire, captured with tcpdump on a compromised host (ws01.corp.internal, 192.0.2.47):
# tcpdump -i eth0 -nn host 192.0.2.200 and port 443
23:14:01.332211 IP 192.0.2.47.52341 > 192.0.2.200.443: Flags [S]
23:14:01.334002 IP 192.0.2.200.443 > 192.0.2.47.52341: Flags [S.]
23:14:01.334891 IP 192.0.2.47.52341 > 192.0.2.200.443: Flags [.]
23:14:01.336104 IP 192.0.2.47.52341 > 192.0.2.200.443: Flags [P.] length 284
23:14:01.338901 IP 192.0.2.200.443 > 192.0.2.47.52341: Flags [P.] length 112
23:14:01.339200 IP 192.0.2.47.52341 > 192.0.2.200.443: Flags [.]
# --- 60 seconds of silence ---
23:15:01.441033 IP 192.0.2.47.52341 > 192.0.2.200.443: Flags [P.] length 284
That 60-second interval is the tell. Normal user HTTPS traffic is bursty and irregular. This is metronomic — a machine checking in, not a human browsing. The payload length of exactly 284 bytes each time is another red flag; real application traffic varies.
An attacker seeing this from their C2 panel knows the implant is alive and waiting for tasking. A defender seeing this in Zeek or Suricata logs should immediately pivot: what process on 192.0.2.47 owns that socket? What’s the parent process?
Persistence Mechanisms: Beyond the Beacon
Beaconing keeps the channel open. Persistence keeps the implant running after reboots, logoffs, and even antivirus cleaning attempts. Attackers stack multiple persistence methods — if one gets pulled, another fires.
Here’s a real-world pattern: a Havoc C2 operator dropping a scheduled task on a compromised Windows host logged in as jsmith:
C:\> schtasks /create /tn "MicrosoftEdgeUpdateTaskUser" /tr "C:\Users\jsmith\AppData\Local\Temp\svchost32.exe" /sc MINUTE /mo 15 /ru jsmith /f
SUCCESS: The scheduled task "MicrosoftEdgeUpdateTaskUser" has been successfully created.
C:\> schtasks /query /tn "MicrosoftEdgeUpdateTaskUser" /fo LIST
Folder: \
TaskName: MicrosoftEdgeUpdateTaskUser
Status: Ready
Schedule Type: One Time Only, Minute
Start Time: N/A
Start Date: N/A
Run As User: jsmith
Task To Run: C:\Users\jsmith\AppData\Local\Temp\svchost32.exe
Three things make this nasty. First, the task name mimics a legitimate Edge updater — most analysts skip right past it. Second, it runs as jsmith, a regular user, so it doesn’t trigger UAC or high-privilege alerts. Third, the binary lives in \Temp\ — a directory that rarely gets scrutinized.
Every 15 minutes, even if the original beacon process is killed, this task re-launches the implant. Defenders need to audit scheduled tasks with schtasks /query /fo LIST /v and flag anything running from AppData, Temp, or ProgramData paths. Those locations are not where legitimate software lives.
Detection: Finding the Thread to Pull
You don’t need a million-dollar SIEM to start hunting. Process-to-network correlation is one of the highest-yield techniques. On Linux, ss combined with process info exposes implants that blend into normal service names:
# ss -tulpn | grep ESTABLISHED | awk '{print $5, $7}'
192.0.2.200:443 users:(("python3",pid=3847,fd=4))
192.0.2.11:5432 users:(("postgres",pid=1021,fd=10))
192.0.2.88:80 users:(("nginx",pid=889,fd=6))
# ls -la /proc/3847/exe
lrwxrwxrwx 1 www-data www-data 0 Nov 9 23:14 /proc/3847/exe -> /var/www/.cache/.update/python3
# stat /var/www/.cache/.update/python3
File: /var/www/.cache/.update/python3
Size: 2847392
Modify: 2026-11-08 02:31:17
Change: 2026-11-08 02:31:17
The PostgreSQL and nginx connections look fine. But python3 connecting outbound to 192.0.2.200:443 from /var/www/.cache/.update/? That binary was dropped 24 hours ago. Real Python interpreters live in /usr/bin/ or /usr/local/bin/ — not hidden directories under the web root.
That modification timestamp tells you exactly when the attacker had access. Now you have a pivot point: check auth logs, web server access logs, and bash history for November 8th at 02:31.
What To Do Now
Pull up your endpoint monitoring tool — EDR, osquery, or even a quick script — and run one query: find every process making an outbound TLS connection where the binary path is NOT in a standard system directory. On Linux that means anything outside /usr/bin, /usr/sbin, /opt. On Windows, flag connections from AppData, Temp, or ProgramData. Sort by regularity of connection intervals. The beacons will stand out. One suspicious process is your starting point — work backward from there.
