mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-14 15:04:56 +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
165 lines
7.1 KiB
Python
165 lines
7.1 KiB
Python
#!/usr/bin/env python3
|
|
"""APT threat hunting agent using MITRE ATT&CK, attackcti, and osquery."""
|
|
|
|
import json
|
|
import sys
|
|
import argparse
|
|
from datetime import datetime
|
|
|
|
try:
|
|
from attackcti import attack_client
|
|
except ImportError:
|
|
print("Install attackcti: pip install attackcti")
|
|
sys.exit(1)
|
|
|
|
|
|
def get_apt_group_ttps(group_name):
|
|
"""Retrieve TTPs for a specific APT group from MITRE ATT&CK."""
|
|
client = attack_client()
|
|
groups = client.get_groups()
|
|
target = None
|
|
for g in groups:
|
|
aliases = [a.lower() for a in g.get("aliases", [])]
|
|
if group_name.lower() in g["name"].lower() or group_name.lower() in aliases:
|
|
target = g
|
|
break
|
|
if not target:
|
|
print(f"[!] Group '{group_name}' not found in ATT&CK")
|
|
return None
|
|
techniques = client.get_techniques_used_by_group(target)
|
|
return {"group": target["name"], "id": target["external_references"][0]["external_id"],
|
|
"techniques": [{"id": t["external_references"][0]["external_id"],
|
|
"name": t["name"],
|
|
"tactic": [p["phase_name"] for p in t.get("kill_chain_phases", [])]}
|
|
for t in techniques]}
|
|
|
|
|
|
def generate_osquery_hunts(techniques):
|
|
"""Generate osquery hunt queries for detected ATT&CK techniques."""
|
|
query_map = {
|
|
"T1059": ("Process execution (Command and Scripting)",
|
|
"SELECT pid, name, cmdline, path, parent FROM processes "
|
|
"WHERE name IN ('powershell.exe','cmd.exe','wscript.exe','cscript.exe','bash','python');"),
|
|
"T1053": ("Scheduled Task/Job persistence",
|
|
"SELECT name, action, path, enabled, last_run_time FROM scheduled_tasks "
|
|
"WHERE enabled=1 AND action NOT LIKE '%System32%';"),
|
|
"T1547": ("Boot/Logon autostart execution",
|
|
"SELECT name, path, source FROM autoexec;"),
|
|
"T1071": ("Application layer protocol C2",
|
|
"SELECT pid, remote_address, remote_port, local_port FROM process_open_sockets "
|
|
"WHERE remote_port IN (443, 8443, 8080, 4443) AND family=2;"),
|
|
"T1055": ("Process injection",
|
|
"SELECT pid, name, cmdline FROM processes WHERE on_disk=0;"),
|
|
"T1003": ("OS credential dumping",
|
|
"SELECT pid, name, cmdline FROM processes "
|
|
"WHERE name IN ('mimikatz.exe','procdump.exe','ntdsutil.exe') "
|
|
"OR cmdline LIKE '%sekurlsa%' OR cmdline LIKE '%lsass%';"),
|
|
"T1021": ("Remote services lateral movement",
|
|
"SELECT pid, name, cmdline FROM processes "
|
|
"WHERE name IN ('psexec.exe','wmic.exe','winrm.cmd') "
|
|
"OR cmdline LIKE '%invoke-command%';"),
|
|
"T1027": ("Obfuscated files or information",
|
|
"SELECT pid, name, cmdline FROM processes "
|
|
"WHERE cmdline LIKE '%-enc%' OR cmdline LIKE '%-encodedcommand%';"),
|
|
"T1566": ("Phishing initial access",
|
|
"SELECT path, filename, size FROM file "
|
|
"WHERE directory LIKE '%Downloads%' "
|
|
"AND (filename LIKE '%.iso' OR filename LIKE '%.img' OR filename LIKE '%.lnk');"),
|
|
"T1218": ("Signed binary proxy execution",
|
|
"SELECT pid, name, cmdline, parent FROM processes "
|
|
"WHERE name IN ('mshta.exe','rundll32.exe','regsvr32.exe','certutil.exe');"),
|
|
}
|
|
hunts = []
|
|
for tech in techniques:
|
|
tech_id = tech["id"].split(".")[0]
|
|
if tech_id in query_map:
|
|
desc, query = query_map[tech_id]
|
|
hunts.append({"technique": tech["id"], "name": tech["name"],
|
|
"description": desc, "osquery": query})
|
|
return hunts
|
|
|
|
|
|
def generate_sigma_rule(technique_id, technique_name, tactic):
|
|
"""Generate a Sigma detection rule for a given technique."""
|
|
return {
|
|
"title": f"Detect {technique_name} ({technique_id})",
|
|
"status": "experimental",
|
|
"description": f"Detects potential {technique_name} activity mapped to {technique_id}",
|
|
"references": [f"https://attack.mitre.org/techniques/{technique_id.replace('.','/')}/"],
|
|
"tags": [f"attack.{t}" for t in tactic] + [f"attack.{technique_id.lower()}"],
|
|
"logsource": {"category": "process_creation", "product": "windows"},
|
|
"detection": {"selection": {"technique_id": technique_id}, "condition": "selection"},
|
|
"level": "medium",
|
|
}
|
|
|
|
|
|
def build_hunt_report(group_name):
|
|
"""Build a complete threat hunt report for an APT group."""
|
|
print(f"\n{'='*70}")
|
|
print(f" APT THREAT HUNT REPORT")
|
|
print(f" Generated: {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')} UTC")
|
|
print(f"{'='*70}\n")
|
|
|
|
print(f"[*] Querying MITRE ATT&CK for group: {group_name}")
|
|
group_data = get_apt_group_ttps(group_name)
|
|
if not group_data:
|
|
return
|
|
|
|
print(f"[+] Found: {group_data['group']} ({group_data['id']})")
|
|
print(f"[+] Techniques mapped: {len(group_data['techniques'])}\n")
|
|
|
|
print(f"--- TECHNIQUE COVERAGE ---")
|
|
tactic_counts = {}
|
|
for t in group_data["techniques"]:
|
|
print(f" [{t['id']}] {t['name']} -> {', '.join(t['tactic'])}")
|
|
for tac in t["tactic"]:
|
|
tactic_counts[tac] = tactic_counts.get(tac, 0) + 1
|
|
|
|
print(f"\n--- TACTIC DISTRIBUTION ---")
|
|
for tac, count in sorted(tactic_counts.items(), key=lambda x: -x[1]):
|
|
bar = "#" * count
|
|
print(f" {tac:<30} {bar} ({count})")
|
|
|
|
print(f"\n--- OSQUERY HUNT QUERIES ---")
|
|
hunts = generate_osquery_hunts(group_data["techniques"])
|
|
if hunts:
|
|
for h in hunts:
|
|
print(f"\n Technique: {h['technique']} - {h['description']}")
|
|
print(f" Query: {h['osquery']}")
|
|
else:
|
|
print(" No matching osquery hunts for this group's techniques.")
|
|
|
|
print(f"\n--- SIGMA RULES ---")
|
|
for t in group_data["techniques"][:5]:
|
|
rule = generate_sigma_rule(t["id"], t["name"], t["tactic"])
|
|
print(f"\n Rule: {rule['title']}")
|
|
print(f" Tags: {', '.join(rule['tags'])}")
|
|
print(f" Level: {rule['level']}")
|
|
|
|
print(f"\n--- HUNT RECOMMENDATIONS ---")
|
|
print(f" 1. Execute osquery hunts across all endpoints via fleet manager")
|
|
print(f" 2. Search SIEM for technique indicators over past 90 days")
|
|
print(f" 3. Validate EDR telemetry covers all {len(group_data['techniques'])} techniques")
|
|
print(f" 4. Cross-reference with network logs (Zeek/Suricata) for C2 patterns")
|
|
print(f" 5. Document findings using Diamond Model analysis framework")
|
|
print(f"\n{'='*70}\n")
|
|
|
|
return group_data
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="APT Threat Hunting Agent")
|
|
parser.add_argument("--group", default="APT29", help="APT group name (e.g., APT29, APT28, Lazarus)")
|
|
parser.add_argument("--output", help="Save report to JSON file")
|
|
args = parser.parse_args()
|
|
|
|
report = build_hunt_report(args.group)
|
|
if report and args.output:
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2)
|
|
print(f"[+] JSON report saved to {args.output}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|