mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-06 15:58:56 +03:00
Add 5 new cybersecurity skills: greenbone vuln mgmt, email compromise detection, MISP sharing, CobaltStrike C2 analysis, registry run key hunting
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
#!/usr/bin/env python3
|
||||
"""MISP Threat Intelligence Sharing agent - creates events, manages attributes, searches IOCs, and validates sharing configuration via PyMISP"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from collections import Counter, defaultdict
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from pymisp import PyMISP, MISPEvent, MISPAttribute, MISPTag
|
||||
HAS_PYMISP = True
|
||||
except ImportError:
|
||||
HAS_PYMISP = False
|
||||
|
||||
MISP_ATTRIBUTE_TYPES = {
|
||||
"ip-dst", "ip-src", "domain", "hostname", "url", "md5", "sha1",
|
||||
"sha256", "filename", "email-src", "email-dst", "mutex", "regkey",
|
||||
"user-agent", "vulnerability", "link", "text", "comment",
|
||||
}
|
||||
|
||||
TLP_TAGS = {
|
||||
"white": "tlp:white",
|
||||
"green": "tlp:green",
|
||||
"amber": "tlp:amber",
|
||||
"amber+strict": "tlp:amber+strict",
|
||||
"red": "tlp:red",
|
||||
}
|
||||
|
||||
|
||||
def load_data(path):
|
||||
return json.loads(Path(path).read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def connect_misp(url, api_key, ssl=True):
|
||||
"""Initialize PyMISP connection."""
|
||||
if not HAS_PYMISP:
|
||||
return None, "pymisp not installed (pip install pymisp)"
|
||||
misp = PyMISP(url, api_key, ssl=ssl)
|
||||
return misp, "connected"
|
||||
|
||||
|
||||
def create_event_from_data(misp, event_data):
|
||||
"""Create a MISP event with attributes and tags."""
|
||||
event = MISPEvent()
|
||||
event.info = event_data.get("info", "Untitled Event")
|
||||
event.distribution = event_data.get("distribution", 1) # 0=org, 1=community, 2=connected, 3=all
|
||||
event.threat_level_id = event_data.get("threat_level", 2) # 1=high, 2=medium, 3=low, 4=undefined
|
||||
event.analysis = event_data.get("analysis", 0) # 0=initial, 1=ongoing, 2=complete
|
||||
for attr in event_data.get("attributes", []):
|
||||
attr_type = attr.get("type", "text")
|
||||
value = attr.get("value", "")
|
||||
category = attr.get("category", "")
|
||||
to_ids = attr.get("to_ids", True)
|
||||
comment = attr.get("comment", "")
|
||||
if attr_type in MISP_ATTRIBUTE_TYPES and value:
|
||||
event.add_attribute(type=attr_type, value=value, category=category,
|
||||
to_ids=to_ids, comment=comment)
|
||||
for tag_name in event_data.get("tags", []):
|
||||
event.add_tag(tag_name)
|
||||
tlp = event_data.get("tlp", "").lower()
|
||||
if tlp in TLP_TAGS:
|
||||
event.add_tag(TLP_TAGS[tlp])
|
||||
if misp:
|
||||
result = misp.add_event(event)
|
||||
return result
|
||||
return event.to_dict()
|
||||
|
||||
|
||||
def validate_event_quality(event_data):
|
||||
"""Validate event data quality for sharing readiness."""
|
||||
findings = []
|
||||
eid = event_data.get("id", event_data.get("info", "unknown"))
|
||||
if not event_data.get("info"):
|
||||
findings.append({
|
||||
"type": "missing_event_info",
|
||||
"severity": "high",
|
||||
"resource": str(eid),
|
||||
"detail": "Event lacks descriptive info/title",
|
||||
})
|
||||
attrs = event_data.get("attributes", event_data.get("Attribute", []))
|
||||
if not attrs:
|
||||
findings.append({
|
||||
"type": "no_attributes",
|
||||
"severity": "high",
|
||||
"resource": str(eid),
|
||||
"detail": "Event has no IOC attributes",
|
||||
})
|
||||
attr_types = Counter(a.get("type", "unknown") for a in attrs)
|
||||
if len(attr_types) == 1 and len(attrs) > 1:
|
||||
findings.append({
|
||||
"type": "single_attribute_type",
|
||||
"severity": "low",
|
||||
"resource": str(eid),
|
||||
"detail": f"All {len(attrs)} attributes are type '{list(attr_types.keys())[0]}' - consider enriching",
|
||||
})
|
||||
tags = event_data.get("tags", event_data.get("Tag", []))
|
||||
tag_names = [t.get("name", t) if isinstance(t, dict) else t for t in tags]
|
||||
has_tlp = any("tlp:" in t.lower() for t in tag_names)
|
||||
if not has_tlp:
|
||||
findings.append({
|
||||
"type": "missing_tlp_tag",
|
||||
"severity": "high",
|
||||
"resource": str(eid),
|
||||
"detail": "Event lacks TLP classification tag",
|
||||
})
|
||||
has_mitre = any("mitre-attack" in t.lower() or "attack-pattern" in t.lower() for t in tag_names)
|
||||
if not has_mitre and len(attrs) > 0:
|
||||
findings.append({
|
||||
"type": "missing_mitre_mapping",
|
||||
"severity": "medium",
|
||||
"resource": str(eid),
|
||||
"detail": "Event lacks MITRE ATT&CK technique mapping",
|
||||
})
|
||||
dist = event_data.get("distribution", -1)
|
||||
if dist == 3:
|
||||
findings.append({
|
||||
"type": "unrestricted_distribution",
|
||||
"severity": "medium",
|
||||
"resource": str(eid),
|
||||
"detail": "Event set to 'All communities' distribution - verify this is intentional",
|
||||
})
|
||||
for attr in attrs:
|
||||
val = attr.get("value", "")
|
||||
atype = attr.get("type", "")
|
||||
if atype in ("ip-dst", "ip-src") and val in ("127.0.0.1", "0.0.0.0", "10.0.0.1", "192.168.1.1"):
|
||||
findings.append({
|
||||
"type": "private_ip_ioc",
|
||||
"severity": "high",
|
||||
"resource": str(eid),
|
||||
"detail": f"Private/localhost IP '{val}' used as IOC - will generate false positives",
|
||||
})
|
||||
if atype in ("md5", "sha1", "sha256") and len(val) < 32:
|
||||
findings.append({
|
||||
"type": "invalid_hash_length",
|
||||
"severity": "high",
|
||||
"resource": str(eid),
|
||||
"detail": f"Hash attribute '{val}' is too short for type {atype}",
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def validate_sharing_config(config):
|
||||
"""Validate MISP sharing and feed configuration."""
|
||||
findings = []
|
||||
servers = config.get("sync_servers", [])
|
||||
if not servers:
|
||||
findings.append({
|
||||
"type": "no_sync_servers",
|
||||
"severity": "medium",
|
||||
"resource": "misp_config",
|
||||
"detail": "No synchronization servers configured for intelligence sharing",
|
||||
})
|
||||
for srv in servers:
|
||||
if not srv.get("pull", False) and not srv.get("push", False):
|
||||
findings.append({
|
||||
"type": "inactive_sync_server",
|
||||
"severity": "medium",
|
||||
"resource": srv.get("name", srv.get("url", "")),
|
||||
"detail": "Sync server has neither pull nor push enabled",
|
||||
})
|
||||
feeds = config.get("feeds", [])
|
||||
enabled_feeds = [f for f in feeds if f.get("enabled", False)]
|
||||
if not enabled_feeds:
|
||||
findings.append({
|
||||
"type": "no_active_feeds",
|
||||
"severity": "medium",
|
||||
"resource": "misp_config",
|
||||
"detail": "No active threat intelligence feeds configured",
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def analyze(data):
|
||||
findings = []
|
||||
events = data.get("events", [data] if "info" in data or "Attribute" in data else [])
|
||||
if isinstance(data, list):
|
||||
events = data
|
||||
for evt in events:
|
||||
findings.extend(validate_event_quality(evt))
|
||||
if "sync_servers" in data or "feeds" in data:
|
||||
findings.extend(validate_sharing_config(data))
|
||||
return findings
|
||||
|
||||
|
||||
def generate_report(input_path):
|
||||
data = load_data(input_path)
|
||||
findings = analyze(data)
|
||||
sev = Counter(f["severity"] for f in findings)
|
||||
cats = Counter(f["type"] for f in findings)
|
||||
return {
|
||||
"report": "misp_threat_intelligence_sharing",
|
||||
"generated_at": datetime.utcnow().isoformat() + "Z",
|
||||
"total_findings": len(findings),
|
||||
"severity_summary": dict(sev),
|
||||
"finding_categories": dict(cats),
|
||||
"findings": findings,
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description="MISP Threat Intelligence Sharing Agent")
|
||||
ap.add_argument("--input", required=True, help="Input JSON with MISP events or config")
|
||||
ap.add_argument("--output", help="Output JSON report path")
|
||||
ap.add_argument("--misp-url", help="MISP instance URL for live operations")
|
||||
ap.add_argument("--api-key", help="MISP API key")
|
||||
args = ap.parse_args()
|
||||
report = generate_report(args.input)
|
||||
out = json.dumps(report, indent=2)
|
||||
if args.output:
|
||||
Path(args.output).write_text(out, encoding="utf-8")
|
||||
print(f"Report written to {args.output}")
|
||||
else:
|
||||
print(out)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user