npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
Overview
Canary Tokens are lightweight tripwire mechanisms that alert when an attacker accesses a resource. This skill uses the Thinkst Canary REST API to programmatically create tokens (web bugs, DNS tokens, MS Word documents, AWS API keys), deploy them to strategic locations, monitor for triggered alerts, and generate deception coverage reports.
When to Use
- When deploying or configuring implementing deception based detection with canarytoken capabilities in your environment
- When establishing security controls aligned to compliance requirements
- When building or improving security architecture for this domain
- When conducting security assessments that require this implementation
Prerequisites
- Thinkst Canary Console or canarytokens.org account
- API auth token from Canary Console
- Python 3.9+ with
requests - File system access for deploying document and file tokens
Steps
- Authenticate to the Canary Console API using auth_token
- Create web bug (HTTP) tokens for embedding in documents and web pages
- Create DNS tokens for monitoring DNS resolution attempts
- Create MS Word document tokens for file share deployment
- List all active tokens and their trigger history
- Query recent alerts for triggered token events
- Generate deception coverage report with deployment recommendations
Expected Output
- JSON report listing all deployed Canary Tokens, trigger history, alert details, and coverage analysis
- Deployment map showing token types across network segments
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 1
api-reference.md2.9 KB
API Reference — Implementing Deception-Based Detection with Canarytoken
Libraries Used
- requests: HTTP client for Thinkst Canary Console REST API
- json: JSON serialization for audit reports
CLI Interface
python agent.py --console abc123 --auth-token TOKEN ping
python agent.py --console abc123 --auth-token TOKEN list
python agent.py --console abc123 --auth-token TOKEN alerts
python agent.py --console abc123 --auth-token TOKEN create --kind http --memo "Web server token"
python agent.py --console abc123 --auth-token TOKEN create --kind dns --memo "DNS honeypot"
python agent.py --console abc123 --auth-token TOKEN coverage
python agent.py --console abc123 --auth-token TOKEN fullCore Functions
CanaryClient(console_domain, auth_token) — API client
Base URL: https://{console_domain}.canary.tools/api/v1
Auth: auth_token parameter on every request.
create_token(kind, memo, **kwargs) — Create Canarytoken
POST /canarytoken/create with kind, memo, auth_token.
For doc-msword: uploads file via multipart form with MIME type
application/vnd.openxmlformats-officedocument.wordprocessingml.document.
list_tokens() — List all deployed tokens
GET /canarytokens/fetch. Returns array of token objects with kind, memo, url, enabled.
get_alerts(newer_than) — Fetch triggered token alerts
GET /incidents/all. Optional newer_than timestamp filter.
Returns src_host (source IP), description, timestamp, acknowledged status.
ack_alert(incident_id) — Acknowledge an alert
POST /incident/acknowledge with incident ID.
audit_token_coverage(client) — Coverage analysis
Calculates: tokens by kind, triggered vs untriggered, missing token types, coverage score as percentage of TOKEN_KINDS deployed.
full_audit(client) — Comprehensive deception audit
Canary Console API Endpoints
| Endpoint | Method | Description |
|---|---|---|
/ping |
GET | Test API connectivity |
/canarytoken/create |
POST | Create new token |
/canarytokens/fetch |
GET | List all tokens |
/canarytoken/fetch |
GET | Get specific token |
/canarytoken/delete |
POST | Delete a token |
/incidents/all |
GET | Fetch all alerts |
/canarytoken/incidents |
GET | Alerts for specific token |
/incident/acknowledge |
POST | Acknowledge alert |
Supported Token Types
| Kind | Description |
|---|---|
| http | Web bug — triggers on HTTP request |
| dns | DNS token — triggers on DNS resolution |
| doc-msword | MS Word document with embedded beacon |
| pdf-acrobat-reader | PDF with embedded beacon |
| aws-id | Fake AWS API key pair |
| web-image | Image with tracking pixel |
| cloned-web | Cloned website detection |
| qr-code | QR code with tracking URL |
| sensitive-cmd | Triggers on command execution |
| windows-dir | Windows folder open detection |
Dependencies
requests>= 2.28.0- Thinkst Canary Console account with API auth token
Scripts 1
agent.py8.1 KB
#!/usr/bin/env python3
"""Agent for deploying and monitoring Canary Tokens via the Thinkst Canary API."""
import json
import argparse
from datetime import datetime
try:
import requests
except ImportError:
requests = None
TOKEN_KINDS = {
"http": "http",
"dns": "dns",
"doc-msword": "doc-msword",
"pdf-acrobat-reader": "pdf-acrobat-reader",
"web-image": "web-image",
"cloned-web": "cloned-web",
"aws-id": "aws-id",
"qr-code": "qr-code",
"sql": "sql",
"svn": "svn",
"smtp": "smtp",
"windows-dir": "windows-dir",
"sensitive-cmd": "sensitive-cmd",
}
class CanaryClient:
"""Client for the Thinkst Canary Console REST API."""
def __init__(self, console_domain, auth_token):
self.base_url = f"https://{console_domain}.canary.tools/api/v1"
self.auth_token = auth_token
def _get(self, endpoint, params=None):
params = params or {}
params["auth_token"] = self.auth_token
resp = requests.get(f"{self.base_url}{endpoint}", params=params, timeout=30)
resp.raise_for_status()
return resp.json()
def _post(self, endpoint, data=None, files=None):
data = data or {}
data["auth_token"] = self.auth_token
resp = requests.post(f"{self.base_url}{endpoint}", data=data, files=files, timeout=30)
resp.raise_for_status()
return resp.json()
def ping(self):
"""Test API connectivity."""
return self._get("/ping")
def create_token(self, kind, memo, **kwargs):
"""Create a new Canarytoken.
Args:
kind: Token type (http, dns, doc-msword, aws-id, etc.)
memo: Description/reminder for the token
**kwargs: Additional parameters (e.g., cloned_web for cloned-web type)
"""
data = {"kind": kind, "memo": memo}
data.update(kwargs)
files = None
if kind == "doc-msword" and "doc" in kwargs:
doc_path = kwargs.pop("doc")
data.pop("doc", None)
files = {"doc": (doc_path, open(doc_path, "rb"),
"application/vnd.openxmlformats-officedocument.wordprocessingml.document")}
return self._post("/canarytoken/create", data=data, files=files)
def list_tokens(self):
"""List all Canarytokens on the console."""
return self._get("/canarytokens/fetch")
def get_token(self, canarytoken):
"""Get details for a specific token."""
return self._get("/canarytoken/fetch", params={"canarytoken": canarytoken})
def delete_token(self, canarytoken):
"""Delete a Canarytoken."""
return self._post("/canarytoken/delete", data={"canarytoken": canarytoken})
def get_alerts(self, newer_than=None):
"""Fetch recent alerts from triggered tokens."""
params = {}
if newer_than:
params["newer_than"] = newer_than
return self._get("/incidents/all", params=params)
def get_token_alerts(self, canarytoken):
"""Fetch alerts for a specific token."""
return self._get("/canarytoken/incidents", params={"canarytoken": canarytoken})
def ack_alert(self, incident_id):
"""Acknowledge an alert."""
return self._post("/incident/acknowledge", data={"incident": incident_id})
def create_deployment(client, deployment_plan):
"""Create multiple tokens based on a deployment plan."""
results = []
for token_spec in deployment_plan:
kind = token_spec.get("kind", "http")
memo = token_spec.get("memo", f"Canarytoken - {kind}")
extra = {k: v for k, v in token_spec.items() if k not in ("kind", "memo")}
try:
resp = client.create_token(kind, memo, **extra)
results.append({
"kind": kind,
"memo": memo,
"status": "CREATED",
"canarytoken": resp.get("canarytoken", {}).get("canarytoken", ""),
"url": resp.get("canarytoken", {}).get("url", ""),
})
except Exception as e:
results.append({"kind": kind, "memo": memo, "status": "FAILED", "error": str(e)})
return results
def audit_token_coverage(client):
"""Audit deployed token coverage and generate report."""
tokens_resp = client.list_tokens()
tokens = tokens_resp.get("tokens", [])
alerts_resp = client.get_alerts()
alerts = alerts_resp.get("incidents", [])
kind_counts = {}
triggered_tokens = set()
for token in tokens:
kind = token.get("kind", "unknown")
kind_counts[kind] = kind_counts.get(kind, 0) + 1
for alert in alerts:
triggered_tokens.add(alert.get("canarytoken", ""))
untriggered = [t for t in tokens if t.get("canarytoken", "") not in triggered_tokens]
recommended_types = []
for kind_name in TOKEN_KINDS:
if kind_name not in kind_counts:
recommended_types.append(kind_name)
return {
"total_tokens": len(tokens),
"total_alerts": len(alerts),
"tokens_by_kind": kind_counts,
"triggered_token_count": len(triggered_tokens),
"untriggered_tokens": len(untriggered),
"missing_token_types": recommended_types,
"coverage_score": round(len(kind_counts) / len(TOKEN_KINDS) * 100, 1),
}
def full_audit(client):
"""Run comprehensive Canarytoken deployment audit."""
coverage = audit_token_coverage(client)
tokens_resp = client.list_tokens()
tokens = tokens_resp.get("tokens", [])
alerts_resp = client.get_alerts()
alerts = alerts_resp.get("incidents", [])
token_details = []
for t in tokens[:30]:
token_details.append({
"canarytoken": t.get("canarytoken"),
"kind": t.get("kind"),
"memo": t.get("memo"),
"created": t.get("created_printable"),
"enabled": t.get("enabled"),
"url": t.get("url", ""),
})
alert_details = []
for a in alerts[:20]:
alert_details.append({
"incident_id": a.get("id"),
"description": a.get("description"),
"source_ip": a.get("src_host"),
"timestamp": a.get("created_printable"),
"canarytoken": a.get("canarytoken"),
"acknowledged": a.get("acknowledged"),
})
return {
"audit_type": "Canarytoken Deception Coverage Audit",
"timestamp": datetime.utcnow().isoformat(),
"coverage": coverage,
"deployed_tokens": token_details,
"recent_alerts": alert_details,
"recommendation": "Deploy missing token types to improve coverage"
if coverage["coverage_score"] < 50 else "Good coverage — review untriggered tokens",
}
def main():
parser = argparse.ArgumentParser(description="Canarytoken Deception Detection Agent")
parser.add_argument("--console", required=True, help="Canary Console domain (e.g., abc123)")
parser.add_argument("--auth-token", required=True, help="API auth token")
sub = parser.add_subparsers(dest="command")
sub.add_parser("ping", help="Test API connectivity")
sub.add_parser("list", help="List all deployed tokens")
sub.add_parser("alerts", help="Fetch recent alerts")
p_create = sub.add_parser("create", help="Create a new token")
p_create.add_argument("--kind", required=True, choices=list(TOKEN_KINDS.keys()))
p_create.add_argument("--memo", required=True)
sub.add_parser("coverage", help="Audit token coverage")
sub.add_parser("full", help="Full deception audit")
args = parser.parse_args()
client = CanaryClient(args.console, args.auth_token)
if args.command == "ping":
result = client.ping()
elif args.command == "list":
result = client.list_tokens()
elif args.command == "alerts":
result = client.get_alerts()
elif args.command == "create":
result = client.create_token(args.kind, args.memo)
elif args.command == "coverage":
result = audit_token_coverage(client)
elif args.command == "full" or args.command is None:
result = full_audit(client)
else:
parser.print_help()
return
print(json.dumps(result, indent=2, default=str))
if __name__ == "__main__":
main()