Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-SkillsFramework mappings
MITRE ATT&CK
NIST CSF 2.0
MITRE D3FEND
Application Protocol Command Analysis on the official MITRE D3FEND siteCertificate Analysis on the official MITRE D3FEND siteContent Format Conversion on the official MITRE D3FEND siteFile Content Analysis on the official MITRE D3FEND siteFile Metadata Consistency Validation on the official MITRE D3FEND site
When to Use
- When proactively hunting for indicators of hunting for unusual network connections in the environment
- After threat intelligence indicates active campaigns using these techniques
- During incident response to scope compromise related to these techniques
- When EDR or SIEM alerts trigger on related indicators
- During periodic security assessments and purple team exercises
Prerequisites
- EDR platform with process and network telemetry (CrowdStrike, MDE, SentinelOne)
- SIEM with relevant log data ingested (Splunk, Elastic, Sentinel)
- Sysmon deployed with comprehensive configuration
- Windows Security Event Log forwarding enabled
- Threat intelligence feeds for IOC correlation
Workflow
- Formulate Hypothesis: Define a testable hypothesis based on threat intelligence or ATT&CK gap analysis.
- Identify Data Sources: Determine which logs and telemetry are needed to validate or refute the hypothesis.
- Execute Queries: Run detection queries against SIEM and EDR platforms to collect relevant events.
- Analyze Results: Examine query results for anomalies, correlating across multiple data sources.
- Validate Findings: Distinguish true positives from false positives through contextual analysis.
- Correlate Activity: Link findings to broader attack chains and threat actor TTPs.
- Document and Report: Record findings, update detection rules, and recommend response actions.
Key Concepts
| Concept | Description |
|---|---|
| T1071 | Application Layer Protocol |
| T1095 | Non-Application Layer Protocol |
| T1571 | Non-Standard Port |
Tools & Systems
| Tool | Purpose |
|---|---|
| CrowdStrike Falcon | EDR telemetry and threat detection |
| Microsoft Defender for Endpoint | Advanced hunting with KQL |
| Splunk Enterprise | SIEM log analysis with SPL queries |
| Elastic Security | Detection rules and investigation timeline |
| Sysmon | Detailed Windows event monitoring |
| Velociraptor | Endpoint artifact collection and hunting |
| Sigma Rules | Cross-platform detection rule format |
Common Scenarios
- Scenario 1: Backdoor communicating to C2 on non-standard port
- Scenario 2: Data exfiltration over DNS to attacker nameserver
- Scenario 3: Compromised host scanning internal network
- Scenario 4: Cryptominer connecting to mining pool
Output Format
Hunt ID: TH-HUNTIN-[DATE]-[SEQ]
Technique: T1071
Host: [Hostname]
User: [Account context]
Evidence: [Log entries, process trees, network data]
Risk Level: [Critical/High/Medium/Low]
Confidence: [High/Medium/Low]
Recommended Action: [Containment, investigation, monitoring]Source materials
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md1.9 KB
API Reference: Hunting for Unusual Network Connections
Connection Analysis Indicators
| Indicator | Threshold | Severity |
|---|---|---|
| Known bad port (4444, 31337) | Any connection | CRITICAL |
| Non-standard port | Not in common set | MEDIUM |
| Rare destination (< 3 conns) | Unique in environment | HIGH |
| Long connection (> 1hr) | Duration > 3600s | HIGH |
| Periodic beaconing (CV < 0.3) | Low interval variance | CRITICAL |
Splunk SPL - Rare Destinations
index=firewall action=allowed
| stats dc(src_ip) as src_count count by dest_ip dest_port
| where src_count == 1 AND count < 5
| sort -count
| table dest_ip dest_port count src_countKQL - Non-Standard Ports
DeviceNetworkEvents
| where RemotePort !in (80, 443, 53, 22, 25, 8080)
| summarize ConnectionCount=count(), dcount(DeviceId) by RemoteIP, RemotePort
| where ConnectionCount < 5
| sort by ConnectionCount ascZeek conn.log Analysis
from zat.log_to_dataframe import LogToDataFrame
df = LogToDataFrame().create_dataframe("conn.log")
# Filter rare external destinations
external = df[~df["id.resp_h"].str.startswith(("10.", "172.16.", "192.168."))]
rare = external.groupby("id.resp_h").size().reset_index(name="count")
rare = rare[rare["count"] < 3]Beaconing Detection
import numpy as np
intervals = np.diff(sorted_timestamps)
cv = np.std(intervals) / np.mean(intervals)
# CV < 0.3 = high periodicity (likely beacon)Sysmon Event ID 3 (Network Connection)
<EventData>
<Data Name="Image">C:\Windows\System32\svchost.exe</Data>
<Data Name="DestinationIp">203.0.113.50</Data>
<Data Name="DestinationPort">4444</Data>
</EventData>References
standards.md1.5 KB
Standards and References - Hunting For Unusual Network Connections
MITRE ATT&CK Mappings
| Technique | Name | Description |
|---|---|---|
| T1071 | Application Layer Protocol | See attack.mitre.org/techniques/T1071 |
| T1095 | Non-Application Layer Protocol | See attack.mitre.org/techniques/T1095 |
| T1571 | Non-Standard Port | See attack.mitre.org/techniques/T1571 |
Detection Data Sources
| Source | Event ID | Purpose |
|---|---|---|
| Sysmon | 1 | Process creation with command line |
| Sysmon | 3 | Network connection initiated |
| Sysmon | 7 | Image loaded (DLL) |
| Sysmon | 10 | Process access (LSASS) |
| Sysmon | 11 | File creation |
| Sysmon | 12/13 | Registry create/set |
| Sysmon | 22 | DNS query |
| Sysmon | 25 | Process tampering |
| Windows Security | 4624 | Successful logon |
| Windows Security | 4625 | Failed logon |
| Windows Security | 4648 | Explicit credential logon |
| Windows Security | 4672 | Special privileges assigned |
| Windows Security | 4688 | Process creation |
| Windows Security | 4697 | Service installed |
| Windows Security | 4698 | Scheduled task created |
| Windows Security | 4769 | Kerberos TGS requested |
| Windows Security | 5140 | Network share accessed |
References
- MITRE ATT&CK Framework: https://attack.mitre.org/
- Sigma Detection Rules: https://github.com/SigmaHQ/sigma
- LOLBAS Project: https://lolbas-project.github.io/
- Atomic Red Team Tests: https://github.com/redcanaryco/atomic-red-team
- Red Canary Threat Detection Report
- SANS Threat Hunting Summit Resources
workflows.md2.9 KB
Detailed Hunting Workflow - Hunting For Unusual Network Connections
Phase 1: Data Collection and Querying
Splunk SPL Query
index=sysmon EventCode=3
| where NOT match(DestinationIp, "^(10\\.|172\\.(1[6-9]|2[0-9]|3[01])\\.|192\\.168\\.)")
| stats count dc(DestinationIp) as unique_ips values(DestinationPort) as ports by Image Computer
| where count > 50 OR unique_ips > 10
| sort -countKQL Query (Microsoft Defender for Endpoint)
DeviceNetworkEvents
| where RemoteIPType == "Public"
| summarize ConnectionCount=count(), UniqueIPs=dcount(RemoteIP), Ports=make_set(RemotePort) by InitiatingProcessFileName, DeviceName
| where ConnectionCount > 50 or UniqueIPs > 10Phase 2: Baseline and Anomaly Detection
Step 2.1 - Establish Normal Behavior Baseline
- Collect 30 days of historical data for the targeted technique
- Document expected patterns, frequencies, and legitimate use cases
- Identify known false positive sources and document exceptions
- Build statistical baseline (mean, standard deviation) for key metrics
Step 2.2 - Identify Anomalies
- Compare current activity against the 30-day baseline
- Flag events exceeding 3 standard deviations from normal
- Prioritize anomalies by risk score and potential business impact
- Cross-reference with threat intelligence for known IOCs
Phase 3: Investigation and Correlation
Step 3.1 - Deep Dive Analysis
- For each anomaly, collect full process tree context
- Correlate with network activity, file operations, and authentication events
- Check binary signatures, file hashes, and certificate validity
- Review user account context and access patterns
Step 3.2 - Attack Chain Reconstruction
- Map findings to MITRE ATT&CK kill chain stages
- Identify initial access vector if applicable
- Trace lateral movement and privilege escalation paths
- Determine data access and potential exfiltration
Phase 4: Validation and Response
Step 4.1 - True/False Positive Determination
- Verify findings with system owners and IT operations
- Check change management records for authorized activities
- Validate user context (authorized actions vs. compromised account)
- Document determination rationale for each finding
Step 4.2 - Response Actions
- For confirmed threats: initiate incident response procedures
- For detection gaps: create or update detection rules
- For false positives: tune existing rules and update exclusions
- Update threat hunting playbook with lessons learned
Phase 5: Documentation and Reporting
Step 5.1 - Hunt Report
- Summarize hypothesis, methodology, and findings
- Include all queries executed and their results
- Document IOCs discovered and detection rules created
- Provide recommendations for security improvements
Step 5.2 - Knowledge Base Update
- Add findings to threat intelligence platform
- Update MITRE ATT&CK coverage heatmap
- Share detection rules via Sigma format
- Schedule follow-up hunts for related techniques
Scripts 2
agent.py7.0 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for hunting unusual network connections from endpoint and firewall logs."""
import json
import argparse
from datetime import datetime
from collections import defaultdict, Counter
COMMON_PORTS = {80, 443, 53, 22, 25, 110, 143, 993, 995, 587, 8080, 8443, 3389}
KNOWN_BAD_PORTS = {4444, 5555, 1234, 9999, 31337, 6666, 6667, 8888, 12345}
PRIVATE_RANGES = [
(0x0A000000, 0x0AFFFFFF), # 10.0.0.0/8
(0xAC100000, 0xAC1FFFFF), # 172.16.0.0/12
(0xC0A80000, 0xC0A8FFFF), # 192.168.0.0/16
]
def ip_to_int(ip):
"""Convert dotted IP to integer."""
parts = ip.split(".")
if len(parts) != 4:
return 0
try:
return (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])
except ValueError:
return 0
def is_private(ip):
"""Check if IP is in private RFC1918 range."""
val = ip_to_int(ip)
return any(start <= val <= end for start, end in PRIVATE_RANGES)
def load_connection_logs(log_path):
"""Load network connection logs from JSON lines."""
entries = []
with open(log_path) as f:
for line in f:
try:
entries.append(json.loads(line))
except json.JSONDecodeError:
continue
return entries
def detect_non_standard_ports(connections):
"""Find connections to unusual destination ports."""
findings = []
for conn in connections:
dst_port = int(conn.get("dest_port", conn.get("dst_port", 0)))
if dst_port in KNOWN_BAD_PORTS:
findings.append({
"src_ip": conn.get("src_ip", conn.get("source_ip", "")),
"dst_ip": conn.get("dst_ip", conn.get("dest_ip", "")),
"dst_port": dst_port,
"process": conn.get("process", conn.get("image", "")),
"severity": "CRITICAL",
"reason": "known_bad_port",
})
elif dst_port not in COMMON_PORTS and dst_port > 0:
findings.append({
"src_ip": conn.get("src_ip", conn.get("source_ip", "")),
"dst_ip": conn.get("dst_ip", conn.get("dest_ip", "")),
"dst_port": dst_port,
"process": conn.get("process", conn.get("image", "")),
"severity": "MEDIUM",
"reason": "non_standard_port",
})
return findings
def detect_rare_destinations(connections, threshold=3):
"""Find rarely contacted external destinations."""
dest_counts = Counter()
dest_conns = defaultdict(list)
for conn in connections:
dst = conn.get("dst_ip", conn.get("dest_ip", ""))
if dst and not is_private(dst):
dest_counts[dst] += 1
dest_conns[dst].append(conn)
findings = []
for dst, count in dest_counts.items():
if count <= threshold:
sample = dest_conns[dst][0]
findings.append({
"dst_ip": dst,
"connection_count": count,
"src_ip": sample.get("src_ip", sample.get("source_ip", "")),
"process": sample.get("process", sample.get("image", "")),
"severity": "HIGH",
"reason": "rare_destination",
})
return sorted(findings, key=lambda x: x["connection_count"])
def detect_long_connections(connections, duration_threshold=3600):
"""Find unusually long-lived connections (potential C2)."""
findings = []
for conn in connections:
duration = conn.get("duration", conn.get("connection_duration", 0))
try:
duration = float(duration)
except (TypeError, ValueError):
continue
if duration > duration_threshold:
findings.append({
"src_ip": conn.get("src_ip", conn.get("source_ip", "")),
"dst_ip": conn.get("dst_ip", conn.get("dest_ip", "")),
"dst_port": conn.get("dest_port", conn.get("dst_port", "")),
"duration_seconds": duration,
"process": conn.get("process", conn.get("image", "")),
"severity": "HIGH",
"reason": "long_duration_connection",
})
return sorted(findings, key=lambda x: x["duration_seconds"], reverse=True)
def detect_high_frequency_beaconing(connections, interval_threshold=60):
"""Detect periodic connections suggestive of beaconing."""
by_dest = defaultdict(list)
for conn in connections:
dst = conn.get("dst_ip", conn.get("dest_ip", ""))
ts = conn.get("timestamp", conn.get("ts", ""))
if dst and ts:
try:
t = datetime.fromisoformat(str(ts).replace("Z", "+00:00"))
by_dest[dst].append(t)
except (ValueError, TypeError):
continue
findings = []
for dst, times in by_dest.items():
if len(times) < 5:
continue
times.sort()
intervals = [(times[i+1] - times[i]).total_seconds() for i in range(len(times)-1)]
avg = sum(intervals) / len(intervals)
if avg < 1:
continue
std = (sum((x - avg)**2 for x in intervals) / len(intervals)) ** 0.5
cv = std / avg if avg > 0 else 999
if cv < 0.3 and avg < interval_threshold:
findings.append({
"dst_ip": dst, "connection_count": len(times),
"avg_interval_sec": round(avg, 2), "cv": round(cv, 3),
"severity": "CRITICAL", "reason": "periodic_beaconing",
})
return findings
def main():
parser = argparse.ArgumentParser(description="Unusual Network Connection Hunter")
parser.add_argument("--log", required=True, help="JSON lines connection log")
parser.add_argument("--output", default="unusual_network_hunt_report.json")
parser.add_argument("--action", choices=[
"ports", "rare", "long", "beacon", "full_analysis"
], default="full_analysis")
args = parser.parse_args()
conns = load_connection_logs(args.log)
report = {"generated_at": datetime.utcnow().isoformat(), "total_connections": len(conns),
"findings": {}}
print(f"[+] Loaded {len(conns)} connections")
if args.action in ("ports", "full_analysis"):
f = detect_non_standard_ports(conns)
report["findings"]["non_standard_ports"] = f
print(f"[+] Non-standard port connections: {len(f)}")
if args.action in ("rare", "full_analysis"):
f = detect_rare_destinations(conns)
report["findings"]["rare_destinations"] = f
print(f"[+] Rare destinations: {len(f)}")
if args.action in ("long", "full_analysis"):
f = detect_long_connections(conns)
report["findings"]["long_connections"] = f
print(f"[+] Long-lived connections: {len(f)}")
if args.action in ("beacon", "full_analysis"):
f = detect_high_frequency_beaconing(conns)
report["findings"]["beaconing"] = f
print(f"[+] Beaconing patterns: {len(f)}")
with open(args.output, "w") as fout:
json.dump(report, fout, indent=2, default=str)
print(f"[+] Report saved to {args.output}")
if __name__ == "__main__":
main()
process.py3.5 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Unusual Network Connections Detection - Analyzes logs for T1071 indicators."""
import json, csv, argparse, datetime, re
from collections import defaultdict
from pathlib import Path
DETECTION_PATTERNS = [
r'port (4444|5555|6666|8888|9090|31337|50050)',
]
def parse_logs(path):
p = Path(path)
if p.suffix == ".json":
with open(p, encoding="utf-8") as f:
data = json.load(f)
return data if isinstance(data, list) else data.get("events", [])
elif p.suffix == ".csv":
with open(p, encoding="utf-8-sig") as f:
return [dict(r) for r in csv.DictReader(f)]
return []
def analyze_event(event):
cmd = event.get("CommandLine", event.get("command_line", event.get("ProcessCommandLine", "")))
content = event.get("Task_Content", event.get("Parameters", event.get("RawEventData", "")))
search_text = f"{cmd} {content}"
risk = 0
indicators = []
for pattern in DETECTION_PATTERNS:
if re.search(pattern, search_text, re.IGNORECASE):
risk += 25
indicators.append(f"Pattern match: {pattern}")
if not indicators:
return None
risk = min(risk, 100)
return {
"technique": "T1071",
"command_line": cmd[:500] if cmd else content[:500],
"hostname": event.get("Computer", event.get("DeviceName", event.get("hostname", "unknown"))),
"user": event.get("User", event.get("AccountName", event.get("UserId", "unknown"))),
"timestamp": event.get("_time", event.get("timestamp", event.get("UtcTime", event.get("Timestamp", "")))),
"risk_score": risk,
"risk_level": "CRITICAL" if risk >= 75 else "HIGH" if risk >= 50 else "MEDIUM" if risk >= 25 else "LOW",
"indicators": indicators,
}
def run_hunt(input_path, output_dir):
print(f"[*] Unusual Network Connections Hunt - {datetime.datetime.now().isoformat()}")
events = parse_logs(input_path)
findings = [f for f in (analyze_event(e) for e in events) if f]
Path(output_dir).mkdir(parents=True, exist_ok=True)
slug = "hunting_for_unusual_"
with open(Path(output_dir) / f"{slug}_findings.json", "w", encoding="utf-8") as f:
json.dump({"hunt_id": f"TH-{datetime.date.today()}", "total_events": len(events), "findings": findings}, f, indent=2)
with open(Path(output_dir) / "hunt_report.md", "w", encoding="utf-8") as f:
f.write(f"# Unusual Network Connections Hunt Report\n\n")
f.write(f"**Date**: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"**Findings**: {len(findings)}\n\n")
for finding in sorted(findings, key=lambda x: x["risk_score"], reverse=True)[:20]:
f.write(f"### [{finding['risk_level']}] {finding['technique']}\n")
f.write(f"- **Host**: {finding['hostname']}\n")
f.write(f"- **Indicators**: {', '.join(finding['indicators'])}\n\n")
print(f"[+] {len(findings)} findings written to {output_dir}")
def main():
p = argparse.ArgumentParser(description="Unusual Network Connections Detection")
sp = p.add_subparsers(dest="cmd")
h = sp.add_parser("hunt"); h.add_argument("--input", "-i", required=True); h.add_argument("--output", "-o", default="./hunting_for_unu_output")
sp.add_parser("queries")
args = p.parse_args()
if args.cmd == "hunt": run_hunt(args.input, args.output)
elif args.cmd == "queries":
print("=== Detection Queries ===")
print("See references/workflows.md for platform-specific queries")
else: p.print_help()
if __name__ == "__main__": main()
Assets 1
template.mdtext/markdown · 2.6 KBKeep exploring