Hunting for Command and Control Beaconing When to Use - When proactively hunting for compromised systems in the network - After threat intel indicates C2 frameworks targeting your industry - When investigating periodic outbound connections to suspicious domains - During incident response to identify active C2 channels - When DNS query logs show unusual patterns to specific domains Prerequisites - Network proxy/firewall logs with full URL and timing data - DNS query logs (passive DNS, DNS server logs, or Sysmon Event ID 22) - Zeek/Bro network connection logs or NetFlow data - SIEM with statist…

, # Direct IP\n ],\n}\n\n\ndef analyze_dns_queries(dns_log_path):\n \"\"\"Analyze DNS query logs for C2 indicators.\"\"\"\n findings = []\n domain_counts = defaultdict(int)\n try:\n with open(dns_log_path, \"r\") as f:\n for line in f:\n if line.startswith(\"#\"):\n continue\n fields = line.strip().split(\"\\t\")\n if len(fields) \u003c 10:\n continue\n query = fields[9] if len(fields) > 9 else \"\"\n domain_counts[query] += 1\n for pattern in C2_INDICATORS[\"dns_c2_patterns\"]:\n if re.match(pattern, query):\n findings.append({\n \"type\": \"suspicious_dns\",\n \"query\": query,\n \"pattern\": pattern,\n })\n except FileNotFoundError:\n pass\n\n high_freq = sorted(domain_counts.items(), key=lambda x: x[1], reverse=True)[:20]\n for domain, count in high_freq:\n if count > 100 and len(domain) > 20:\n findings.append({\n \"type\": \"high_frequency_dns\",\n \"domain\": domain,\n \"query_count\": count,\n })\n return findings\n\n\ndef analyze_http_logs(http_log_path):\n \"\"\"Analyze HTTP logs for C2-like traffic patterns.\"\"\"\n findings = []\n try:\n with open(http_log_path, \"r\") as f:\n for line in f:\n if line.startswith(\"#\"):\n continue\n fields = line.strip().split(\"\\t\")\n if len(fields) \u003c 13:\n continue\n host = fields[8] if len(fields) > 8 else \"\"\n uri = fields[9] if len(fields) > 9 else \"\"\n user_agent = fields[12] if len(fields) > 12 else \"\"\n for ua in C2_INDICATORS[\"suspicious_user_agents\"]:\n if ua in user_agent.lower():\n findings.append({\n \"type\": \"suspicious_user_agent\",\n \"host\": host,\n \"uri\": uri[:100],\n \"user_agent\": user_agent[:100],\n })\n break\n if re.match(r'^/[a-zA-Z0-9]{4,8}

Hunting for Command and Control Beaconing When to Use - When proactively hunting for compromised systems in the network - After threat intel indicates C2 frameworks targeting your industry - When investigating periodic outbound connections to suspicious domains - During incident response to identify active C2 channels - When DNS query logs show unusual patterns to specific domains Prerequisites - Network proxy/firewall logs with full URL and timing data - DNS query logs (passive DNS, DNS server logs, or Sysmon Event ID 22) - Zeek/Bro network connection logs or NetFlow data - SIEM with statist…

