文件内容
scripts/agent.py
#!/usr/bin/env python3
"""Threat Actor Profiling from OSINT Agent - Builds threat actor profiles using open-source intelligence."""
import json
import logging
import argparse
from datetime import datetime
import requests
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)
MITRE_ATTACK_URL = "https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json"
def fetch_mitre_attack_data():
"""Fetch MITRE ATT&CK enterprise data."""
resp = requests.get(MITRE_ATTACK_URL, timeout=60)
resp.raise_for_status()
bundle = resp.json()
logger.info("Fetched ATT&CK bundle with %d objects", len(bundle.get("objects", [])))
return bundle
def extract_group_info(bundle, group_name):
"""Extract threat group information from ATT&CK STIX bundle."""
groups = [o for o in bundle["objects"] if o.get("type") == "intrusion-set"]
target_group = None
for g in groups:
aliases = [g.get("name", "").lower()] + [a.lower() for a in g.get("aliases", [])]
if group_name.lower() in aliases:
target_group = g
break
if not target_group:
logger.warning("Group '%s' not found. Available: %s", group_name, [g["name"] for g in groups[:20]])
return None
return {
"name": target_group.get("name"),
"aliases": target_group.get("aliases", []),
"description": target_group.get("description", "")[:500],
"stix_id": target_group.get("id"),
"created": target_group.get("created"),
"modified": target_group.get("modified"),
"external_references": [{"source": r.get("source_name"), "url": r.get("url")}
for r in target_group.get("external_references", []) if r.get("url")],
}
def extract_group_techniques(bundle, group_stix_id):
"""Extract techniques used by a threat group via relationships."""
relationships = [o for o in bundle["objects"] if o.get("type") == "relationship"
and o.get("source_ref") == group_stix_id and o.get("relationship_type") == "uses"]
technique_map = {}
for obj in bundle["objects"]:
if obj.get("type") == "attack-pattern":
technique_map[obj["id"]] = obj
techniques = []
for rel in relationships:
target_id = rel.get("target_ref", "")
tech = technique_map.get(target_id)
if tech:
ext_refs = tech.get("external_references", [])
tech_id = next((r.get("external_id") for r in ext_refs if r.get("source_name") == "mitre-attack"), "")
kill_chain = [p.get("phase_name") for p in tech.get("kill_chain_phases", [])]
techniques.append({"technique_id": tech_id, "name": tech.get("name"), "tactics": kill_chain,
"description": rel.get("description", "")[:200]})
logger.info("Found %d techniques for group", len(techniques))
return techniques
def extract_group_malware_tools(bundle, group_stix_id):
"""Extract malware and tools associated with the group."""
relationships = [o for o in bundle["objects"] if o.get("type") == "relationship"
and o.get("source_ref") == group_stix_id and o.get("relationship_type") == "uses"]
obj_map = {o["id"]: o for o in bundle["objects"] if o.get("type") in ("malware", "tool")}
items = []
for rel in relationships:
target = obj_map.get(rel.get("target_ref"))
if target:
items.append({"name": target.get("name"), "type": target.get("type"),
"description": target.get("description", "")[:200]})
return items
def search_alienvault_otx(group_name, otx_key=None):
"""Search AlienVault OTX for threat actor intelligence."""
headers = {}
if otx_key:
headers["X-OTX-API-KEY"] = otx_key
try:
resp = requests.get(f"https://otx.alienvault.com/api/v1/pulses/search",
params={"q": group_name, "limit": 10}, headers=headers, timeout=15)
if resp.status_code == 200:
pulses = resp.json().get("results", [])
return [{"name": p.get("name"), "created": p.get("created"), "tags": p.get("tags", []),
"indicator_count": len(p.get("indicators", []))} for p in pulses]
except requests.RequestException as e:
logger.warning("OTX search failed: %s", e)
return []
def build_tactic_coverage(techniques):
"""Analyze tactic coverage across the kill chain."""
tactic_map = {}
for tech in techniques:
for tactic in tech.get("tactics", []):
if tactic not in tactic_map:
tactic_map[tactic] = []
tactic_map[tactic].append(tech["technique_id"])
return {tactic: {"count": len(techs), "techniques": techs} for tactic, techs in tactic_map.items()}
def generate_report(group_info, techniques, malware_tools, otx_results, tactic_coverage):
"""Generate threat actor profile report."""
report = {
"timestamp": datetime.utcnow().isoformat(),
"threat_actor_profile": group_info,
"mitre_techniques": techniques,
"malware_and_tools": malware_tools,
"tactic_coverage": tactic_coverage,
"osint_intelligence": otx_results,
"summary": {
"technique_count": len(techniques),
"tool_count": len(malware_tools),
"tactics_covered": len(tactic_coverage),
"osint_reports": len(otx_results),
},
}
name = group_info.get("name", "Unknown") if group_info else "Unknown"
print(f"THREAT ACTOR PROFILE: {name}, {len(techniques)} techniques, "
f"{len(malware_tools)} tools, {len(tactic_coverage)} tactics")
return report
def main():
parser = argparse.ArgumentParser(description="Threat Actor Profiling from OSINT")
parser.add_argument("--group", required=True, help="Threat actor group name (e.g., APT29)")
parser.add_argument("--otx-key", help="AlienVault OTX API key")
parser.add_argument("--output", default="threat_actor_profile.json")
args = parser.parse_args()
bundle = fetch_mitre_attack_data()
group_info = extract_group_info(bundle, args.group)
techniques, malware_tools = [], []
if group_info:
techniques = extract_group_techniques(bundle, group_info["stix_id"])
malware_tools = extract_group_malware_tools(bundle, group_info["stix_id"])
otx_results = search_alienvault_otx(args.group, args.otx_key)
tactic_coverage = build_tactic_coverage(techniques)
report = generate_report(group_info, techniques, malware_tools, otx_results, tactic_coverage)
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
logger.info("Report saved to %s", args.output)
if __name__ == "__main__":
main()