mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-19 14:09:40 +03:00
Add folder anatomy (scripts/agent.py + references/api-reference.md) for 648 cybersecurity skills
Complete skill folder anatomy across all cybersecurity skills: - scripts/agent.py: 80-150 line Python agents using real libraries (impacket, boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.) - references/api-reference.md: real API documentation with method signatures - LICENSE: MIT license for all skill folders
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Threat campaign correlation agent using MISP and STIX."""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import ssl
|
||||
from collections import Counter
|
||||
from datetime import datetime
|
||||
from math import radians, sin, cos, sqrt, atan2
|
||||
|
||||
|
||||
class MISPClient:
|
||||
"""Client for MISP REST API for campaign correlation."""
|
||||
|
||||
def __init__(self, url, api_key, verify_ssl=False):
|
||||
self.base_url = url.rstrip("/")
|
||||
self.headers = {
|
||||
"Authorization": api_key,
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
}
|
||||
self.ctx = ssl.create_default_context()
|
||||
if not verify_ssl:
|
||||
self.ctx.check_hostname = False
|
||||
self.ctx.verify_mode = ssl.CERT_NONE
|
||||
|
||||
def _request(self, method, path, data=None):
|
||||
url = f"{self.base_url}{path}"
|
||||
body = json.dumps(data).encode() if data else None
|
||||
req = urllib.request.Request(url, data=body, headers=self.headers, method=method)
|
||||
try:
|
||||
with urllib.request.urlopen(req, context=self.ctx, timeout=60) as resp:
|
||||
return json.loads(resp.read().decode())
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
def search_attributes(self, attr_type, value):
|
||||
"""Search MISP for attributes matching type and value."""
|
||||
data = {"type": attr_type, "value": value, "searchall": 1}
|
||||
return self._request("POST", "/attributes/restSearch", data)
|
||||
|
||||
def search_events(self, tags=None, date_from=None, date_to=None):
|
||||
"""Search MISP events with filters."""
|
||||
data = {}
|
||||
if tags:
|
||||
data["tags"] = tags
|
||||
if date_from:
|
||||
data["from"] = date_from
|
||||
if date_to:
|
||||
data["to"] = date_to
|
||||
return self._request("POST", "/events/restSearch", data)
|
||||
|
||||
def get_event(self, event_id):
|
||||
"""Get full event details by ID."""
|
||||
return self._request("GET", f"/events/view/{event_id}")
|
||||
|
||||
def get_correlations(self, event_id):
|
||||
"""Retrieve correlation data for a MISP event."""
|
||||
event = self.get_event(event_id)
|
||||
if "error" in event:
|
||||
return event
|
||||
correlations = []
|
||||
ev = event.get("Event", event)
|
||||
for attr in ev.get("Attribute", []):
|
||||
if attr.get("RelatedAttribute"):
|
||||
for rel in attr["RelatedAttribute"]:
|
||||
correlations.append({
|
||||
"source_event": event_id,
|
||||
"source_attr": attr.get("value"),
|
||||
"source_type": attr.get("type"),
|
||||
"related_event": rel.get("event_id"),
|
||||
"related_value": rel.get("value"),
|
||||
})
|
||||
return correlations
|
||||
|
||||
|
||||
def calculate_campaign_confidence(events):
|
||||
"""Calculate campaign attribution confidence from correlated events."""
|
||||
if not events or len(events) < 2:
|
||||
return {"confidence": "LOW", "score": 0, "reason": "Insufficient events"}
|
||||
|
||||
all_ips = []
|
||||
all_domains = []
|
||||
all_hashes = []
|
||||
all_tags = []
|
||||
|
||||
for event in events:
|
||||
ev = event.get("Event", event)
|
||||
for attr in ev.get("Attribute", []):
|
||||
atype = attr.get("type", "")
|
||||
val = attr.get("value", "")
|
||||
if atype in ("ip-src", "ip-dst"):
|
||||
all_ips.append(val)
|
||||
elif atype in ("domain", "hostname"):
|
||||
all_domains.append(val)
|
||||
elif "hash" in atype or "md5" in atype or "sha" in atype:
|
||||
all_hashes.append(val)
|
||||
for tag in ev.get("Tag", []):
|
||||
all_tags.append(tag.get("name", ""))
|
||||
|
||||
ip_counts = Counter(all_ips)
|
||||
domain_counts = Counter(all_domains)
|
||||
hash_counts = Counter(all_hashes)
|
||||
|
||||
shared_ips = sum(1 for c in ip_counts.values() if c > 1)
|
||||
shared_domains = sum(1 for c in domain_counts.values() if c > 1)
|
||||
shared_hashes = sum(1 for c in hash_counts.values() if c > 1)
|
||||
|
||||
num_events = len(events)
|
||||
infra_score = min(40, (shared_ips + shared_domains) / max(num_events, 1) * 40)
|
||||
capability_score = min(35, shared_hashes / max(num_events, 1) * 35)
|
||||
tag_overlap = len(set(all_tags)) / max(len(all_tags), 1)
|
||||
ttp_score = min(15, tag_overlap * 15)
|
||||
|
||||
total = infra_score + capability_score + ttp_score
|
||||
if total >= 70:
|
||||
confidence = "HIGH"
|
||||
elif total >= 45:
|
||||
confidence = "MEDIUM"
|
||||
else:
|
||||
confidence = "LOW"
|
||||
|
||||
return {
|
||||
"confidence": confidence,
|
||||
"score": round(total, 1),
|
||||
"shared_infrastructure": {"ips": shared_ips, "domains": shared_domains},
|
||||
"shared_capabilities": {"hashes": shared_hashes},
|
||||
"events_analyzed": num_events,
|
||||
}
|
||||
|
||||
|
||||
def extract_campaign_iocs(events):
|
||||
"""Extract shared IOCs across correlated events for blocking."""
|
||||
ioc_events = {}
|
||||
for event in events:
|
||||
ev = event.get("Event", event)
|
||||
eid = ev.get("id", "unknown")
|
||||
for attr in ev.get("Attribute", []):
|
||||
val = attr.get("value", "")
|
||||
atype = attr.get("type", "")
|
||||
key = f"{atype}:{val}"
|
||||
if key not in ioc_events:
|
||||
ioc_events[key] = []
|
||||
ioc_events[key].append(eid)
|
||||
|
||||
shared = {k: v for k, v in ioc_events.items() if len(v) > 1}
|
||||
return {
|
||||
"total_unique_iocs": len(ioc_events),
|
||||
"shared_iocs": len(shared),
|
||||
"shared_indicators": [
|
||||
{"type": k.split(":")[0], "value": ":".join(k.split(":")[1:]), "event_count": len(v)}
|
||||
for k, v in sorted(shared.items(), key=lambda x: len(x[1]), reverse=True)
|
||||
][:50],
|
||||
}
|
||||
|
||||
|
||||
def build_campaign_report(campaign_name, events, attribution=None):
|
||||
"""Build a structured campaign intelligence report."""
|
||||
confidence = calculate_campaign_confidence(events)
|
||||
iocs = extract_campaign_iocs(events)
|
||||
|
||||
dates = []
|
||||
targets = []
|
||||
for event in events:
|
||||
ev = event.get("Event", event)
|
||||
if ev.get("date"):
|
||||
dates.append(ev["date"])
|
||||
info = ev.get("info", "")
|
||||
if info:
|
||||
targets.append(info)
|
||||
|
||||
return {
|
||||
"campaign_name": campaign_name,
|
||||
"report_date": datetime.utcnow().isoformat() + "Z",
|
||||
"timeline": {"first_seen": min(dates) if dates else None, "last_seen": max(dates) if dates else None},
|
||||
"attribution": attribution or "Unattributed",
|
||||
"confidence": confidence,
|
||||
"shared_indicators": iocs,
|
||||
"events_correlated": len(events),
|
||||
"target_summary": targets[:10],
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import os
|
||||
misp_url = os.environ.get("MISP_URL", "https://misp.example.com")
|
||||
misp_key = os.environ.get("MISP_KEY", "")
|
||||
|
||||
action = sys.argv[1] if len(sys.argv) > 1 else "help"
|
||||
if action == "search" and len(sys.argv) > 3:
|
||||
client = MISPClient(misp_url, misp_key)
|
||||
result = client.search_attributes(sys.argv[2], sys.argv[3])
|
||||
print(json.dumps(result, indent=2, default=str))
|
||||
elif action == "correlations" and len(sys.argv) > 2:
|
||||
client = MISPClient(misp_url, misp_key)
|
||||
result = client.get_correlations(sys.argv[2])
|
||||
print(json.dumps(result, indent=2, default=str))
|
||||
elif action == "events":
|
||||
client = MISPClient(misp_url, misp_key)
|
||||
tags = sys.argv[2] if len(sys.argv) > 2 else None
|
||||
result = client.search_events(tags=tags)
|
||||
print(json.dumps(result, indent=2, default=str))
|
||||
else:
|
||||
print("Usage: agent.py [search <type> <value>|correlations <event_id>|events [tag]]")
|
||||
print("Env: MISP_URL, MISP_KEY")
|
||||
Reference in New Issue
Block a user