, uri):\n findings.append({\n \"type\": \"c2_uri_pattern\",\n \"host\": host,\n \"uri\": uri,\n \"note\": \"Short random URI typical of C2 frameworks\",\n })\n except FileNotFoundError:\n pass\n return findings\n\n\ndef analyze_connection_patterns(conn_log_path):\n \"\"\"Detect persistent long-duration connections typical of C2.\"\"\"\n findings = []\n try:\n with open(conn_log_path, \"r\") as f:\n for line in f:\n if line.startswith(\"#\"):\n continue\n fields = line.strip().split(\"\\t\")\n if len(fields) \u003c 10:\n continue\n src = fields[2]\n dst = fields[4]\n dst_port = fields[5]\n duration = fields[8] if len(fields) > 8 else \"0\"\n orig_bytes = fields[9] if len(fields) > 9 else \"0\"\n resp_bytes = fields[10] if len(fields) > 10 else \"0\"\n try:\n dur = float(duration) if duration != \"-\" else 0\n ob = int(orig_bytes) if orig_bytes != \"-\" else 0\n rb = int(resp_bytes) if resp_bytes != \"-\" else 0\n except ValueError:\n continue\n if dur > 3600 and ob > 0 and rb > 0:\n ratio = ob / rb if rb > 0 else 999\n if 0.8 \u003c ratio \u003c 1.2:\n findings.append({\n \"type\": \"persistent_symmetric\",\n \"src\": src, \"dst\": dst, \"port\": dst_port,\n \"duration_hours\": round(dur / 3600, 1),\n \"data_ratio\": round(ratio, 2),\n })\n except FileNotFoundError:\n pass\n return findings\n\n\ndef main():\n parser = argparse.ArgumentParser(\n description=\"Hunt for C2 beaconing across network data sources\"\n )\n parser.add_argument(\"--conn-log\", help=\"Zeek conn.log\")\n parser.add_argument(\"--dns-log\", help=\"Zeek dns.log\")\n parser.add_argument(\"--http-log\", help=\"Zeek http.log\")\n parser.add_argument(\"--output\", \"-o\", help=\"Output JSON report\")\n args = parser.parse_args()\n\n print(\"[*] C2 Beaconing Hunting Agent\")\n report = {\"timestamp\": datetime.now(timezone.utc).isoformat(), \"findings\": {}}\n\n if args.dns_log:\n dns = analyze_dns_queries(args.dns_log)\n report[\"findings\"][\"dns\"] = dns\n print(f\"[*] DNS findings: {len(dns)}\")\n\n if args.http_log:\n http = analyze_http_logs(args.http_log)\n report[\"findings\"][\"http\"] = http\n print(f\"[*] HTTP findings: {len(http)}\")\n\n if args.conn_log:\n conn = analyze_connection_patterns(args.conn_log)\n report[\"findings\"][\"connections\"] = conn\n print(f\"[*] Connection findings: {len(conn)}\")\n\n total = sum(len(v) for v in report[\"findings\"].values())\n report[\"risk_level\"] = \"CRITICAL\" if total >= 10 else \"HIGH\" if total >= 5 else \"MEDIUM\" if total > 0 else \"LOW\"\n\n if args.output:\n with open(args.output, \"w\") as f:\n json.dump(report, f, indent=2)\n print(f\"[*] Report saved to {args.output}\")\n else:\n print(json.dumps(report, indent=2))\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":6213,"content_sha256":"9b749f6dc3d7710c44293d607cf56fd3b6b10dd64bcb925c182d602ab78a6407"},{"filename":"scripts/process.py","content":"#!/usr/bin/env python3\n\"\"\"\nC2 Beaconing Detection Script\nAnalyzes network connection logs for periodic beaconing patterns\nusing statistical frequency analysis and jitter detection.\n\"\"\"\n\nimport json\nimport csv\nimport argparse\nimport datetime\nimport math\nimport re\nfrom collections import defaultdict\nfrom pathlib import Path\n\n# Known legitimate beaconing services to exclude\nKNOWN_GOOD_DOMAINS = {\n \"microsoft.com\", \"windowsupdate.com\", \"google.com\", \"googleapis.com\",\n \"gstatic.com\", \"amazonaws.com\", \"cloudflare.com\", \"akamai.net\",\n \"apple.com\", \"icloud.com\", \"adobe.com\", \"symantec.com\",\n \"norton.com\", \"mcafee.com\", \"crowdstrike.com\", \"sentinelone.com\",\n \"office365.com\", \"office.com\", \"live.com\", \"outlook.com\",\n \"github.com\", \"slack.com\", \"teams.microsoft.com\",\n}\n\n# Known C2 framework default ports\nC2_SUSPICIOUS_PORTS = {443, 8443, 8080, 4444, 5555, 6666, 8888, 9090, 50050, 31337}\n\n# Beaconing detection thresholds\nBEACON_THRESHOLDS = {\n \"min_connections\": 20, # Minimum connections for analysis\n \"max_cv\": 0.25, # Max coefficient of variation for periodicity\n \"min_interval\": 10, # Minimum average interval (seconds)\n \"max_interval\": 86400, # Maximum average interval (1 day)\n \"max_data_cv\": 0.30, # Max CV for data size consistency\n}\n\n\ndef parse_logs(input_path: str) -> list[dict]:\n \"\"\"Parse connection logs (Zeek, CSV, JSON format).\"\"\"\n path = Path(input_path)\n events = []\n\n if path.suffix == \".json\":\n with open(path, \"r\", encoding=\"utf-8\") as f:\n data = json.load(f)\n events = data if isinstance(data, list) else data.get(\"events\", [])\n elif path.suffix == \".csv\":\n with open(path, \"r\", encoding=\"utf-8-sig\") as f:\n events = [dict(row) for row in csv.DictReader(f)]\n elif path.suffix == \".log\":\n # Zeek tab-separated format\n with open(path, \"r\", encoding=\"utf-8\") as f:\n headers = None\n for line in f:\n if line.startswith(\"#fields\"):\n headers = line.strip().split(\"\\t\")[1:]\n elif line.startswith(\"#\"):\n continue\n elif headers:\n values = line.strip().split(\"\\t\")\n if len(values) == len(headers):\n events.append(dict(zip(headers, values)))\n return events\n\n\ndef normalize_connection(event: dict) -> dict:\n \"\"\"Normalize connection event fields.\"\"\"\n field_map = {\n \"timestamp\": [\"ts\", \"timestamp\", \"_time\", \"@timestamp\", \"Timestamp\"],\n \"src_ip\": [\"id.orig_h\", \"src_ip\", \"source_ip\", \"LocalIP\", \"DeviceName\"],\n \"src_port\": [\"id.orig_p\", \"src_port\", \"source_port\", \"LocalPort\"],\n \"dst_ip\": [\"id.resp_h\", \"dst_ip\", \"dest_ip\", \"RemoteIP\", \"DestinationIp\"],\n \"dst_port\": [\"id.resp_p\", \"dst_port\", \"dest_port\", \"RemotePort\", \"DestinationPort\"],\n \"domain\": [\"query\", \"domain\", \"host\", \"RemoteUrl\", \"server_name\", \"dest\"],\n \"bytes_sent\": [\"orig_bytes\", \"bytes_out\", \"SentBytes\", \"bytes_sent\"],\n \"bytes_recv\": [\"resp_bytes\", \"bytes_in\", \"ReceivedBytes\", \"bytes_recv\"],\n \"duration\": [\"duration\", \"conn_duration\", \"session_duration\"],\n \"proto\": [\"proto\", \"protocol\", \"Protocol\"],\n \"user_agent\": [\"user_agent\", \"UserAgent\", \"http_user_agent\"],\n }\n normalized = {}\n for target, sources in field_map.items():\n for src in sources:\n if src in event and event[src] and event[src] != \"-\":\n normalized[target] = str(event[src])\n break\n if target not in normalized:\n normalized[target] = \"\"\n return normalized\n\n\ndef is_known_good(domain: str) -> bool:\n \"\"\"Check if domain is in known-good list.\"\"\"\n domain_lower = domain.lower()\n for good in KNOWN_GOOD_DOMAINS:\n if domain_lower.endswith(good):\n return True\n return False\n\n\ndef calculate_entropy(text: str) -> float:\n \"\"\"Calculate Shannon entropy of a string.\"\"\"\n if not text:\n return 0.0\n freq = defaultdict(int)\n for char in text:\n freq[char] += 1\n length = len(text)\n entropy = 0.0\n for count in freq.values():\n p = count / length\n if p > 0:\n entropy -= p * math.log2(p)\n return entropy\n\n\ndef detect_beaconing(connections: list[dict]) -> list[dict]:\n \"\"\"Analyze connection patterns for beaconing behavior.\"\"\"\n # Group connections by source-destination pair\n pairs = defaultdict(list)\n for conn in connections:\n src = conn.get(\"src_ip\", \"\")\n dst = conn.get(\"domain\", \"\") or conn.get(\"dst_ip\", \"\")\n if src and dst and not is_known_good(dst):\n try:\n ts = float(conn.get(\"timestamp\", 0))\n except (ValueError, TypeError):\n # Try parsing ISO timestamp\n try:\n dt = datetime.datetime.fromisoformat(conn[\"timestamp\"].replace(\"Z\", \"+00:00\"))\n ts = dt.timestamp()\n except (ValueError, KeyError):\n continue\n pairs[(src, dst)].append({\n \"timestamp\": ts,\n \"bytes_sent\": int(conn.get(\"bytes_sent\", 0) or 0),\n \"bytes_recv\": int(conn.get(\"bytes_recv\", 0) or 0),\n \"dst_port\": conn.get(\"dst_port\", \"\"),\n \"user_agent\": conn.get(\"user_agent\", \"\"),\n })\n\n findings = []\n\n for (src, dst), conns in pairs.items():\n if len(conns) \u003c BEACON_THRESHOLDS[\"min_connections\"]:\n continue\n\n # Sort by timestamp\n conns.sort(key=lambda x: x[\"timestamp\"])\n\n # Calculate intervals\n intervals = []\n for i in range(1, len(conns)):\n interval = conns[i][\"timestamp\"] - conns[i - 1][\"timestamp\"]\n if interval > 0:\n intervals.append(interval)\n\n if len(intervals) \u003c 10:\n continue\n\n # Statistical analysis\n avg_interval = sum(intervals) / len(intervals)\n if avg_interval \u003c BEACON_THRESHOLDS[\"min_interval\"] or avg_interval > BEACON_THRESHOLDS[\"max_interval\"]:\n continue\n\n variance = sum((x - avg_interval) ** 2 for x in intervals) / len(intervals)\n stdev = math.sqrt(variance)\n cv = stdev / avg_interval if avg_interval > 0 else float(\"inf\")\n\n # Check if beaconing threshold met\n if cv > BEACON_THRESHOLDS[\"max_cv\"]:\n continue\n\n # Calculate data size consistency\n bytes_sent_list = [c[\"bytes_sent\"] for c in conns if c[\"bytes_sent\"] > 0]\n data_cv = 0.0\n if bytes_sent_list:\n avg_bytes = sum(bytes_sent_list) / len(bytes_sent_list)\n if avg_bytes > 0:\n data_var = sum((x - avg_bytes) ** 2 for x in bytes_sent_list) / len(bytes_sent_list)\n data_cv = math.sqrt(data_var) / avg_bytes\n\n # Calculate risk score\n risk = 0\n indicators = []\n\n # Low CV = high periodicity\n if cv \u003c 0.05:\n risk += 40\n indicators.append(f\"Very regular interval (CV={cv:.4f})\")\n elif cv \u003c 0.15:\n risk += 30\n indicators.append(f\"Regular interval (CV={cv:.4f})\")\n else:\n risk += 20\n indicators.append(f\"Moderately regular interval (CV={cv:.4f})\")\n\n # Consistent data sizes\n if data_cv \u003c 0.10 and bytes_sent_list:\n risk += 15\n indicators.append(f\"Very consistent payload size (CV={data_cv:.4f})\")\n\n # Suspicious port\n dst_ports = set(c[\"dst_port\"] for c in conns)\n for port in dst_ports:\n try:\n if int(port) in C2_SUSPICIOUS_PORTS:\n risk += 10\n indicators.append(f\"Suspicious port: {port}\")\n except ValueError:\n pass\n\n # High connection count\n if len(conns) > 500:\n risk += 10\n indicators.append(f\"High connection count: {len(conns)}\")\n\n # Domain entropy (DGA indicator)\n domain_parts = dst.split(\".\")\n if domain_parts:\n entropy = calculate_entropy(domain_parts[0])\n if entropy > 3.5:\n risk += 15\n indicators.append(f\"High domain entropy: {entropy:.2f} (possible DGA)\")\n\n risk_level = (\n \"CRITICAL\" if risk >= 70 else \"HIGH\" if risk >= 50\n else \"MEDIUM\" if risk >= 30 else \"LOW\"\n )\n\n # Estimate jitter percentage\n jitter_pct = (stdev / avg_interval * 100) if avg_interval > 0 else 0\n\n findings.append({\n \"src_ip\": src,\n \"destination\": dst,\n \"connection_count\": len(conns),\n \"avg_interval_sec\": round(avg_interval, 2),\n \"stdev_interval\": round(stdev, 2),\n \"coefficient_of_variation\": round(cv, 4),\n \"estimated_jitter_pct\": round(jitter_pct, 1),\n \"avg_bytes_sent\": round(sum(bytes_sent_list) / len(bytes_sent_list)) if bytes_sent_list else 0,\n \"data_size_cv\": round(data_cv, 4),\n \"first_seen\": datetime.datetime.fromtimestamp(conns[0][\"timestamp\"]).isoformat(),\n \"last_seen\": datetime.datetime.fromtimestamp(conns[-1][\"timestamp\"]).isoformat(),\n \"dst_ports\": list(dst_ports),\n \"risk_score\": risk,\n \"risk_level\": risk_level,\n \"indicators\": indicators,\n })\n\n return sorted(findings, key=lambda x: x[\"risk_score\"], reverse=True)\n\n\ndef detect_dns_tunneling(connections: list[dict]) -> list[dict]:\n \"\"\"Detect DNS tunneling indicators.\"\"\"\n domain_stats = defaultdict(lambda: {\"queries\": 0, \"unique_subdomains\": set(), \"total_length\": 0, \"txt_queries\": 0})\n\n for conn in connections:\n domain = conn.get(\"domain\", \"\")\n if not domain:\n continue\n\n parts = domain.split(\".\")\n if len(parts) \u003c 3:\n continue\n\n base_domain = \".\".join(parts[-2:])\n subdomain = \".\".join(parts[:-2])\n\n stats = domain_stats[base_domain]\n stats[\"queries\"] += 1\n stats[\"unique_subdomains\"].add(subdomain)\n stats[\"total_length\"] += len(domain)\n\n findings = []\n for base_domain, stats in domain_stats.items():\n if stats[\"queries\"] \u003c 50:\n continue\n\n avg_len = stats[\"total_length\"] / stats[\"queries\"]\n unique_subs = len(stats[\"unique_subdomains\"])\n\n risk = 0\n indicators = []\n\n if unique_subs > 100:\n risk += 30\n indicators.append(f\"High unique subdomain count: {unique_subs}\")\n if avg_len > 40:\n risk += 25\n indicators.append(f\"Long average query length: {avg_len:.1f}\")\n if stats[\"queries\"] > 500:\n risk += 15\n indicators.append(f\"High query volume: {stats['queries']}\")\n\n # Check subdomain entropy\n for sub in list(stats[\"unique_subdomains\"])[:10]:\n ent = calculate_entropy(sub)\n if ent > 3.5:\n risk += 20\n indicators.append(f\"High subdomain entropy: {ent:.2f}\")\n break\n\n if risk >= 30:\n risk_level = \"CRITICAL\" if risk >= 70 else \"HIGH\" if risk >= 50 else \"MEDIUM\"\n findings.append({\n \"detection_type\": \"DNS_TUNNELING\",\n \"domain\": base_domain,\n \"query_count\": stats[\"queries\"],\n \"unique_subdomains\": unique_subs,\n \"avg_query_length\": round(avg_len, 1),\n \"risk_score\": risk,\n \"risk_level\": risk_level,\n \"indicators\": indicators,\n })\n\n return sorted(findings, key=lambda x: x[\"risk_score\"], reverse=True)\n\n\ndef run_hunt(input_path: str, output_dir: str) -> None:\n \"\"\"Execute C2 beaconing hunt.\"\"\"\n print(f\"[*] C2 Beaconing Hunt - {datetime.datetime.now().isoformat()}\")\n\n connections = parse_logs(input_path)\n normalized = [normalize_connection(c) for c in connections]\n print(f\"[*] Loaded {len(normalized)} connections\")\n\n beacon_findings = detect_beaconing(normalized)\n dns_findings = detect_dns_tunneling(normalized)\n all_findings = beacon_findings + dns_findings\n\n print(f\"[*] Beacon detections: {len(beacon_findings)}\")\n print(f\"[*] DNS tunnel detections: {len(dns_findings)}\")\n\n output_path = Path(output_dir)\n output_path.mkdir(parents=True, exist_ok=True)\n\n with open(output_path / \"c2_beacon_findings.json\", \"w\", encoding=\"utf-8\") as f:\n json.dump({\n \"hunt_id\": f\"TH-C2-{datetime.date.today().isoformat()}\",\n \"total_connections\": len(normalized),\n \"beacon_findings\": len(beacon_findings),\n \"dns_tunnel_findings\": len(dns_findings),\n \"findings\": all_findings,\n }, f, indent=2)\n\n with open(output_path / \"hunt_report.md\", \"w\", encoding=\"utf-8\") as f:\n f.write(f\"# C2 Beaconing Hunt Report\\n\\n\")\n f.write(f\"**Date**: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\\n\")\n f.write(f\"**Connections Analyzed**: {len(normalized)}\\n\\n\")\n f.write(\"## Beaconing Detections\\n\\n\")\n for bf in beacon_findings[:20]:\n f.write(f\"### [{bf['risk_level']}] {bf['src_ip']} -> {bf['destination']}\\n\")\n f.write(f\"- Interval: {bf['avg_interval_sec']}s (CV: {bf['coefficient_of_variation']})\\n\")\n f.write(f\"- Jitter: ~{bf['estimated_jitter_pct']}%\\n\")\n f.write(f\"- Connections: {bf['connection_count']}\\n\\n\")\n f.write(\"## DNS Tunneling Detections\\n\\n\")\n for df in dns_findings[:10]:\n f.write(f\"### [{df['risk_level']}] {df['domain']}\\n\")\n f.write(f\"- Queries: {df['query_count']}, Unique Subdomains: {df['unique_subdomains']}\\n\\n\")\n\n print(f\"[+] Results written to {output_dir}\")\n\n\ndef main():\n parser = argparse.ArgumentParser(description=\"C2 Beaconing Detection\")\n subparsers = parser.add_subparsers(dest=\"command\")\n\n hunt_p = subparsers.add_parser(\"hunt\")\n hunt_p.add_argument(\"--input\", \"-i\", required=True)\n hunt_p.add_argument(\"--output\", \"-o\", default=\"./c2_hunt_output\")\n\n subparsers.add_parser(\"queries\", help=\"Print hunting queries\")\n\n args = parser.parse_args()\n\n if args.command == \"hunt\":\n run_hunt(args.input, args.output)\n elif args.command == \"queries\":\n print(\"=== Splunk Beaconing Queries ===\\n\")\n print(\"--- HTTP/S Beacon Frequency ---\")\n print(\"\"\"index=proxy\n| bin _time span=1s\n| stats count by src_ip dest _time\n| streamstats current=f last(_time) as prev_time by src_ip dest\n| eval interval=_time-prev_time\n| stats count avg(interval) as avg stdev(interval) as sd by src_ip dest\n| eval cv=sd/avg\n| where count>50 AND cv\u003c0.20 AND avg>30\n| sort cv\"\"\")\n else:\n parser.print_help()\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":14852,"content_sha256":"0f1f04bad94b57e3ace1abd29a05cda3d228eb83083afd6f17a7bcce6aa5783a"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Hunting for Command and Control Beaconing","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to Use","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"When proactively hunting for compromised systems in the network","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"After threat intel indicates C2 frameworks targeting your industry","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"When investigating periodic outbound connections to suspicious domains","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"During incident response to identify active C2 channels","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"When DNS query logs show unusual patterns to specific domains","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Prerequisites","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Network proxy/firewall logs with full URL and timing data","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DNS query logs (passive DNS, DNS server logs, or Sysmon Event ID 22)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Zeek/Bro network connection logs or NetFlow data","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SIEM with statistical analysis capabilities (Splunk, Elastic)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Threat intelligence feeds for domain/IP reputation","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Workflow","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Identify Beaconing Characteristics","type":"text","marks":[{"type":"strong"}]},{"text":": Define what constitutes beaconing (regular intervals, small payload sizes, consistent destinations, jitter patterns).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Collect Network Telemetry","type":"text","marks":[{"type":"strong"}]},{"text":": Aggregate proxy logs, DNS queries, and connection metadata for analysis.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Apply Frequency Analysis","type":"text","marks":[{"type":"strong"}]},{"text":": Identify connections with regular intervals using statistical methods (standard deviation, coefficient of variation).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Filter Known-Good Traffic","type":"text","marks":[{"type":"strong"}]},{"text":": Exclude legitimate periodic traffic (Windows Update, AV updates, heartbeat services, NTP).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Analyze Domain/IP Reputation","type":"text","marks":[{"type":"strong"}]},{"text":": Check identified beaconing destinations against threat intel, WHOIS data, and certificate transparency logs.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Investigate Endpoint Context","type":"text","marks":[{"type":"strong"}]},{"text":": Correlate beaconing activity with process creation, user context, and file system changes on source endpoints.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Confirm and Respond","type":"text","marks":[{"type":"strong"}]},{"text":": Validate C2 activity, block communication, and initiate incident response.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Key Concepts","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Concept","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Description","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"T1071","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Application Layer Protocol (HTTP/HTTPS/DNS C2)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"T1071.001","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Web Protocols (HTTP/S beaconing)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"T1071.004","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"DNS (DNS tunneling C2)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"T1573","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Encrypted Channel","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"T1572","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Protocol Tunneling","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"T1568","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Dynamic Resolution (DGA, fast-flux)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"T1132","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Data Encoding in C2","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"T1095","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Non-Application Layer Protocol","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Beacon Interval","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Time between C2 check-ins","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Jitter","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Random variation in beacon interval","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"DGA","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Domain Generation Algorithm","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fast-Flux","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Rapidly changing DNS resolution","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Tools & Systems","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Tool","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Purpose","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"RITA (Real Intelligence Threat Analytics)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Automated beacon detection in Zeek logs","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Splunk","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Statistical beacon analysis with SPL","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Elastic Security","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ML-based anomaly detection for beaconing","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Zeek/Bro","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Network connection metadata collection","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Suricata","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Network IDS with JA3/JA4 fingerprinting","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"VirusTotal","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Domain and IP reputation checking","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"PassiveDNS","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Historical DNS resolution data","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Flare","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"C2 profile detection","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Common Scenarios","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Cobalt Strike Beacon","type":"text","marks":[{"type":"strong"}]},{"text":": HTTP/HTTPS beaconing with configurable sleep time and jitter to malleable C2 profiles.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DNS Tunneling C2","type":"text","marks":[{"type":"strong"}]},{"text":": Data exfiltration and command receipt via encoded DNS TXT/CNAME queries to attacker-controlled domains.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Sliver C2 over HTTPS","type":"text","marks":[{"type":"strong"}]},{"text":": Modern C2 framework using HTTPS with configurable beacon intervals and domain fronting.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DGA-based C2","type":"text","marks":[{"type":"strong"}]},{"text":": Malware generating random domains daily, with adversary registering upcoming domains for C2.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Legitimate Service Abuse","type":"text","marks":[{"type":"strong"}]},{"text":": C2 over legitimate cloud services (Azure, AWS, Slack, Discord, Telegram).","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Output Format","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Hunt ID: TH-C2-[DATE]-[SEQ]\nSource IP: [Internal IP]\nSource Host: [Hostname]\nDestination: [Domain/IP]\nProtocol: [HTTP/HTTPS/DNS/Custom]\nBeacon Interval: [Average seconds]\nJitter: [Percentage]\nConnection Count: [Total connections]\nData Volume: [Bytes sent/received]\nFirst Seen: [Timestamp]\nLast Seen: [Timestamp]\nDomain Age: [Days]\nTI Match: [Yes/No - source]\nRisk Level: [Critical/High/Medium/Low]","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"hunting-for-command-and-control-beaconing","tags":["threat-hunting","mitre-attack","c2","beaconing","network-analysis","proactive-detection"],"author":"@skillopedia","domain":"cybersecurity","source":{"stars":13207,"repo_name":"anthropic-cybersecurity-skills","origin_url":"https://github.com/mukul975/anthropic-cybersecurity-skills/blob/HEAD/skills/hunting-for-command-and-control-beaconing/SKILL.md","repo_owner":"mukul975","body_sha256":"3a139faf3dadad4aa21c3e9348a0927ecd40aff7f102a7f49fb2f7b9c615b34f","cluster_key":"88464f23c8ac0cc41e12aeaef2515ee75fa813143184e8e4f815e1fae24b08be","clean_bundle":{"format":"clean-skill-bundle-v1","source":"mukul975/anthropic-cybersecurity-skills/skills/hunting-for-command-and-control-beaconing/SKILL.md","attachments":[{"id":"1bc82440-03c5-5d8c-9725-4cb6586771d3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1bc82440-03c5-5d8c-9725-4cb6586771d3/attachment.md","path":"assets/template.md","size":996,"sha256":"12a3ff1bb846f4688dab6595814519cc64151ee147cd7f01c0d9c926335f306f","contentType":"text/markdown; charset=utf-8"},{"id":"af584e2a-d3ab-5189-9758-2d9e5e045c48","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/af584e2a-d3ab-5189-9758-2d9e5e045c48/attachment.md","path":"references/api-reference.md","size":2222,"sha256":"a02975a93bd5b392dd5b034431eb9a4d30b1d6bc047818af30a4dfea119678c6","contentType":"text/markdown; charset=utf-8"},{"id":"9036fffc-9f63-5db6-9eb8-46afc45485cb","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9036fffc-9f63-5db6-9eb8-46afc45485cb/attachment.md","path":"references/standards.md","size":3224,"sha256":"1ba2cba80f6bc83c4a9b7b678ef2b41bfc945ebc2a00b45e3b10a52e0268ef0a","contentType":"text/markdown; charset=utf-8"},{"id":"aaf1545b-1946-5d76-b643-e324f645f157","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/aaf1545b-1946-5d76-b643-e324f645f157/attachment.md","path":"references/workflows.md","size":4360,"sha256":"5126d21dd3c3b65e4deea3ed94b26969b56e5762335cc52693e2cb51fb2e87b7","contentType":"text/markdown; charset=utf-8"},{"id":"49f3949b-db82-5d51-a565-637e64c2a707","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/49f3949b-db82-5d51-a565-637e64c2a707/attachment.py","path":"scripts/agent.py","size":6213,"sha256":"9b749f6dc3d7710c44293d607cf56fd3b6b10dd64bcb925c182d602ab78a6407","contentType":"text/x-python; charset=utf-8"},{"id":"c42f0a76-a38a-50a2-9a07-062b1bf8a978","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c42f0a76-a38a-50a2-9a07-062b1bf8a978/attachment.py","path":"scripts/process.py","size":14852,"sha256":"0f1f04bad94b57e3ace1abd29a05cda3d228eb83083afd6f17a7bcce6aa5783a","contentType":"text/x-python; charset=utf-8"}],"bundle_sha256":"9d85b9e5fbd5a9d9f2e5b276fb6aa410098a729ebedb76c30d81f608d6920c31","attachment_count":6,"text_attachments":6,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":2,"skill_md_path":"skills/hunting-for-command-and-control-beaconing/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"security","category_label":"Security"},"exact_dupes_collapsed_into_this":1},"license":"Apache-2.0","version":"v1","category":"security","nist_csf":["DE.CM-01","DE.AE-02","DE.AE-07","ID.RA-05"],"subdomain":"threat-hunting","import_tag":"clean-skills-v1","description":"Detect C2 beaconing patterns in network traffic using frequency analysis, jitter detection, and domain reputation to identify compromised endpoints communicating with adversary infrastructure.","d3fend_techniques":["File Metadata Consistency Validation","Certificate Analysis","Application Protocol Command Analysis","Content Format Conversion","File Content Analysis"]}},"renderedAt":1782981592718}

Hunting for Command and Control Beaconing When to Use - When proactively hunting for compromised systems in the network - After threat intel indicates C2 frameworks targeting your industry - When investigating periodic outbound connections to suspicious domains - During incident response to identify active C2 channels - When DNS query logs show unusual patterns to specific domains Prerequisites - Network proxy/firewall logs with full URL and timing data - DNS query logs (passive DNS, DNS server logs, or Sysmon Event ID 22) - Zeek/Bro network connection logs or NetFlow data - SIEM with statist…