npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
NIST CSF 2.0
MITRE D3FEND
Overview
Domain fronting (MITRE ATT&CK T1090.004) is a technique where attackers use different domain names in the TLS SNI field and the HTTP Host header to disguise C2 traffic behind legitimate CDN-hosted domains. This skill detects domain fronting by parsing proxy/web gateway logs for SNI-Host header mismatches, analyzing TLS certificates for CDN provider identification, flagging connections where the SNI points to a high-reputation domain but the Host header targets an attacker-controlled domain, and correlating with known CDN provider IP ranges.
When to Use
- When investigating security incidents that require hunting for domain fronting c2 traffic
- When building detection rules or threat hunting queries for this domain
- When SOC analysts need structured procedures for this analysis type
- When validating security monitoring coverage for related attack techniques
Prerequisites
- Web proxy or secure web gateway logs with SNI and Host header fields
- Python 3.8+ with pyOpenSSL and cryptography libraries
- TLS inspection enabled on proxy for Host header visibility
- CDN provider IP range lists (CloudFront, Azure CDN, Cloudflare)
Steps
- Parse proxy logs for connections with both SNI and Host header fields
- Compare SNI domain against HTTP Host header for mismatches
- Extract TLS certificate Subject and SAN fields using pyOpenSSL
- Identify CDN-hosted connections via certificate issuer and IP ranges
- Flag high-confidence domain fronting where SNI and Host differ on CDN IPs
- Score alerts based on domain reputation differential
- Generate detection report with network flow context
Expected Output
JSON report containing detected domain fronting indicators with SNI-Host pairs, certificate details, CDN provider identification, confidence scores, and MITRE ATT&CK technique mapping.
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 1
api-reference.md2.1 KB
Domain Fronting C2 Traffic Detection API Reference
Domain Fronting Mechanism
TLS ClientHello: SNI = legitimate-cdn-domain.cloudfront.net
HTTP Request: Host: attacker-c2-server.evil.comThe CDN accepts the TLS connection based on SNI, then routes the HTTP request to the backend specified in the Host header. Network monitoring sees only the legitimate SNI domain.
MITRE ATT&CK
| Technique | ID | Description |
|---|---|---|
| Proxy: Domain Fronting | T1090.004 | Route C2 through CDN using SNI/Host mismatch |
CDN Provider Identification
Certificate Issuers
| CDN | Certificate CN Pattern |
|---|---|
| CloudFront | *.cloudfront.net |
| Azure CDN | *.azureedge.net |
| Cloudflare | sni.cloudflaressl.com |
| Akamai | *.akamaiedge.net |
| Fastly | *.fastly.net |
Proxy Log Detection
Squid Proxy Log Fields
timestamp src_ip CONNECT sni:443 -> status Host: host_headerPalo Alto Threat ID
Threat ID 86467: Domain fronting detected (SNI/Host mismatch)Splunk Detection Query
index=proxy sourcetype=squid OR sourcetype=bluecoat
| eval sni_root=mvindex(split(sni, "."), -2) + "." + mvindex(split(sni, "."), -1)
| eval host_root=mvindex(split(host_header, "."), -2) + "." + mvindex(split(host_header, "."), -1)
| where sni_root != host_root
| stats count by sni, host_header, src_ip
| sort -countpyOpenSSL Certificate Inspection
from OpenSSL import crypto
import ssl, socket
ctx = ssl.create_default_context()
with ctx.wrap_socket(socket.socket(), server_hostname=hostname) as s:
s.connect((hostname, 443))
der_cert = s.getpeercert(True)
x509 = crypto.load_certificate(crypto.FILETYPE_ASN1, der_cert)
subject_cn = x509.get_subject().CN
issuer_cn = x509.get_issuer().CN
for i in range(x509.get_extension_count()):
ext = x509.get_extension(i)
if ext.get_short_name() == b"subjectAltName":
print(str(ext)) # DNS:*.cloudfront.net, DNS:cloudfront.netCLI Usage
python agent.py --proxy-log squid_access.csv --output fronting_report.json
python agent.py --proxy-log logs.csv --check-certsScripts 1
agent.py6.5 KB
#!/usr/bin/env python3
"""Detect domain fronting C2 traffic via SNI/Host header mismatch and TLS certificate analysis."""
import json
import csv
import ssl
import socket
import argparse
from collections import defaultdict
from datetime import datetime
try:
from OpenSSL import crypto
HAS_PYOPENSSL = True
except ImportError:
HAS_PYOPENSSL = False
CDN_PROVIDERS = {
"cloudfront.net": "Amazon CloudFront",
"azureedge.net": "Azure CDN",
"cloudflare.com": "Cloudflare",
"akamaiedge.net": "Akamai",
"fastly.net": "Fastly",
"googleapis.com": "Google Cloud CDN",
"azurefd.net": "Azure Front Door",
}
def load_proxy_logs(filepath):
"""Load proxy logs CSV with columns: timestamp, src_ip, sni, host_header, dst_ip, dst_port, method, url, status."""
records = []
with open(filepath, newline="", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
records.append({
"timestamp": row.get("timestamp", ""),
"src_ip": row.get("src_ip", row.get("c-ip", "")),
"sni": row.get("sni", row.get("cs-ssl-sni", "")).lower().strip(),
"host_header": row.get("host_header", row.get("cs-host", "")).lower().strip(),
"dst_ip": row.get("dst_ip", row.get("s-ip", "")),
"dst_port": int(row.get("dst_port", row.get("s-port", "443"))),
"method": row.get("method", row.get("cs-method", "")),
"url": row.get("url", row.get("cs-uri-stem", "")),
"status": row.get("status", row.get("sc-status", "")),
"bytes": int(row.get("bytes", row.get("sc-bytes", "0"))),
})
return records
def extract_domain_root(domain):
"""Extract root domain from FQDN (e.g., sub.example.com -> example.com)."""
parts = domain.rstrip(".").split(".")
return ".".join(parts[-2:]) if len(parts) >= 2 else domain
def identify_cdn_provider(domain):
"""Check if a domain belongs to a known CDN provider."""
for cdn_suffix, provider in CDN_PROVIDERS.items():
if domain.endswith(cdn_suffix):
return provider
return None
def detect_sni_host_mismatch(records):
"""Detect connections where SNI and Host header point to different domains."""
alerts = []
for rec in records:
sni = rec["sni"]
host = rec["host_header"]
if not sni or not host:
continue
sni_root = extract_domain_root(sni)
host_root = extract_domain_root(host)
if sni_root != host_root:
cdn = identify_cdn_provider(sni) or identify_cdn_provider(host)
confidence = "high" if cdn else "medium"
alerts.append({
"detection": "SNI/Host Header Mismatch",
"mitre_technique": "T1090.004",
"timestamp": rec["timestamp"],
"src_ip": rec["src_ip"],
"sni": sni,
"host_header": host,
"sni_root": sni_root,
"host_root": host_root,
"cdn_provider": cdn,
"dst_ip": rec["dst_ip"],
"confidence": confidence,
"severity": "critical" if cdn else "high",
"description": f"Domain fronting: SNI={sni} but Host={host}",
})
return alerts
def get_tls_certificate_info(hostname, port=443, timeout=5):
"""Retrieve TLS certificate details for a given hostname using pyOpenSSL."""
if not HAS_PYOPENSSL:
return {"error": "pyOpenSSL not installed"}
try:
ctx = ssl.create_default_context()
with ctx.wrap_socket(socket.socket(), server_hostname=hostname) as s:
s.settimeout(timeout)
s.connect((hostname, port))
der_cert = s.getpeercert(True)
x509 = crypto.load_certificate(crypto.FILETYPE_ASN1, der_cert)
subject = dict(x509.get_subject().get_components())
issuer = dict(x509.get_issuer().get_components())
san_list = []
for i in range(x509.get_extension_count()):
ext = x509.get_extension(i)
if ext.get_short_name() == b"subjectAltName":
san_list = [s.strip().replace("DNS:", "") for s in str(ext).split(",")]
return {
"subject_cn": subject.get(b"CN", b"").decode(),
"issuer_cn": issuer.get(b"CN", b"").decode(),
"issuer_o": issuer.get(b"O", b"").decode(),
"san": san_list[:20],
"not_before": str(x509.get_notBefore()),
"not_after": str(x509.get_notAfter()),
"serial": str(x509.get_serial_number()),
}
except Exception as e:
return {"error": str(e)}
def analyze_fronting_pairs(alerts):
"""Aggregate and rank domain fronting pairs by frequency."""
pair_counts = defaultdict(int)
for a in alerts:
pair_counts[(a["sni"], a["host_header"])] += 1
ranked = sorted(pair_counts.items(), key=lambda x: -x[1])
return [{"sni": p[0], "host": p[1], "count": c} for (p, c) in ranked[:20]]
def main():
parser = argparse.ArgumentParser(description="Domain Fronting C2 Traffic Hunter")
parser.add_argument("--proxy-log", required=True, help="CSV proxy log with SNI and Host header fields")
parser.add_argument("--check-certs", action="store_true", help="Fetch TLS certs for top fronting domains")
parser.add_argument("--output", default="domain_fronting_report.json", help="Output report path")
args = parser.parse_args()
records = load_proxy_logs(args.proxy_log)
print(f"[+] Loaded {len(records)} proxy log entries")
alerts = detect_sni_host_mismatch(records)
print(f"[+] Detected {len(alerts)} SNI/Host mismatches")
fronting_pairs = analyze_fronting_pairs(alerts)
cert_info = {}
if args.check_certs and fronting_pairs:
for pair in fronting_pairs[:5]:
cert_info[pair["sni"]] = get_tls_certificate_info(pair["sni"])
report = {
"analysis_time": datetime.utcnow().isoformat() + "Z",
"total_proxy_entries": len(records),
"detections": alerts[:50],
"total_mismatches": len(alerts),
"fronting_pairs_ranked": fronting_pairs,
"certificate_analysis": cert_info,
"mitre_technique": "T1090.004",
"cdn_involved": list({a["cdn_provider"] for a in alerts if a.get("cdn_provider")}),
}
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
print(f"[+] Top fronting pairs: {len(fronting_pairs)}")
print(f"[+] Report saved to {args.output}")
if __name__ == "__main__":
main()