mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-10 13:14:55 +03:00
c47eed6a64
- Fix 25 shell=True subprocess calls with list-based commands - Fix 49 verify=False in defensive skills (env-var override) - Add timeout to 231 HTTP/subprocess/socket calls - Fix 6 SQL injection patterns with whitelist validation - Replace 8 __import__() with standard imports - Remove 701 unused imports across 442 files - Add authorized-testing disclaimers to all offensive skills - Complete 11 incomplete skill directories - Expand 10 stub SKILL.md files with full content - Fix 2 YAML parse errors in frontmatter - Fix 5 pre-existing syntax errors - Convert 22 hardcoded paths/ports to environment variables - Back up 21 redundant skill pairs to .bak - Fix 2 global declaration errors - 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE) - 0 compile errors across all 724 agent.py files
254 lines
9.9 KiB
Python
254 lines
9.9 KiB
Python
#!/usr/bin/env python3
|
||
"""Campaign attribution analysis agent using Diamond Model and ACH methodology.
|
||
|
||
Evaluates attribution evidence including infrastructure overlaps, TTP consistency,
|
||
malware code similarity, timing patterns, and language artifacts.
|
||
"""
|
||
|
||
import json
|
||
import re
|
||
from collections import defaultdict
|
||
from datetime import datetime
|
||
|
||
|
||
DIAMOND_DIMENSIONS = {
|
||
"adversary": "Threat actor identity, group attribution",
|
||
"capability": "Malware, exploits, tools used",
|
||
"infrastructure": "C2 servers, domains, IP addresses",
|
||
"victim": "Targeted sectors, regions, organizations",
|
||
}
|
||
|
||
EVIDENCE_WEIGHTS = {
|
||
"infrastructure_overlap": 0.25,
|
||
"ttp_consistency": 0.30,
|
||
"malware_code_similarity": 0.25,
|
||
"timing_pattern": 0.10,
|
||
"language_artifact": 0.10,
|
||
}
|
||
|
||
CONFIDENCE_LEVELS = {
|
||
(0.8, 1.0): "HIGH - Strong attribution confidence",
|
||
(0.5, 0.8): "MEDIUM - Moderate attribution, further analysis recommended",
|
||
(0.2, 0.5): "LOW - Weak attribution, insufficient evidence",
|
||
(0.0, 0.2): "NEGLIGIBLE - No meaningful attribution possible",
|
||
}
|
||
|
||
|
||
def diamond_model_analysis(adversary=None, capability=None, infrastructure=None, victim=None):
|
||
"""Structure evidence using the Diamond Model of Intrusion Analysis."""
|
||
model = {
|
||
"adversary": {
|
||
"identified": adversary is not None,
|
||
"details": adversary or "Unknown",
|
||
},
|
||
"capability": {
|
||
"tools": capability.get("tools", []) if capability else [],
|
||
"exploits": capability.get("exploits", []) if capability else [],
|
||
"malware": capability.get("malware", []) if capability else [],
|
||
},
|
||
"infrastructure": {
|
||
"c2_servers": infrastructure.get("c2", []) if infrastructure else [],
|
||
"domains": infrastructure.get("domains", []) if infrastructure else [],
|
||
"ip_addresses": infrastructure.get("ips", []) if infrastructure else [],
|
||
},
|
||
"victim": {
|
||
"sectors": victim.get("sectors", []) if victim else [],
|
||
"regions": victim.get("regions", []) if victim else [],
|
||
},
|
||
"pivot_opportunities": [],
|
||
}
|
||
if infrastructure and infrastructure.get("c2"):
|
||
model["pivot_opportunities"].append("Pivot from C2 infrastructure to related campaigns")
|
||
if capability and capability.get("malware"):
|
||
model["pivot_opportunities"].append("Pivot from malware samples to shared infrastructure")
|
||
return model
|
||
|
||
|
||
def evaluate_infrastructure_overlap(campaign_infra, known_actor_infra):
|
||
"""Score infrastructure overlap between campaign and known actor."""
|
||
campaign_set = set(campaign_infra)
|
||
known_set = set(known_actor_infra)
|
||
if not campaign_set or not known_set:
|
||
return 0.0, []
|
||
overlap = campaign_set & known_set
|
||
score = len(overlap) / max(len(campaign_set), len(known_set))
|
||
return round(score, 4), sorted(overlap)
|
||
|
||
|
||
def evaluate_ttp_consistency(campaign_ttps, actor_ttps):
|
||
"""Score TTP consistency using MITRE ATT&CK technique overlap."""
|
||
campaign_set = set(campaign_ttps)
|
||
actor_set = set(actor_ttps)
|
||
if not campaign_set or not actor_set:
|
||
return 0.0, []
|
||
overlap = campaign_set & actor_set
|
||
jaccard = len(overlap) / len(campaign_set | actor_set)
|
||
return round(jaccard, 4), sorted(overlap)
|
||
|
||
|
||
def evaluate_malware_similarity(sample_features, known_features):
|
||
"""Score malware code similarity based on feature comparison."""
|
||
if not sample_features or not known_features:
|
||
return 0.0
|
||
matches = 0
|
||
total = max(len(sample_features), len(known_features))
|
||
for feature in sample_features:
|
||
if feature in known_features:
|
||
matches += 1
|
||
return round(matches / total, 4) if total > 0 else 0.0
|
||
|
||
|
||
def evaluate_timing_pattern(campaign_timestamps, actor_timezone_offset=None):
|
||
"""Analyze operational timing to infer timezone/working hours."""
|
||
if not campaign_timestamps:
|
||
return {"score": 0.0, "working_hours": None, "timezone_guess": None}
|
||
hours = []
|
||
for ts in campaign_timestamps:
|
||
try:
|
||
if isinstance(ts, str):
|
||
dt = datetime.fromisoformat(ts.replace("Z", "+00:00"))
|
||
else:
|
||
dt = ts
|
||
adjusted = dt.hour + (actor_timezone_offset or 0)
|
||
hours.append(adjusted % 24)
|
||
except (ValueError, TypeError):
|
||
continue
|
||
if not hours:
|
||
return {"score": 0.0}
|
||
work_hours = sum(1 for h in hours if 8 <= h <= 18)
|
||
work_ratio = work_hours / len(hours)
|
||
avg_hour = sum(hours) / len(hours)
|
||
return {
|
||
"score": round(work_ratio, 4),
|
||
"average_hour_utc": round(avg_hour, 1),
|
||
"work_hour_ratio": round(work_ratio, 4),
|
||
"sample_size": len(hours),
|
||
}
|
||
|
||
|
||
def evaluate_language_artifacts(strings_list):
|
||
"""Detect language artifacts in malware strings or documents."""
|
||
language_indicators = {
|
||
"Russian": [r"[а-яА-Я]{3,}", r"codepage.*1251", r"locale.*ru"],
|
||
"Chinese": [r"[\u4e00-\u9fff]{2,}", r"codepage.*936", r"GB2312"],
|
||
"Korean": [r"[\uac00-\ud7af]{2,}", r"codepage.*949", r"EUC-KR"],
|
||
"Farsi": [r"[\u0600-\u06ff]{3,}", r"codepage.*1256"],
|
||
"English": [r"\b(the|and|for|with)\b"],
|
||
}
|
||
detections = defaultdict(int)
|
||
for s in strings_list:
|
||
for lang, patterns in language_indicators.items():
|
||
for pattern in patterns:
|
||
if re.search(pattern, s, re.IGNORECASE):
|
||
detections[lang] += 1
|
||
total = sum(detections.values()) or 1
|
||
scored = {lang: round(count / total, 4) for lang, count in detections.items()}
|
||
return scored
|
||
|
||
|
||
def ach_analysis(hypotheses, evidence_items):
|
||
"""Analysis of Competing Hypotheses (ACH) for attribution."""
|
||
matrix = {}
|
||
for hyp in hypotheses:
|
||
hyp_name = hyp["name"]
|
||
matrix[hyp_name] = {"consistent": 0, "inconsistent": 0, "neutral": 0, "score": 0}
|
||
for evidence in evidence_items:
|
||
ev_name = evidence["name"]
|
||
consistency = evidence.get("hypotheses", {}).get(hyp_name, "neutral")
|
||
if consistency == "consistent":
|
||
matrix[hyp_name]["consistent"] += evidence.get("weight", 1)
|
||
elif consistency == "inconsistent":
|
||
matrix[hyp_name]["inconsistent"] += evidence.get("weight", 1)
|
||
else:
|
||
matrix[hyp_name]["neutral"] += evidence.get("weight", 1)
|
||
c = matrix[hyp_name]["consistent"]
|
||
i = matrix[hyp_name]["inconsistent"]
|
||
matrix[hyp_name]["score"] = round((c - i) / (c + i + 0.01), 4)
|
||
return matrix
|
||
|
||
|
||
def compute_attribution_score(scores):
|
||
"""Compute weighted attribution confidence score."""
|
||
total = 0.0
|
||
for evidence_type, weight in EVIDENCE_WEIGHTS.items():
|
||
score = scores.get(evidence_type, 0.0)
|
||
total += score * weight
|
||
confidence = "UNKNOWN"
|
||
for (low, high), label in CONFIDENCE_LEVELS.items():
|
||
if low <= total < high:
|
||
confidence = label
|
||
break
|
||
return round(total, 4), confidence
|
||
|
||
|
||
def generate_attribution_report(campaign_name, candidate_actor, evidence):
|
||
"""Generate structured attribution assessment report."""
|
||
scores = {}
|
||
details = {}
|
||
|
||
infra_score, infra_overlap = evaluate_infrastructure_overlap(
|
||
evidence.get("campaign_infra", []), evidence.get("actor_infra", []))
|
||
scores["infrastructure_overlap"] = infra_score
|
||
details["infrastructure_overlap"] = infra_overlap
|
||
|
||
ttp_score, ttp_overlap = evaluate_ttp_consistency(
|
||
evidence.get("campaign_ttps", []), evidence.get("actor_ttps", []))
|
||
scores["ttp_consistency"] = ttp_score
|
||
details["ttp_consistency"] = ttp_overlap
|
||
|
||
malware_score = evaluate_malware_similarity(
|
||
evidence.get("sample_features", []), evidence.get("known_features", []))
|
||
scores["malware_code_similarity"] = malware_score
|
||
|
||
timing = evaluate_timing_pattern(
|
||
evidence.get("timestamps", []), evidence.get("tz_offset"))
|
||
scores["timing_pattern"] = timing.get("score", 0.0)
|
||
details["timing"] = timing
|
||
|
||
lang = evaluate_language_artifacts(evidence.get("strings", []))
|
||
scores["language_artifact"] = max(lang.values()) if lang else 0.0
|
||
details["language_artifacts"] = lang
|
||
|
||
total_score, confidence = compute_attribution_score(scores)
|
||
|
||
return {
|
||
"campaign": campaign_name,
|
||
"candidate_actor": candidate_actor,
|
||
"attribution_score": total_score,
|
||
"confidence_level": confidence,
|
||
"evidence_scores": scores,
|
||
"evidence_details": details,
|
||
}
|
||
|
||
|
||
if __name__ == "__main__":
|
||
print("=" * 60)
|
||
print("Campaign Attribution Evidence Analysis Agent")
|
||
print("Diamond Model, ACH, TTP/infrastructure/malware scoring")
|
||
print("=" * 60)
|
||
|
||
demo_evidence = {
|
||
"campaign_infra": ["185.220.101.1", "evil-domain.com", "c2.attacker.net"],
|
||
"actor_infra": ["185.220.101.1", "c2.attacker.net", "other-domain.org"],
|
||
"campaign_ttps": ["T1566.001", "T1059.001", "T1053.005", "T1071.001", "T1041"],
|
||
"actor_ttps": ["T1566.001", "T1059.001", "T1053.005", "T1071.001", "T1021.001", "T1003.001"],
|
||
"sample_features": ["xor_0x55", "mutex_Global\\QWE", "ua_Mozilla5", "rc4_key"],
|
||
"known_features": ["xor_0x55", "mutex_Global\\QWE", "ua_Mozilla5", "aes_cbc"],
|
||
"timestamps": ["2024-03-15T06:30:00Z", "2024-03-15T07:15:00Z",
|
||
"2024-03-16T08:00:00Z", "2024-03-16T09:45:00Z"],
|
||
"tz_offset": 3,
|
||
"strings": ["Привет мир", "connect to server", "upload file"],
|
||
}
|
||
|
||
report = generate_attribution_report("Operation DarkShadow", "APT29", demo_evidence)
|
||
|
||
print(f"\n[*] Campaign: {report['campaign']}")
|
||
print(f"[*] Candidate: {report['candidate_actor']}")
|
||
print(f"[*] Attribution Score: {report['attribution_score']}")
|
||
print(f"[*] Confidence: {report['confidence_level']}")
|
||
print("\n--- Evidence Scores ---")
|
||
for ev, score in report["evidence_scores"].items():
|
||
weight = EVIDENCE_WEIGHTS.get(ev, 0)
|
||
print(f" {ev:30s} score={score:.4f} weight={weight}")
|
||
print(f"\n[*] Full report:\n{json.dumps(report, indent=2, default=str)}")
|