mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-26 11:44:37 +03:00
8cae0648ec
Demand-driven expansion targeting the fastest-growing 2025-2026 threat and
skills categories (ISC2/WEF/CrowdStrike/Mandiant signals):
- AI Security (NEW domain, 12 skills): LLM red-teaming with garak/PyRIT,
prompt injection (direct/indirect/RAG), MCP tool-poisoning, agentic tool
invocation, guardrails, model/data poisoning, system-prompt leakage,
embedding/vector weaknesses, model extraction, continuous red-teaming
- Supply Chain Security (NEW domain, 5 skills): SBOMs, dependency confusion,
malicious-npm triage, typosquatting, SLSA/Sigstore provenance
- Hardware & Firmware Security (NEW domain, 4 skills): CHIPSEC/UEFI audit,
Secure Boot bypass, TPM measured-boot attestation, ESP bootkit hunting
- Identity (10): Entra ID/ROADtools, GraphRunner, AADInternals, ADCS/Certipy,
shadow credentials, coercion, BloodHound CE, device-code phishing, SSO abuse
- Cloud-native (8): Stratus, Pacu, CloudFox, container escape, K8s RBAC,
Falco, Trivy, kube-bench
- Offensive C2 (6): Sliver, Havoc, NetExec, DPAPI, NTLM relay ESC8, redirectors
- DFIR (6): Hayabusa, Chainsaw, KAPE, Velociraptor, EZ Tools, Plaso
- Backfill (4): OpenCTI, MISP, honeytokens, post-quantum crypto migration
Each skill follows the repo taxonomy (SKILL.md + references/{standards,api-reference}.md
+ scripts/agent.py + LICENSE), with researched real tool commands (no placeholders),
complete frontmatter, and ATT&CK/ATLAS + NIST CSF mappings. Updates README domain
table, skill count, and index.json.
134 lines
4.7 KiB
Python
134 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Chainsaw EVTX hunting helper.
|
|
|
|
Drives the Chainsaw binary to run a Sigma+Chainsaw hunt over a directory of
|
|
.evtx files, emits JSON, and summarizes detections by rule and level. Can also
|
|
run a targeted keyword/regex search to confirm a hypothesis.
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from collections import Counter
|
|
from datetime import datetime, timezone
|
|
|
|
|
|
def find_binary(explicit):
|
|
"""Locate the chainsaw binary."""
|
|
if explicit:
|
|
if os.path.isfile(explicit):
|
|
return explicit
|
|
sys.exit(f"[!] Binary not found: {explicit}")
|
|
for name in ("chainsaw", "chainsaw.exe"):
|
|
path = shutil.which(name)
|
|
if path:
|
|
return path
|
|
# common build location
|
|
local = os.path.join("target", "release", "chainsaw")
|
|
if os.path.isfile(local):
|
|
return local
|
|
sys.exit("[!] chainsaw not found; pass --bin /path/to/chainsaw")
|
|
|
|
|
|
def run_json(cmd):
|
|
"""Run a chainsaw command expected to emit JSON on stdout."""
|
|
print(f"[*] {' '.join(cmd)}", file=sys.stderr)
|
|
proc = subprocess.run(cmd, capture_output=True, text=True)
|
|
if proc.returncode != 0 and not proc.stdout.strip():
|
|
print(proc.stderr[-1500:], file=sys.stderr)
|
|
sys.exit(f"[!] chainsaw exited {proc.returncode}")
|
|
try:
|
|
return json.loads(proc.stdout)
|
|
except json.JSONDecodeError:
|
|
return []
|
|
|
|
|
|
def hunt(binary, evtx, sigma, mapping, rules, level, status):
|
|
cmd = [binary, "hunt", evtx, "-s", sigma, "--mapping", mapping, "--json"]
|
|
if rules:
|
|
cmd += ["-r", rules]
|
|
if level:
|
|
cmd += ["--level", level]
|
|
if status:
|
|
cmd += ["--status", status]
|
|
return run_json(cmd)
|
|
|
|
|
|
def search(binary, evtx, pattern, regex, ignore_case):
|
|
cmd = [binary, "search"]
|
|
if regex:
|
|
cmd += ["-e", pattern]
|
|
else:
|
|
cmd += [pattern]
|
|
if ignore_case:
|
|
cmd += ["-i"]
|
|
cmd += [evtx, "--json"]
|
|
return run_json(cmd)
|
|
|
|
|
|
def summarize(detections):
|
|
"""Summarize Chainsaw hunt JSON by rule name and level."""
|
|
rules, levels = Counter(), Counter()
|
|
total = 0
|
|
for group in detections:
|
|
# Chainsaw JSON groups detections; each carries name/level metadata
|
|
name = group.get("name") or group.get("group") or "unknown"
|
|
level = group.get("level", "unknown")
|
|
docs = group.get("document") or group.get("documents") or [group]
|
|
n = len(docs) if isinstance(docs, list) else 1
|
|
total += n
|
|
rules[name] += n
|
|
levels[level] += n
|
|
return {"total_hits": total,
|
|
"by_level": dict(levels),
|
|
"top_rules": rules.most_common(15)}
|
|
|
|
|
|
def main():
|
|
p = argparse.ArgumentParser(description="Chainsaw EVTX hunting helper")
|
|
p.add_argument("evtx", help="Directory or file of .evtx logs")
|
|
p.add_argument("--bin", help="Path to chainsaw binary")
|
|
p.add_argument("-s", "--sigma", help="SigmaHQ rules directory (for hunt)")
|
|
p.add_argument("--mapping", help="Sigma->event mapping yml (for hunt)")
|
|
p.add_argument("-r", "--rules", help="Chainsaw rules directory")
|
|
p.add_argument("--level", help="Filter by Sigma level (e.g. high)")
|
|
p.add_argument("--status", help="Filter by rule status (e.g. stable)")
|
|
p.add_argument("--search", dest="search_pattern", help="Run a search instead of a hunt")
|
|
p.add_argument("--regex", action="store_true", help="Treat --search as a regex")
|
|
p.add_argument("-i", "--ignore-case", action="store_true")
|
|
p.add_argument("--report", help="Write JSON output to this path")
|
|
args = p.parse_args()
|
|
|
|
binary = find_binary(args.bin)
|
|
|
|
if args.search_pattern:
|
|
results = search(binary, args.evtx, args.search_pattern,
|
|
args.regex, args.ignore_case)
|
|
out = {"mode": "search", "pattern": args.search_pattern,
|
|
"hits": len(results) if isinstance(results, list) else 0}
|
|
print(f"[+] search hits: {out['hits']}")
|
|
else:
|
|
if not (args.sigma and args.mapping):
|
|
p.error("hunt mode requires --sigma and --mapping")
|
|
results = hunt(binary, args.evtx, args.sigma, args.mapping,
|
|
args.rules, args.level, args.status)
|
|
out = summarize(results)
|
|
out["mode"] = "hunt"
|
|
print(f"[+] {out['total_hits']} hits across {len(out['top_rules'])} rules")
|
|
print(f"[+] By level: {out['by_level']}")
|
|
for name, count in out["top_rules"]:
|
|
print(f" {count:5d} {name}")
|
|
|
|
out["generated"] = datetime.now(timezone.utc).isoformat()
|
|
if args.report:
|
|
with open(args.report, "w") as f:
|
|
json.dump({"summary": out, "raw": results}, f, indent=2)
|
|
print(f"[+] Report written to {args.report}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|