mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-26 19:54: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.
161 lines
6.2 KiB
Python
161 lines
6.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
TPM 2.0 measured-boot and attestation helper.
|
|
|
|
Reads PCRs via tpm2-tools, replays the firmware event log, optionally produces
|
|
and verifies a nonce-bound quote, and diffs measured PCRs against a golden
|
|
baseline JSON. Emits a structured report.
|
|
|
|
Requires tpm2-tools (tpm2_pcrread, tpm2_eventlog, tpm2_quote, tpm2_checkquote).
|
|
Authorized integrity-verification use only.
|
|
"""
|
|
import argparse
|
|
import json
|
|
import os
|
|
import re
|
|
import secrets
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
EVENTLOG = "/sys/kernel/security/tpm0/binary_bios_measurements"
|
|
|
|
|
|
def run(cmd: list[str], timeout: int = 120) -> tuple[int, str, str]:
|
|
try:
|
|
p = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
|
|
return p.returncode, p.stdout, p.stderr
|
|
except FileNotFoundError:
|
|
return 127, "", f"not found: {cmd[0]}"
|
|
except subprocess.SubprocessError as exc:
|
|
return 1, "", str(exc)
|
|
|
|
|
|
def need(tool: str) -> bool:
|
|
if not shutil.which(tool):
|
|
print(f"[error] required tool missing: {tool}", file=sys.stderr)
|
|
return False
|
|
return True
|
|
|
|
|
|
def read_pcrs(selection: str) -> dict:
|
|
if not need("tpm2_pcrread"):
|
|
return {"error": "tpm2_pcrread missing"}
|
|
rc, out, err = run(["tpm2_pcrread", f"sha256:{selection}"])
|
|
if rc != 0:
|
|
return {"error": err.strip() or "tpm2_pcrread failed", "returncode": rc}
|
|
pcrs = {}
|
|
for line in out.splitlines():
|
|
m = re.match(r"\s*(\d+)\s*:\s*(0x[0-9A-Fa-f]+)", line)
|
|
if m:
|
|
pcrs[m.group(1)] = m.group(2).lower()
|
|
return pcrs
|
|
|
|
|
|
def replay_eventlog() -> dict:
|
|
if not need("tpm2_eventlog"):
|
|
return {"error": "tpm2_eventlog missing"}
|
|
if not os.path.exists(EVENTLOG):
|
|
return {"error": f"event log not found at {EVENTLOG} (run as root?)"}
|
|
rc, out, err = run(["tpm2_eventlog", EVENTLOG])
|
|
if rc != 0:
|
|
return {"error": err.strip() or "tpm2_eventlog failed"}
|
|
# Pull the calculated pcrs section emitted by the tool
|
|
calc = {}
|
|
for line in out.splitlines():
|
|
m = re.match(r"\s*(\d+)\s*:\s*(0x[0-9A-Fa-f]+)", line)
|
|
if m:
|
|
calc[m.group(1)] = m.group(2).lower()
|
|
return {"calculated_pcrs": calc, "event_count": out.count("EventNum")}
|
|
|
|
|
|
def attest(selection: str, workdir: Path) -> dict:
|
|
for tool in ("tpm2_createprimary", "tpm2_create", "tpm2_load", "tpm2_readpublic",
|
|
"tpm2_quote", "tpm2_checkquote"):
|
|
if not need(tool):
|
|
return {"error": f"{tool} missing"}
|
|
primary = workdir / "primary.ctx"
|
|
akpub, akpriv, akctx, akpem = (workdir / f for f in ("ak.pub", "ak.priv", "ak.ctx", "ak.pem"))
|
|
qmsg, qsig, qpcrs = (workdir / f for f in ("quote.msg", "quote.sig", "quote.pcrs"))
|
|
nonce = secrets.token_hex(20)
|
|
|
|
steps = [
|
|
["tpm2_createprimary", "-C", "e", "-g", "sha256", "-G", "rsa", "-c", str(primary)],
|
|
["tpm2_create", "-C", str(primary), "-G", "rsa", "-u", str(akpub), "-r", str(akpriv),
|
|
"-a", "fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|sign"],
|
|
["tpm2_load", "-C", str(primary), "-u", str(akpub), "-r", str(akpriv), "-c", str(akctx)],
|
|
["tpm2_readpublic", "-c", str(akctx), "-o", str(akpem), "-f", "pem"],
|
|
["tpm2_quote", "-c", str(akctx), "-l", f"sha256:{selection}", "-q", nonce,
|
|
"-m", str(qmsg), "-s", str(qsig), "-o", str(qpcrs), "-g", "sha256"],
|
|
]
|
|
for cmd in steps:
|
|
rc, out, err = run(cmd)
|
|
if rc != 0:
|
|
return {"error": f"{cmd[0]} failed: {err.strip()}"}
|
|
|
|
rc, out, err = run(["tpm2_checkquote", "-u", str(akpem), "-m", str(qmsg),
|
|
"-s", str(qsig), "-f", str(qpcrs), "-q", nonce, "-g", "sha256"])
|
|
return {"nonce": nonce, "verified": rc == 0,
|
|
"checkquote_output": (out or err)[:800]}
|
|
|
|
|
|
def diff_baseline(pcrs: dict, baseline_path: str) -> dict:
|
|
try:
|
|
baseline = json.loads(Path(baseline_path).read_text(encoding="utf-8"))
|
|
except (OSError, json.JSONDecodeError) as exc:
|
|
return {"error": f"cannot read baseline: {exc}"}
|
|
drift = {k: {"expected": baseline[k], "actual": pcrs.get(k)}
|
|
for k in baseline if baseline.get(k) != pcrs.get(k)}
|
|
return {"match": not drift, "drift": drift}
|
|
|
|
|
|
def main() -> int:
|
|
ap = argparse.ArgumentParser(description="TPM measured-boot attestation helper")
|
|
ap.add_argument("--pcrs", default="0,1,2,3,4,5,6,7", help="comma-separated PCR indices")
|
|
ap.add_argument("--quote", action="store_true", help="produce + verify an AK quote")
|
|
ap.add_argument("--baseline", help="golden PCR baseline JSON to diff against")
|
|
ap.add_argument("--save-baseline", help="write current PCRs as a baseline JSON")
|
|
ap.add_argument("--output", help="write JSON report")
|
|
args = ap.parse_args()
|
|
|
|
report = {"pcr_selection": args.pcrs}
|
|
pcrs = read_pcrs(args.pcrs)
|
|
report["pcrs"] = pcrs
|
|
if "error" not in pcrs:
|
|
for idx, val in sorted(pcrs.items(), key=lambda x: int(x[0])):
|
|
print(f"PCR[{idx}] = {val}")
|
|
|
|
report["eventlog"] = replay_eventlog()
|
|
if isinstance(pcrs, dict) and "calculated_pcrs" in report["eventlog"]:
|
|
calc = report["eventlog"]["calculated_pcrs"]
|
|
mismatches = {k: (pcrs.get(k), calc.get(k)) for k in calc if pcrs.get(k) != calc.get(k)}
|
|
report["eventlog_replay_match"] = not mismatches
|
|
if mismatches:
|
|
print(f"[!] event-log replay MISMATCH: {mismatches}")
|
|
else:
|
|
print("[+] event-log replay matches live PCRs")
|
|
|
|
if args.save_baseline and "error" not in pcrs:
|
|
Path(args.save_baseline).write_text(json.dumps(pcrs, indent=2), encoding="utf-8")
|
|
print(f"[+] baseline saved to {args.save_baseline}")
|
|
|
|
if args.baseline and "error" not in pcrs:
|
|
report["baseline_diff"] = diff_baseline(pcrs, args.baseline)
|
|
print(f"[+] baseline match: {report['baseline_diff'].get('match')}")
|
|
|
|
if args.quote:
|
|
with tempfile.TemporaryDirectory() as td:
|
|
report["attestation"] = attest(args.pcrs, Path(td))
|
|
print(f"[+] quote verified: {report['attestation'].get('verified')}")
|
|
|
|
if args.output:
|
|
Path(args.output).write_text(json.dumps(report, indent=2), encoding="utf-8")
|
|
print(f"[+] report written to {args.output}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|