Nginx redirector¶
A redirector takes inbound traffic from the target’s network and forwards only the parts that match a known beacon to the frontend. Everything else goes to a cover site.
This is one layer thinner than the frontend nginx. The frontend terminates TLS for the cover site and routes to backends. The redirector sits closer to the target, on cheap, burnable hosts, and only knows where the next hop is.
Pattern¶
target ── HTTPS ──> redirector (nginx) ── HTTPS ──> frontend (nginx) ── HTTP ──> backend (C2)
Minimal config¶
/etc/nginx/conf.d/redir.conf:
server {
listen 443 ssl http2;
server_name www.<customdomain>.com;
ssl_certificate /etc/letsencrypt/live/<customdomain>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<customdomain>/privkey.pem;
# Beacon URI prefix forwarded to the frontend
location /api/v2/ {
proxy_pass https://frontend.<otherdomain>.com;
proxy_set_header Host frontend.<otherdomain>.com;
proxy_ssl_server_name on;
}
# Everything else gets the cover page
location / {
return 301 https://www.example-cover-site.com$request_uri;
}
}
Match the /api/v2/ prefix to the beacon profile of the chosen C2. Pick something
boring that the cover site could plausibly serve.
Rotation¶
Provision the redirector via Terraform or cloud-init.
Generate a fresh certificate per host.
When the IP is burned, destroy the host and rebuild. The frontend stays put, the next redirector points at it under a new name.
User-Agent and source filters¶
Drop traffic that does not look like the beacon profile early:
if ($http_user_agent !~* "(Mozilla|Googlebot)") { return 404; }
if ($geoip_country_code !~ "(US|GB|NL)") { return 404; }
This frustrates lazy scanners and reduces the amount of noise the frontend has to filter.