Files
Anthropic-Cybersecurity-Skills/skills/analyzing-campaign-attribution-evidence/scripts/agent.py
T
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- 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
2026-03-19 13:26:49 +01:00

254 lines
9.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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)}")