npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
When to Use
Use this skill when:
- Ingesting new commercial or OSINT threat feeds and assessing their signal-to-noise ratio
- Normalizing heterogeneous IOC formats (STIX 2.1, OpenIOC, YARA, Sigma) into a unified schema
- Evaluating feed freshness, fidelity, and relevance to the organization's threat profile
- Building automated enrichment pipelines that correlate IOCs against SIEM events
Do not use this skill for raw packet capture analysis or live incident triage without first establishing a CTI baseline.
Prerequisites
- Access to a Threat Intelligence Platform (TIP) such as ThreatConnect, MISP, or OpenCTI
- API keys for at least one commercial feed (Recorded Future, Mandiant Advantage, or VirusTotal Enterprise)
- TAXII 2.1 client library (taxii2-client Python package or equivalent)
- Role with read/write permissions to the TIP's indicator database
Workflow
Step 1: Enumerate and Prioritize Feed Sources
List all available feeds categorized by type (commercial, government, ISAC, OSINT):
- Commercial: Recorded Future, Mandiant Advantage, CrowdStrike Falcon Intelligence
- Government: CISA AIS (Automated Indicator Sharing), FBI InfraGard, MS-ISAC
- OSINT: AlienVault OTX, Abuse.ch, PhishTank, Emerging Threats
Score each feed on: update frequency, historical accuracy rate, coverage of your sector, and attribution depth. Use a weighted scoring matrix with criteria from NIST SP 800-150 (Guide to Cyber Threat Information Sharing).
Step 2: Ingest via TAXII 2.1 or API
For TAXII-enabled feeds:
taxii2-client discover https://feed.example.com/taxii/
taxii2-client get-collection --collection-id <id> --since 2024-01-01For REST API feeds (e.g., Recorded Future):
- Query
/v2/indicator/searchwithrisk_score_min=65to filter low-confidence IOCs - Apply rate limiting and exponential backoff for API resilience
Step 3: Normalize to STIX 2.1
Convert each IOC to STIX 2.1 objects using the OASIS standard schema:
- IP address →
indicatorobject withpattern: "[ipv4-addr:value = '...']" - Domain →
indicatorwithpattern: "[domain-name:value = '...']" - File hash →
indicatorwithpattern: "[file:hashes.SHA-256 = '...']"
Attach relationship objects linking indicators to threat-actor or malware objects. Use confidence field (0–100) based on source fidelity rating.
Step 4: Deduplicate and Enrich
Run deduplication against existing TIP database using normalized value + type as composite key. Enrich surviving IOCs:
- VirusTotal: detection ratio, sandbox behavior reports
- PassiveTotal (RiskIQ): WHOIS history, passive DNS, SSL certificate chains
- Shodan: banner data, open ports, geographic location
Step 5: Distribute to Consuming Systems
Export enriched indicators via TAXII 2.1 push to SIEM (Splunk, Microsoft Sentinel), firewalls (Palo Alto XSOAR playbooks), and EDR platforms. Set TTL (time-to-live) per indicator type: IP addresses 30 days, domains 90 days, file hashes 1 year.
Key Concepts
| Term | Definition |
|---|---|
| STIX 2.1 | Structured Threat Information Expression — OASIS standard JSON schema for CTI objects including indicators, threat actors, campaigns, and relationships |
| TAXII 2.1 | Trusted Automated eXchange of Intelligence Information — HTTPS-based protocol for sharing STIX content between servers and clients |
| IOC | Indicator of Compromise — observable artifact (IP, domain, hash, URL) that indicates a system may have been breached |
| TLP | Traffic Light Protocol — color-coded classification (RED/AMBER/GREEN/WHITE) defining sharing restrictions for CTI |
| Confidence Score | Numeric value (0–100 in STIX) reflecting the producer's certainty about an indicator's malicious attribution |
| Feed Fidelity | Historical accuracy rate of a feed measured by true positive rate in production detections |
Tools & Systems
- ThreatConnect TC Exchange: Aggregates 100+ commercial and OSINT feeds; provides automated playbooks for IOC enrichment
- MISP (Malware Information Sharing Platform): Open-source TIP supporting STIX/TAXII; widely used by ISACs and government CERTs
- OpenCTI: Open-source platform with native MITRE ATT&CK integration and graph-based relationship visualization
- Recorded Future: Commercial feed with AI-powered risk scoring and real-time dark web monitoring
- taxii2-client: Python library for TAXII 2.0/2.1 client operations (pip install taxii2-client)
- PyMISP: Python API for MISP feed management and IOC submission
Common Pitfalls
- IOC age staleness: IP addresses and domains rotate frequently; applying 1-year-old IOCs generates false positives. Enforce TTL policies.
- Missing context: Blocking an IOC without understanding the associated campaign or adversary can disrupt legitimate business traffic (e.g., CDN IPs shared with malicious actors).
- Feed overlap without deduplication: Ingesting the same IOC from five feeds without deduplication inflates indicator counts and SIEM rule complexity.
- TLP violation: Redistributing RED-classified intelligence outside authorized boundaries violates sharing agreements and trust relationships.
- Over-blocking on low-confidence indicators: Indicators with confidence below 50 should trigger detection-only rules, not blocking, to avoid operational disruption.
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 1
api-reference.md2.3 KB
API Reference: Analyzing Threat Intelligence Feeds
taxii2-client
Server Discovery
from taxii2client.v21 import Server
server = Server("https://cti.example.com/taxii2/", user="u", password="p")
for api_root in server.api_roots:
for col in api_root.collections:
print(col.id, col.title)Fetch Indicators from Collection
from taxii2client.v21 import Collection, as_pages
collection = Collection(
"https://cti.example.com/taxii2/collections/abc123/",
user="u", password="p"
)
for bundle in as_pages(collection.get_objects, per_request=100):
for obj in bundle.get("objects", []):
if obj["type"] == "indicator":
print(obj["pattern"])Push Indicators
collection.add_objects(stix_bundle_json)stix2 (Python Library)
Create Indicator
from stix2 import Indicator
indicator = Indicator(
name="Malicious IP",
pattern="[ipv4-addr:value = '1.2.3.4']",
pattern_type="stix",
valid_from="2025-01-01T00:00:00Z",
confidence=85,
)Create Bundle and Serialize
from stix2 import Bundle
bundle = Bundle(objects=[indicator])
print(bundle.serialize(pretty=True))MemoryStore for Querying
from stix2 import MemoryStore, Filter
store = MemoryStore(stix_data=bundle)
results = store.query([Filter("type", "=", "indicator")])STIX 2.1 Pattern Syntax
| IOC Type | Pattern |
|---|---|
| IPv4 | [ipv4-addr:value = '1.2.3.4'] |
| Domain | [domain-name:value = 'evil.com'] |
| SHA-256 | [file:hashes.'SHA-256' = 'abc...'] |
| URL | [url:value = 'http://evil.com/payload'] |
[email-addr:value = 'phish@evil.com'] |
TAXII 2.1 HTTP Endpoints
| Endpoint | Method | Description |
|---|---|---|
/taxii2/ |
GET | Server discovery |
/{api-root}/collections/ |
GET | List collections |
/{api-root}/collections/{id}/objects/ |
GET | Get STIX objects |
/{api-root}/collections/{id}/objects/ |
POST | Add STIX objects |
References
- taxii2-client: https://pypi.org/project/taxii2-client/
- stix2 library: https://pypi.org/project/stix2/
- STIX 2.1 spec: https://docs.oasis-open.org/cti/stix/v2.1/stix-v2.1.html
- TAXII 2.1 spec: https://docs.oasis-open.org/cti/taxii/v2.1/taxii-v2.1.html
Scripts 1
agent.py6.8 KB
#!/usr/bin/env python3
"""Agent for analyzing threat intelligence feeds via TAXII 2.1 and STIX 2.1."""
import os
import json
import argparse
from datetime import datetime, timedelta, timezone
from taxii2client.v21 import Server, Collection, as_pages
from stix2 import Indicator, Bundle
def discover_taxii_server(url, user=None, password=None):
"""Discover TAXII 2.1 server and list available API roots and collections."""
server = Server(url, user=user, password=password)
info = {"title": server.title, "api_roots": []}
for api_root in server.api_roots:
root_info = {"title": api_root.title, "collections": []}
for collection in api_root.collections:
root_info["collections"].append({
"id": collection.id,
"title": collection.title,
"can_read": collection.can_read,
"can_write": collection.can_write,
})
info["api_roots"].append(root_info)
return info
def fetch_indicators(collection_url, user=None, password=None, added_after=None):
"""Fetch STIX indicators from a TAXII 2.1 collection."""
collection = Collection(collection_url, user=user, password=password)
kwargs = {}
if added_after:
kwargs["added_after"] = added_after
indicators = []
for bundle_resource in as_pages(collection.get_objects, per_request=50, **kwargs):
if "objects" in bundle_resource:
for obj in bundle_resource["objects"]:
if obj.get("type") == "indicator":
indicators.append(obj)
return indicators
def normalize_to_stix(ioc_value, ioc_type, source_name, confidence=50):
"""Convert a raw IOC into a STIX 2.1 Indicator object."""
pattern_map = {
"ipv4": f"[ipv4-addr:value = '{ioc_value}']",
"domain": f"[domain-name:value = '{ioc_value}']",
"sha256": f"[file:hashes.'SHA-256' = '{ioc_value}']",
"url": f"[url:value = '{ioc_value}']",
"email": f"[email-addr:value = '{ioc_value}']",
}
pattern = pattern_map.get(ioc_type)
if not pattern:
raise ValueError(f"Unsupported IOC type: {ioc_type}")
indicator = Indicator(
name=f"{ioc_type.upper()} indicator: {ioc_value}",
pattern=pattern,
pattern_type="stix",
valid_from=datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"),
confidence=confidence,
created_by_ref="identity--f165a29e-a997-5f8a-a63b-4b72b9f2f963",
labels=["malicious-activity"],
external_references=[{
"source_name": source_name,
"description": f"IOC from {source_name}",
}],
)
return indicator
def deduplicate_indicators(indicators):
"""Deduplicate indicators by pattern value."""
seen = set()
unique = []
for ind in indicators:
pattern = ind.get("pattern") if isinstance(ind, dict) else ind.pattern
if pattern not in seen:
seen.add(pattern)
unique.append(ind)
return unique
def score_feed_quality(indicators, known_good_iocs=None):
"""Score feed quality based on indicator attributes."""
total = len(indicators)
if total == 0:
return {"total": 0, "score": 0}
with_confidence = sum(1 for i in indicators if i.get("confidence", 0) > 0)
with_labels = sum(1 for i in indicators if i.get("labels"))
with_refs = sum(1 for i in indicators if i.get("external_references"))
freshness = sum(
1 for i in indicators
if i.get("valid_from") and
datetime.fromisoformat(i["valid_from"].replace("Z", "+00:00"))
> datetime.now(tz=timezone.utc) - timedelta(days=90)
)
score = int(
(with_confidence / total * 25) +
(with_labels / total * 25) +
(with_refs / total * 25) +
(freshness / total * 25)
)
return {
"total": total,
"with_confidence": with_confidence,
"with_labels": with_labels,
"with_external_refs": with_refs,
"fresh_last_90d": freshness,
"quality_score": score,
}
def export_stix_bundle(indicators, output_path):
"""Export indicators as a STIX 2.1 bundle JSON file."""
bundle = Bundle(objects=indicators, allow_custom=True)
with open(output_path, "w") as f:
f.write(bundle.serialize(pretty=True))
return output_path
def classify_ioc_type(value):
"""Auto-detect IOC type from value."""
import re
if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", value):
return "ipv4"
elif re.match(r"^[a-fA-F0-9]{64}$", value):
return "sha256"
elif re.match(r"^https?://", value):
return "url"
elif re.match(r"^[^@]+@[^@]+\.[^@]+$", value):
return "email"
else:
return "domain"
def main():
parser = argparse.ArgumentParser(description="Threat Intelligence Feed Analysis Agent")
parser.add_argument("--taxii-url", help="TAXII 2.1 server discovery URL")
parser.add_argument("--collection-url", help="TAXII collection URL to fetch from")
parser.add_argument("--user", default=os.getenv("TAXII_USER"))
parser.add_argument("--password", default=os.getenv("TAXII_PASSWORD"))
parser.add_argument("--added-after", help="Fetch indicators added after (ISO date)")
parser.add_argument("--ioc-file", help="File with raw IOCs (one per line) to normalize")
parser.add_argument("--source", default="custom-feed", help="Source name for IOCs")
parser.add_argument("--output", default="stix_bundle.json", help="Output STIX bundle path")
parser.add_argument("--action", choices=[
"discover", "fetch", "normalize", "score", "full_pipeline"
], default="full_pipeline")
args = parser.parse_args()
if args.action == "discover" and args.taxii_url:
info = discover_taxii_server(args.taxii_url, args.user, args.password)
print(json.dumps(info, indent=2))
return
if args.action in ("fetch", "full_pipeline") and args.collection_url:
indicators = fetch_indicators(args.collection_url, args.user, args.password, args.added_after)
indicators = deduplicate_indicators(indicators)
quality = score_feed_quality(indicators)
print(f"[+] Fetched {len(indicators)} unique indicators")
print(f"[+] Feed quality score: {quality['quality_score']}/100")
if args.action in ("normalize", "full_pipeline") and args.ioc_file:
stix_objects = []
with open(args.ioc_file) as f:
for line in f:
value = line.strip()
if not value or value.startswith("#"):
continue
ioc_type = classify_ioc_type(value)
indicator = normalize_to_stix(value, ioc_type, args.source)
stix_objects.append(indicator)
path = export_stix_bundle(stix_objects, args.output)
print(f"[+] Normalized {len(stix_objects)} IOCs -> {path}")
if __name__ == "__main__":
main()