mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-09-18 05:35:53 +03:00
Add 55 new skills across 3 new domains + 6 undercovered areas (762 -> 817)
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.
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
honeytoken_agent.py — Generate, validate, and inventory Canarytokens.
|
||||
|
||||
Talks to a Canarytokens frontend (the public service at https://canarytokens.org
|
||||
or a self-hosted thinkst/canarytokens-docker instance) via its POST /generate
|
||||
and GET /history HTTP API. Maintains a local JSON inventory of every token
|
||||
deployed, with its memo, planted location, and MITRE D3FEND mapping, so a blue
|
||||
team can track deception coverage.
|
||||
|
||||
This is a defensive tool. Only generate tokens for assets you own or are
|
||||
authorized to instrument, and never commit decoy artifacts to public repos.
|
||||
|
||||
Examples:
|
||||
# Generate an HTTP web-bug token on the public service
|
||||
python3 honeytoken_agent.py generate --type http \
|
||||
--email soc@example.com --memo "wiki admin-passwords page" \
|
||||
--location "https://wiki.internal/it/admin" --d3fend D3-DF
|
||||
|
||||
# Generate against a self-hosted frontend with a webhook
|
||||
python3 honeytoken_agent.py generate --base-url https://canary.example.com \
|
||||
--type aws_keys --webhook https://hooks.slack.com/services/T/B/X \
|
||||
--memo "decoy keys jenkins host" --location "/root/.aws/credentials"
|
||||
|
||||
# Show triggers (history) for a stored token
|
||||
python3 honeytoken_agent.py history --token-id <token> --auth <auth>
|
||||
|
||||
# List the local inventory
|
||||
python3 honeytoken_agent.py inventory
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
sys.stderr.write("ERROR: install dependency with: python3 -m pip install requests\n")
|
||||
sys.exit(2)
|
||||
|
||||
DEFAULT_BASE = "https://canarytokens.org"
|
||||
INVENTORY = os.environ.get("CANARY_INVENTORY", "canarytoken_inventory.json")
|
||||
|
||||
VALID_TYPES = {
|
||||
"http", "dns", "aws_keys", "msword", "adobe_pdf", "slack_api",
|
||||
"kubeconfig", "azure_id", "qr_code", "web_image", "log4shell",
|
||||
"cmd", "cloned_web", "sql_server",
|
||||
}
|
||||
|
||||
|
||||
def _load_inventory():
|
||||
if not os.path.exists(INVENTORY):
|
||||
return []
|
||||
try:
|
||||
with open(INVENTORY, "r", encoding="utf-8") as fh:
|
||||
return json.load(fh)
|
||||
except (json.JSONDecodeError, OSError) as exc:
|
||||
sys.stderr.write(f"WARN: could not read inventory {INVENTORY}: {exc}\n")
|
||||
return []
|
||||
|
||||
|
||||
def _save_inventory(items):
|
||||
try:
|
||||
with open(INVENTORY, "w", encoding="utf-8") as fh:
|
||||
json.dump(items, fh, indent=2)
|
||||
except OSError as exc:
|
||||
sys.stderr.write(f"ERROR: could not write inventory {INVENTORY}: {exc}\n")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def generate(args):
|
||||
if args.type not in VALID_TYPES:
|
||||
sys.stderr.write(f"ERROR: unknown type '{args.type}'. Valid: {sorted(VALID_TYPES)}\n")
|
||||
sys.exit(1)
|
||||
if not args.email and not args.webhook:
|
||||
sys.stderr.write("ERROR: provide --email and/or --webhook for alerting.\n")
|
||||
sys.exit(1)
|
||||
|
||||
data = {"type": args.type, "memo": args.memo}
|
||||
if args.email:
|
||||
data["email"] = args.email
|
||||
if args.webhook:
|
||||
data["webhook_url"] = args.webhook
|
||||
|
||||
url = args.base_url.rstrip("/") + "/generate"
|
||||
try:
|
||||
resp = requests.post(url, data=data, timeout=args.timeout)
|
||||
resp.raise_for_status()
|
||||
except requests.RequestException as exc:
|
||||
sys.stderr.write(f"ERROR: generate request failed: {exc}\n")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
body = resp.json()
|
||||
except ValueError:
|
||||
sys.stderr.write("ERROR: non-JSON response from server:\n" + resp.text[:500] + "\n")
|
||||
sys.exit(1)
|
||||
|
||||
token_id = body.get("token") or body.get("canarytoken")
|
||||
record = {
|
||||
"type": args.type,
|
||||
"memo": args.memo,
|
||||
"location": args.location or "",
|
||||
"d3fend": args.d3fend or "",
|
||||
"token": token_id,
|
||||
"auth": body.get("auth"),
|
||||
"hostname": body.get("hostname"),
|
||||
"url": body.get("url"),
|
||||
"access_key_id": body.get("access_key_id"),
|
||||
"created": datetime.now(timezone.utc).isoformat(),
|
||||
"base_url": args.base_url.rstrip("/"),
|
||||
}
|
||||
inv = _load_inventory()
|
||||
inv.append(record)
|
||||
_save_inventory(inv)
|
||||
|
||||
print(json.dumps({k: v for k, v in record.items() if v is not None}, indent=2))
|
||||
if args.type in ("msword", "adobe_pdf", "aws_keys") and token_id and record["auth"]:
|
||||
dl = (f"{record['base_url']}/download?fmt={args.type}"
|
||||
f"&token={token_id}&auth={record['auth']}")
|
||||
print(f"\nDownload artifact:\n curl -s '{dl}' -o token_artifact")
|
||||
return 0
|
||||
|
||||
|
||||
def history(args):
|
||||
url = args.base_url.rstrip("/") + "/history"
|
||||
try:
|
||||
resp = requests.get(url, params={"token": args.token_id, "auth": args.auth},
|
||||
timeout=args.timeout)
|
||||
resp.raise_for_status()
|
||||
except requests.RequestException as exc:
|
||||
sys.stderr.write(f"ERROR: history request failed: {exc}\n")
|
||||
sys.exit(1)
|
||||
try:
|
||||
print(json.dumps(resp.json(), indent=2))
|
||||
except ValueError:
|
||||
print(resp.text)
|
||||
return 0
|
||||
|
||||
|
||||
def inventory(_args):
|
||||
inv = _load_inventory()
|
||||
if not inv:
|
||||
print("(inventory empty)")
|
||||
return 0
|
||||
print(f"{'TYPE':<12} {'D3FEND':<8} {'MEMO':<40} LOCATION")
|
||||
print("-" * 90)
|
||||
for rec in inv:
|
||||
print(f"{rec.get('type',''):<12} {rec.get('d3fend',''):<8} "
|
||||
f"{(rec.get('memo','') or '')[:40]:<40} {rec.get('location','')}")
|
||||
print(f"\nTotal tokens deployed: {len(inv)}")
|
||||
return 0
|
||||
|
||||
|
||||
def build_parser():
|
||||
p = argparse.ArgumentParser(description="Canarytoken generation, validation and inventory helper.")
|
||||
p.add_argument("--base-url", default=DEFAULT_BASE,
|
||||
help=f"Canarytokens frontend base URL (default {DEFAULT_BASE})")
|
||||
p.add_argument("--timeout", type=int, default=20, help="HTTP timeout seconds")
|
||||
sub = p.add_subparsers(dest="cmd", required=True)
|
||||
|
||||
g = sub.add_parser("generate", help="Create a new canarytoken")
|
||||
g.add_argument("--type", required=True, help="Token type (e.g. http, dns, aws_keys, msword)")
|
||||
g.add_argument("--email", help="Alert email address")
|
||||
g.add_argument("--webhook", help="Alert webhook URL")
|
||||
g.add_argument("--memo", required=True, help="Reminder of where the token is planted")
|
||||
g.add_argument("--location", help="Where the token will be planted (for inventory)")
|
||||
g.add_argument("--d3fend", help="MITRE D3FEND mapping, e.g. D3-DF or D3-DUC")
|
||||
g.set_defaults(func=generate)
|
||||
|
||||
h = sub.add_parser("history", help="Show triggers for a token")
|
||||
h.add_argument("--token-id", required=True)
|
||||
h.add_argument("--auth", required=True)
|
||||
h.set_defaults(func=history)
|
||||
|
||||
i = sub.add_parser("inventory", help="List the local token inventory")
|
||||
i.set_defaults(func=inventory)
|
||||
return p
|
||||
|
||||
|
||||
def main():
|
||||
args = build_parser().parse_args()
|
||||
return args.func(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user