npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
Overview
Building a Threat Intelligence Platform (TIP) involves deploying and integrating multiple CTI tools into a unified system for collecting, analyzing, enriching, and disseminating threat intelligence. This skill covers designing TIP architecture using open-source tools (MISP, OpenCTI, TheHive, Cortex), configuring feed ingestion pipelines, establishing enrichment workflows, implementing STIX/TAXII interoperability, and building analyst dashboards for CTI operations.
When to Use
- When deploying or configuring building threat intelligence platform 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
- Docker and Docker Compose for deploying platform components
- Python 3.9+ with
pymisp,pycti,thehive4pylibraries - Elasticsearch/OpenSearch cluster for data storage
- Redis and RabbitMQ for message queuing
- Understanding of STIX 2.1 data model and TAXII 2.1 transport
- API keys for enrichment services (VirusTotal, Shodan, AbuseIPDB)
Key Concepts
TIP Architecture Components
- Collection Layer: Feed ingestion from OSINT, commercial, and internal sources
- Storage Layer: Elasticsearch/OpenSearch for indexed CTI data with STIX 2.1 schema
- Analysis Layer: OpenCTI for knowledge graph analysis and MISP for IOC correlation
- Enrichment Layer: Cortex analyzers for automated IOC enrichment
- Response Layer: TheHive for case management and incident response integration
- Sharing Layer: TAXII server for outbound intelligence sharing
Platform Integration Points
- MISP <-> OpenCTI: Bidirectional sync via OpenCTI MISP connector
- OpenCTI <-> TheHive: Alert/case creation from high-confidence indicators
- TheHive <-> Cortex: Automated analysis and enrichment of case observables
- All <-> SIEM: Real-time IOC push to Splunk/Elastic via API or Kafka
Workflow
Step 1: Deploy Platform with Docker Compose
version: '3.8'
services:
# --- Storage Layer ---
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms2g -Xmx2g"
ports:
- "9200:9200"
volumes:
- es-data:/usr/share/elasticsearch/data
redis:
image: redis:7
ports:
- "6379:6379"
rabbitmq:
image: rabbitmq:3-management
ports:
- "5672:5672"
- "15672:15672"
minio:
image: minio/minio
command: server /data --console-address ":9001"
ports:
- "9000:9000"
- "9001:9001"
# --- MISP ---
misp:
image: ghcr.io/misp/misp-docker/misp-core:latest
ports:
- "8443:443"
environment:
- MISP_ADMIN_EMAIL=admin@tip.local
- MISP_BASEURL=https://localhost:8443
volumes:
- misp-data:/var/www/MISP/app/files
# --- OpenCTI ---
opencti:
image: opencti/platform:6.4.4
environment:
- APP__PORT=8080
- APP__ADMIN__EMAIL=admin@tip.local
- APP__ADMIN__PASSWORD=TIPAdminPassword
- APP__ADMIN__TOKEN=tip-opencti-token-uuid
- ELASTICSEARCH__URL=http://elasticsearch:9200
- MINIO__ENDPOINT=minio
- RABBITMQ__HOSTNAME=rabbitmq
- REDIS__HOSTNAME=redis
ports:
- "8080:8080"
depends_on:
- elasticsearch
- redis
- rabbitmq
- minio
# --- TheHive ---
thehive:
image: strangebee/thehive:5.3
environment:
- TH_CORTEX_URL=http://cortex:9001
ports:
- "9000:9000"
depends_on:
- elasticsearch
# --- Cortex ---
cortex:
image: thehiveproject/cortex:3.1.8
ports:
- "9001:9001"
depends_on:
- elasticsearch
volumes:
es-data:
misp-data:Step 2: Configure Feed Ingestion Pipeline
from pymisp import PyMISP
from pycti import OpenCTIApiClient
import json
class TIPFeedManager:
"""Manage threat intelligence feed ingestion across platform components."""
def __init__(self, misp_url, misp_key, opencti_url, opencti_token):
self.misp = PyMISP(misp_url, misp_key, ssl=False)
self.opencti = OpenCTIApiClient(opencti_url, opencti_token)
def configure_osint_feeds(self):
"""Enable default OSINT feeds in MISP."""
osint_feeds = [
{"name": "CIRCL OSINT", "id": 1},
{"name": "Botvrij.eu", "id": 2},
{"name": "abuse.ch URLhaus", "id": 5},
{"name": "abuse.ch Feodo Tracker", "id": 6},
]
for feed in osint_feeds:
try:
self.misp.enable_feed(feed["id"])
self.misp.fetch_feed(feed["id"])
print(f"[+] Enabled feed: {feed['name']}")
except Exception as e:
print(f"[-] Failed: {feed['name']}: {e}")
def configure_opencti_connectors(self):
"""List and verify OpenCTI connector status."""
connectors = self.opencti.connector.list()
for conn in connectors:
print(
f" Connector: {conn['name']} - "
f"Active: {conn['active']} - "
f"Type: {conn['connector_type']}"
)
def sync_misp_to_opencti(self):
"""Verify MISP-OpenCTI sync is operational."""
# OpenCTI MISP connector handles this automatically
# Check connector status
connectors = self.opencti.connector.list()
misp_connector = [
c for c in connectors if "misp" in c["name"].lower()
]
if misp_connector:
print(f"[+] MISP connector active: {misp_connector[0]['active']}")
else:
print("[-] MISP connector not found - configure in Docker Compose")Step 3: Build Enrichment Pipeline with Cortex
import requests
class CortexEnrichment:
"""Integrate Cortex analyzers for automated enrichment."""
def __init__(self, cortex_url, cortex_key):
self.url = cortex_url
self.headers = {"Authorization": f"Bearer {cortex_key}"}
def list_analyzers(self):
"""List available Cortex analyzers."""
resp = requests.get(
f"{self.url}/api/analyzer",
headers=self.headers,
timeout=30,
)
if resp.status_code == 200:
analyzers = resp.json()
for a in analyzers:
print(f" {a['name']}: {a.get('description', '')[:60]}")
return analyzers
return []
def analyze_observable(self, observable_type, observable_value, analyzer_id):
"""Submit an observable for analysis."""
job = {
"data": observable_value,
"dataType": observable_type,
"tlp": 2,
"message": "TIP automated enrichment",
}
resp = requests.post(
f"{self.url}/api/analyzer/{analyzer_id}/run",
json=job,
headers=self.headers,
timeout=30,
)
if resp.status_code == 200:
return resp.json()
return None
def get_job_report(self, job_id):
"""Get the report for a completed analysis job."""
resp = requests.get(
f"{self.url}/api/job/{job_id}/report",
headers=self.headers,
timeout=60,
)
if resp.status_code == 200:
return resp.json()
return NoneStep 4: Implement Analyst Dashboard Metrics
class TIPMetrics:
"""Collect platform metrics for analyst dashboards."""
def __init__(self, misp, opencti):
self.misp = misp
self.opencti = opencti
def get_platform_stats(self):
"""Collect statistics across all platform components."""
stats = {}
# MISP stats
misp_stats = self.misp.get_server_statistics()
stats["misp"] = {
"total_events": misp_stats.get("event_count", 0),
"total_attributes": misp_stats.get("attribute_count", 0),
"active_feeds": len([
f for f in self.misp.feeds()
if f.get("Feed", {}).get("enabled")
]),
}
# OpenCTI stats via GraphQL
stats["opencti"] = {
"total_indicators": self.opencti.indicator.list(
first=0, withPagination=True
).get("pagination", {}).get("globalCount", 0),
"total_reports": self.opencti.report.list(
first=0, withPagination=True
).get("pagination", {}).get("globalCount", 0),
}
return statsValidation Criteria
- All platform components (MISP, OpenCTI, TheHive, Cortex) deployed and accessible
- MISP-OpenCTI bidirectional sync operational
- At least 3 OSINT feeds ingesting data
- Cortex analyzers configured and returning enrichment results
- Platform metrics dashboard showing real-time statistics
- STIX/TAXII export functional for intelligence sharing
References
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md1.9 KB
API Reference: Threat Intelligence Platform
STIX 2.1 Indicator Object
{
"type": "indicator",
"spec_version": "2.1",
"id": "indicator--<uuid5>",
"created": "2025-01-15T10:00:00.000Z",
"modified": "2025-01-15T10:00:00.000Z",
"name": "Malicious IP",
"pattern": "[ipv4-addr:value = '198.51.100.42']",
"pattern_type": "stix",
"valid_from": "2025-01-15T10:00:00.000Z",
"confidence": 85,
"object_marking_refs": ["marking-definition--f88d31f6-486f-44da-b317-01333bde0b82"]
}TLP Marking Definition IDs (STIX 2.1)
| TLP Level | STIX Marking Definition ID |
|---|---|
| TLP:CLEAR | marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9 |
| TLP:GREEN | marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da |
| TLP:AMBER | marking-definition--f88d31f6-486f-44da-b317-01333bde0b82 |
| TLP:AMBER+STRICT | marking-definition--826578e1-40a3-4b46-a8d8-b9931fdd750e |
| TLP:RED | marking-definition--5e57c739-391a-4eb3-b6be-7d15ca92d5ed |
TAXII 2.1 Endpoints
# Discovery
curl https://taxii.server.com/taxii2/
# Collections
curl https://taxii.server.com/taxii2/collections/
# Get objects from collection
curl "https://taxii.server.com/taxii2/collections/{id}/objects?type=indicator"
# Add objects
curl -X POST "https://taxii.server.com/taxii2/collections/{id}/objects" \
-H "Content-Type: application/stix+json;version=2.1" \
-d @bundle.jsonOpenCTI GraphQL API
mutation {
indicatorAdd(input: {
name: "Malicious IP"
pattern: "[ipv4-addr:value = '198.51.100.42']"
pattern_type: "stix"
x_opencti_score: 80
}) {
id
standard_id
}
}MISP REST API
# Add attribute
curl -X POST "https://misp/attributes/add/EVENT_ID" \
-H "Authorization: MISP_KEY" \
-H "Content-Type: application/json" \
-d '{"type":"ip-dst","value":"198.51.100.42","category":"Network activity","to_ids":true}'standards.md1.2 KB
Standards and Frameworks Reference
TIP Architecture Standards
- STIX 2.1: Native data model for CTI representation
- TAXII 2.1: Transport protocol for CTI sharing
- MITRE ATT&CK: Technique taxonomy for TTP mapping
- Diamond Model: Intrusion analysis framework
- Kill Chain: Lockheed Martin Cyber Kill Chain for attack phase tracking
Platform Component Standards
| Component | Protocol | Data Format |
|---|---|---|
| MISP | REST API | MISP JSON, STIX 2.1 |
| OpenCTI | GraphQL API | STIX 2.1 |
| TheHive | REST API | TheHive JSON |
| Cortex | REST API | Cortex Report JSON |
| Elasticsearch | REST API | JSON |
Integration Standards
- MISP Sync Protocol: Push/Pull over HTTPS with API key auth
- OpenCTI Connectors: RabbitMQ-based message queue for async processing
- Cortex Analyzers: Docker-based analyzers with standardized I/O
- SIEM Integration: Syslog, Kafka, REST API, or file-based export
References
workflows.md1.5 KB
TIP Architecture Workflows
Workflow 1: End-to-End Intelligence Pipeline
[External Feeds] --> [MISP] --> [OpenCTI] --> [Enrichment (Cortex)] --> [SIEM/TheHive]
| | | | |
v v v v v
OSINT/Commercial Correlate Knowledge Graph VT/Shodan/AIPDB Alerts/CasesWorkflow 2: Incident-to-Intelligence Feedback Loop
[SOC Alert] --> [TheHive Case] --> [Cortex Analysis] --> [IOC Extraction]
|
v
[MISP Event Creation]
|
v
[OpenCTI Knowledge Update]
|
v
[Updated Detections --> SIEM]Workflow 3: Platform Health Monitoring
[Prometheus/Grafana] --> [Component Health] --> [Feed Status] --> [Alert on Failure]
| |
v v
[ES Cluster Health] [Connector Status]Scripts 2
agent.py5.7 KB
#!/usr/bin/env python3
"""Threat intelligence platform builder.
Core TIP components: STIX/TAXII ingestion, indicator lifecycle management,
confidence scoring, sharing groups, and intelligence dissemination.
"""
import json
import datetime
import re
import uuid
STIX_INDICATOR_TYPES = {
"ipv4-addr": "[ipv4-addr:value = '{}']",
"domain-name": "[domain-name:value = '{}']",
"url": "[url:value = '{}']",
"file-sha256": "[file:hashes.'SHA-256' = '{}']",
"file-md5": "[file:hashes.MD5 = '{}']",
"email-addr": "[email-addr:value = '{}']",
}
TLP_DEFINITIONS = {
"TLP:CLEAR": {"color": "white", "sharing": "Unlimited", "code": 0},
"TLP:GREEN": {"color": "green", "sharing": "Community", "code": 1},
"TLP:AMBER": {"color": "amber", "sharing": "Organization", "code": 2},
"TLP:AMBER+STRICT": {"color": "amber", "sharing": "Need-to-know only", "code": 3},
"TLP:RED": {"color": "red", "sharing": "Named recipients only", "code": 4},
}
def classify_indicator(value):
"""Classify indicator type from raw value."""
if re.match(r"^[0-9]{1,3}(\\.[0-9]{1,3}){3}$", value):
return "ipv4-addr"
if re.match(r"^[a-fA-F0-9]{64}$", value):
return "file-sha256"
if re.match(r"^[a-fA-F0-9]{32}$", value):
return "file-md5"
if re.match(r"^[a-zA-Z0-9][a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", value):
return "domain-name"
if value.startswith("http://") or value.startswith("https://"):
return "url"
if "@" in value:
return "email-addr"
return "unknown"
def create_stix_indicator(value, indicator_type=None, confidence=50, tlp="TLP:AMBER"):
"""Create a STIX 2.1 Indicator object."""
if not indicator_type:
indicator_type = classify_indicator(value)
pattern_template = STIX_INDICATOR_TYPES.get(indicator_type)
if not pattern_template:
return {"error": "Unsupported indicator type: " + indicator_type}
now = datetime.datetime.utcnow().isoformat(timespec="milliseconds") + "Z"
indicator_id = "indicator--" + str(uuid.uuid5(uuid.NAMESPACE_URL, value))
indicator = {
"type": "indicator",
"spec_version": "2.1",
"id": indicator_id,
"created": now,
"modified": now,
"name": "{}: {}".format(indicator_type, value),
"pattern": pattern_template.format(value),
"pattern_type": "stix",
"valid_from": now,
"confidence": confidence,
"labels": ["malicious-activity"],
"object_marking_refs": [tlp_to_marking_ref(tlp)],
}
return indicator
def tlp_to_marking_ref(tlp):
"""Convert TLP label to STIX marking definition ID."""
tlp_refs = {
"TLP:CLEAR": "marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9",
"TLP:GREEN": "marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da",
"TLP:AMBER": "marking-definition--f88d31f6-486f-44da-b317-01333bde0b82",
"TLP:AMBER+STRICT": "marking-definition--826578e1-40a3-4b46-a8d8-b9931fdd750e",
"TLP:RED": "marking-definition--5e57c739-391a-4eb3-b6be-7d15ca92d5ed",
}
return tlp_refs.get(tlp, tlp_refs["TLP:AMBER"])
def calculate_indicator_score(sources_count, age_days, confirmed_sightings, false_positives):
"""Calculate indicator confidence score (0-100)."""
source_score = min(sources_count * 15, 40)
age_penalty = min(age_days * 0.5, 30)
sighting_score = min(confirmed_sightings * 10, 30)
fp_penalty = min(false_positives * 15, 30)
score = source_score - age_penalty + sighting_score - fp_penalty
return max(0, min(100, round(score)))
def build_stix_bundle(indicators):
"""Build STIX 2.1 Bundle from list of indicators."""
bundle = {
"type": "bundle",
"id": "bundle--" + str(uuid.uuid4()),
"objects": indicators,
}
return bundle
def generate_tip_report(indicators, platform_name="Internal TIP"):
"""Generate TIP status report."""
type_counts = {}
for ind in indicators:
itype = ind.get("name", "").split(":")[0] if ":" in ind.get("name", "") else "unknown"
type_counts[itype] = type_counts.get(itype, 0) + 1
return {
"platform": platform_name,
"generated_at": datetime.datetime.utcnow().isoformat() + "Z",
"total_indicators": len(indicators),
"type_breakdown": type_counts,
"avg_confidence": round(
sum(i.get("confidence", 0) for i in indicators) / max(len(indicators), 1), 1
),
}
if __name__ == "__main__":
print("=" * 60)
print("Threat Intelligence Platform Builder")
print("STIX 2.1 indicators, scoring, TLP marking, bundle export")
print("=" * 60)
demo_values = [
"198.51.100.42",
"evil-domain.example.com",
"a" * 64,
"https://evil.example.com/payload.exe",
"attacker@evil.example.com",
]
indicators = []
for val in demo_values:
ind = create_stix_indicator(val, confidence=75, tlp="TLP:AMBER")
if "error" not in ind:
indicators.append(ind)
print("\n--- Indicators Created ---")
for ind in indicators:
print(" {} [confidence={}]".format(ind["name"], ind["confidence"]))
print(" Pattern: {}".format(ind["pattern"]))
bundle = build_stix_bundle(indicators)
print("\nSTIX Bundle: {} ({} objects)".format(bundle["id"], len(bundle["objects"])))
score = calculate_indicator_score(sources_count=3, age_days=5, confirmed_sightings=2, false_positives=0)
print("\nSample score calculation: {}".format(score))
report = generate_tip_report(indicators)
print("\n--- Platform Report ---")
for k, v in report.items():
print(" {}: {}".format(k, v))
print("\n" + json.dumps({"indicators_created": len(indicators)}, indent=2))
process.py6.2 KB
#!/usr/bin/env python3
"""
Threat Intelligence Platform Management Script
Manages a multi-component TIP deployment:
- Checks platform component health
- Configures feed ingestion across MISP and OpenCTI
- Runs enrichment pipelines via Cortex analyzers
- Generates platform metrics and dashboards
Requirements:
pip install pymisp pycti requests
Usage:
python process.py --check-health --misp-url URL --misp-key KEY --opencti-url URL --opencti-token TOKEN
python process.py --configure-feeds --misp-url URL --misp-key KEY
python process.py --platform-stats --misp-url URL --misp-key KEY --opencti-url URL --opencti-token TOKEN
"""
import argparse
import json
import sys
from datetime import datetime
import requests
try:
from pymisp import PyMISP
except ImportError:
PyMISP = None
try:
from pycti import OpenCTIApiClient
except ImportError:
OpenCTIApiClient = None
class TIPManager:
"""Manage Threat Intelligence Platform operations."""
def __init__(self, misp_url="", misp_key="", opencti_url="", opencti_token="",
thehive_url="", thehive_key="", cortex_url="", cortex_key=""):
self.misp = PyMISP(misp_url, misp_key, ssl=False) if PyMISP and misp_url else None
self.opencti = (
OpenCTIApiClient(opencti_url, opencti_token)
if OpenCTIApiClient and opencti_url else None
)
self.thehive_url = thehive_url
self.thehive_key = thehive_key
self.cortex_url = cortex_url
self.cortex_key = cortex_key
def check_health(self) -> dict:
"""Check health of all platform components."""
health = {}
if self.misp:
try:
version = self.misp.misp_instance_version
health["misp"] = {"status": "healthy", "version": str(version)}
except Exception as e:
health["misp"] = {"status": "unhealthy", "error": str(e)}
if self.opencti:
try:
about = self.opencti.health.check()
health["opencti"] = {"status": "healthy"}
except Exception as e:
health["opencti"] = {"status": "unhealthy", "error": str(e)}
if self.thehive_url:
try:
resp = requests.get(
f"{self.thehive_url}/api/status",
headers={"Authorization": f"Bearer {self.thehive_key}"},
timeout=10,
)
health["thehive"] = {
"status": "healthy" if resp.status_code == 200 else "unhealthy"
}
except Exception as e:
health["thehive"] = {"status": "unreachable", "error": str(e)}
if self.cortex_url:
try:
resp = requests.get(
f"{self.cortex_url}/api/status",
headers={"Authorization": f"Bearer {self.cortex_key}"},
timeout=10,
)
health["cortex"] = {
"status": "healthy" if resp.status_code == 200 else "unhealthy"
}
except Exception as e:
health["cortex"] = {"status": "unreachable", "error": str(e)}
return health
def configure_feeds(self) -> dict:
"""Configure default OSINT feeds in MISP."""
if not self.misp:
return {"error": "MISP not configured"}
feeds = self.misp.feeds()
enabled = []
for feed in feeds:
feed_info = feed.get("Feed", {})
if not feed_info.get("enabled"):
try:
self.misp.enable_feed(feed_info["id"])
enabled.append(feed_info["name"])
except Exception:
pass
return {"enabled_feeds": enabled, "total_feeds": len(feeds)}
def get_platform_stats(self) -> dict:
"""Collect statistics from all platform components."""
stats = {"timestamp": datetime.utcnow().isoformat()}
if self.misp:
try:
server_stats = self.misp.get_server_statistics()
feeds = self.misp.feeds()
stats["misp"] = {
"events": server_stats.get("event_count", 0),
"attributes": server_stats.get("attribute_count", 0),
"active_feeds": len([
f for f in feeds if f.get("Feed", {}).get("enabled")
]),
"organizations": server_stats.get("org_count", 0),
}
except Exception as e:
stats["misp"] = {"error": str(e)}
if self.opencti:
try:
connectors = self.opencti.connector.list()
stats["opencti"] = {
"active_connectors": len([
c for c in connectors if c.get("active")
]),
"total_connectors": len(connectors),
}
except Exception as e:
stats["opencti"] = {"error": str(e)}
return stats
def main():
parser = argparse.ArgumentParser(description="TIP Management Tool")
parser.add_argument("--misp-url", default="", help="MISP URL")
parser.add_argument("--misp-key", default="", help="MISP API key")
parser.add_argument("--opencti-url", default="", help="OpenCTI URL")
parser.add_argument("--opencti-token", default="", help="OpenCTI token")
parser.add_argument("--check-health", action="store_true")
parser.add_argument("--configure-feeds", action="store_true")
parser.add_argument("--platform-stats", action="store_true")
parser.add_argument("--output", default="tip_report.json", help="Output file")
args = parser.parse_args()
manager = TIPManager(args.misp_url, args.misp_key, args.opencti_url, args.opencti_token)
result = {}
if args.check_health:
result = manager.check_health()
elif args.configure_feeds:
result = manager.configure_feeds()
elif args.platform_stats:
result = manager.get_platform_stats()
print(json.dumps(result, indent=2, default=str))
with open(args.output, "w") as f:
json.dump(result, f, indent=2, default=str)
if __name__ == "__main__":
main()