threat intelligence

Performing Threat Intelligence Sharing with MISP

Use PyMISP to create, enrich, and share threat intelligence events on a MISP platform, including IOC management, feed integration, STIX export, and community sharing workflows.

information-sharingioc-sharingmisppymispstixtaxiithreat-feedsthreat-intelligence
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

MISP (Malware Information Sharing Platform) is an open-source threat intelligence platform designed for collecting, storing, distributing, and sharing cybersecurity indicators and threat information. PyMISP is the official Python library for interacting with MISP instances via the REST API, enabling programmatic event creation, attribute management, tag assignment, galaxy cluster attachment, and feed synchronization. This skill covers using PyMISP to create events with structured IOCs (IP addresses, domains, file hashes, URLs), enrich events with MITRE ATT&CK tags, manage sharing groups and distribution levels, search for existing intelligence, and export in STIX 2.1 format for interoperability with other platforms.

When to Use

  • When conducting security assessments that involve performing threat intelligence sharing with misp
  • When following incident response procedures for related security events
  • When performing scheduled security testing or auditing activities
  • When validating security controls through hands-on testing

Prerequisites

  • MISP instance (v2.4+) with API access enabled
  • Python 3.9+ with pymisp (pip install pymisp)
  • MISP API key (Settings > Auth Keys)
  • Understanding of MISP data model (Events, Attributes, Objects, Tags, Galaxies)
  • Knowledge of TLP marking and sharing protocols

Steps

  1. Install PyMISP: pip install pymisp
  2. Initialize ExpandedPyMISP(url, key, ssl=True) connection
  3. Create a MISPEvent with info, distribution level, threat level, and analysis status
  4. Add attributes via event.add_attribute(type, value) for IPs, domains, hashes
  5. Apply TLP tags and MITRE ATT&CK technique tags
  6. Publish the event with misp.publish(event)
  7. Search existing events with misp.search(controller='events', value=..., type_attribute=...)
  8. Enable and configure threat feeds for automatic IOC ingestion
  9. Export events in STIX 2.1 format for cross-platform sharing
  10. Validate sharing group configuration and sync server settings

Expected Output

A JSON report summarizing events created, attributes added, tags applied, feed sync status, and any correlation hits against existing intelligence, with event IDs and distribution metadata.

Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 1

api-reference.md2.7 KB

MISP / PyMISP API Reference

Installation

pip install pymisp

Connection Setup

from pymisp import PyMISP, MISPEvent, MISPAttribute
 
misp = PyMISP(
    url="https://misp.example.com",
    key="YOUR_API_KEY",
    ssl=True
)

Core PyMISP Methods

Method Description
misp.add_event(event) Create new event
misp.update_event(event) Update existing event
misp.publish(event) Publish event for sharing
misp.delete_event(event_id) Delete an event
misp.search(controller, value, type_attribute) Search events/attributes
misp.get_event(event_id) Retrieve single event
misp.add_tag(event, tag) Add tag to event
misp.search_index(published=True) Search event index

Creating Events

event = MISPEvent()
event.info = "APT Campaign - Phishing IOCs"
event.distribution = 1        # 0=Org, 1=Community, 2=Connected, 3=All
event.threat_level_id = 2     # 1=High, 2=Medium, 3=Low, 4=Undefined
event.analysis = 0            # 0=Initial, 1=Ongoing, 2=Complete
 
event.add_attribute("ip-dst", "203.0.113.50", to_ids=True, comment="C2 server")
event.add_attribute("domain", "evil.example.com", to_ids=True)
event.add_attribute("sha256", "a1b2c3d4...", category="Payload delivery")
event.add_tag("tlp:amber")
event.add_tag("mitre-attack-pattern:T1566 - Phishing")
 
result = misp.add_event(event)

Searching Intelligence

# Search by attribute value
results = misp.search(controller="attributes", value="203.0.113.50", type_attribute="ip-dst")
 
# Search events by date range
results = misp.search(controller="events", date_from="2025-01-01", date_to="2025-12-31")
 
# Search with tags
results = misp.search(controller="events", tags=["tlp:white", "ransomware"])

MISP Attribute Types

Type Example Category
ip-dst 203.0.113.50 Network activity
domain evil.example.com Network activity
url https://evil.com/payload Network activity
sha256 a1b2c3... Payload delivery
md5 d41d8c... Payload delivery
email-src attacker@evil.com Payload delivery
filename malware.exe Payload delivery
regkey HKLM\...\Run\evil Persistence mechanism

Distribution Levels

  • 0 - Your organisation only
  • 1 - This community only
  • 2 - Connected communities
  • 3 - All communities
  • 4 - Sharing group

CLI Usage

python agent.py --input events.json --output report.json
python agent.py --input events.json --misp-url https://misp.example.com --api-key KEY

References

Scripts 1

agent.py7.8 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""MISP Threat Intelligence Sharing agent - creates events, manages attributes, searches IOCs, and validates sharing configuration via PyMISP"""

import argparse
import json
from collections import Counter
from datetime import datetime
from pathlib import Path

try:
    from pymisp import PyMISP, MISPEvent
    HAS_PYMISP = True
except ImportError:
    HAS_PYMISP = False

MISP_ATTRIBUTE_TYPES = {
    "ip-dst", "ip-src", "domain", "hostname", "url", "md5", "sha1",
    "sha256", "filename", "email-src", "email-dst", "mutex", "regkey",
    "user-agent", "vulnerability", "link", "text", "comment",
}

TLP_TAGS = {
    "white": "tlp:white",
    "green": "tlp:green",
    "amber": "tlp:amber",
    "amber+strict": "tlp:amber+strict",
    "red": "tlp:red",
}


def load_data(path):
    return json.loads(Path(path).read_text(encoding="utf-8"))


def connect_misp(url, api_key, ssl=True):
    """Initialize PyMISP connection."""
    if not HAS_PYMISP:
        return None, "pymisp not installed (pip install pymisp)"
    misp = PyMISP(url, api_key, ssl=ssl)
    return misp, "connected"


def create_event_from_data(misp, event_data):
    """Create a MISP event with attributes and tags."""
    event = MISPEvent()
    event.info = event_data.get("info", "Untitled Event")
    event.distribution = event_data.get("distribution", 1)  # 0=org, 1=community, 2=connected, 3=all
    event.threat_level_id = event_data.get("threat_level", 2)  # 1=high, 2=medium, 3=low, 4=undefined
    event.analysis = event_data.get("analysis", 0)  # 0=initial, 1=ongoing, 2=complete
    for attr in event_data.get("attributes", []):
        attr_type = attr.get("type", "text")
        value = attr.get("value", "")
        category = attr.get("category", "")
        to_ids = attr.get("to_ids", True)
        comment = attr.get("comment", "")
        if attr_type in MISP_ATTRIBUTE_TYPES and value:
            event.add_attribute(type=attr_type, value=value, category=category,
                                to_ids=to_ids, comment=comment)
    for tag_name in event_data.get("tags", []):
        event.add_tag(tag_name)
    tlp = event_data.get("tlp", "").lower()
    if tlp in TLP_TAGS:
        event.add_tag(TLP_TAGS[tlp])
    if misp:
        result = misp.add_event(event)
        return result
    return event.to_dict()


def validate_event_quality(event_data):
    """Validate event data quality for sharing readiness."""
    findings = []
    eid = event_data.get("id", event_data.get("info", "unknown"))
    if not event_data.get("info"):
        findings.append({
            "type": "missing_event_info",
            "severity": "high",
            "resource": str(eid),
            "detail": "Event lacks descriptive info/title",
        })
    attrs = event_data.get("attributes", event_data.get("Attribute", []))
    if not attrs:
        findings.append({
            "type": "no_attributes",
            "severity": "high",
            "resource": str(eid),
            "detail": "Event has no IOC attributes",
        })
    attr_types = Counter(a.get("type", "unknown") for a in attrs)
    if len(attr_types) == 1 and len(attrs) > 1:
        findings.append({
            "type": "single_attribute_type",
            "severity": "low",
            "resource": str(eid),
            "detail": f"All {len(attrs)} attributes are type '{list(attr_types.keys())[0]}' - consider enriching",
        })
    tags = event_data.get("tags", event_data.get("Tag", []))
    tag_names = [t.get("name", t) if isinstance(t, dict) else t for t in tags]
    has_tlp = any("tlp:" in t.lower() for t in tag_names)
    if not has_tlp:
        findings.append({
            "type": "missing_tlp_tag",
            "severity": "high",
            "resource": str(eid),
            "detail": "Event lacks TLP classification tag",
        })
    has_mitre = any("mitre-attack" in t.lower() or "attack-pattern" in t.lower() for t in tag_names)
    if not has_mitre and len(attrs) > 0:
        findings.append({
            "type": "missing_mitre_mapping",
            "severity": "medium",
            "resource": str(eid),
            "detail": "Event lacks MITRE ATT&CK technique mapping",
        })
    dist = event_data.get("distribution", -1)
    if dist == 3:
        findings.append({
            "type": "unrestricted_distribution",
            "severity": "medium",
            "resource": str(eid),
            "detail": "Event set to 'All communities' distribution - verify this is intentional",
        })
    for attr in attrs:
        val = attr.get("value", "")
        atype = attr.get("type", "")
        if atype in ("ip-dst", "ip-src") and val in ("127.0.0.1", "0.0.0.0", "10.0.0.1", "192.168.1.1"):
            findings.append({
                "type": "private_ip_ioc",
                "severity": "high",
                "resource": str(eid),
                "detail": f"Private/localhost IP '{val}' used as IOC - will generate false positives",
            })
        if atype in ("md5", "sha1", "sha256") and len(val) < 32:
            findings.append({
                "type": "invalid_hash_length",
                "severity": "high",
                "resource": str(eid),
                "detail": f"Hash attribute '{val}' is too short for type {atype}",
            })
    return findings


def validate_sharing_config(config):
    """Validate MISP sharing and feed configuration."""
    findings = []
    servers = config.get("sync_servers", [])
    if not servers:
        findings.append({
            "type": "no_sync_servers",
            "severity": "medium",
            "resource": "misp_config",
            "detail": "No synchronization servers configured for intelligence sharing",
        })
    for srv in servers:
        if not srv.get("pull", False) and not srv.get("push", False):
            findings.append({
                "type": "inactive_sync_server",
                "severity": "medium",
                "resource": srv.get("name", srv.get("url", "")),
                "detail": "Sync server has neither pull nor push enabled",
            })
    feeds = config.get("feeds", [])
    enabled_feeds = [f for f in feeds if f.get("enabled", False)]
    if not enabled_feeds:
        findings.append({
            "type": "no_active_feeds",
            "severity": "medium",
            "resource": "misp_config",
            "detail": "No active threat intelligence feeds configured",
        })
    return findings


def analyze(data):
    findings = []
    events = data.get("events", [data] if "info" in data or "Attribute" in data else [])
    if isinstance(data, list):
        events = data
    for evt in events:
        findings.extend(validate_event_quality(evt))
    if "sync_servers" in data or "feeds" in data:
        findings.extend(validate_sharing_config(data))
    return findings


def generate_report(input_path):
    data = load_data(input_path)
    findings = analyze(data)
    sev = Counter(f["severity"] for f in findings)
    cats = Counter(f["type"] for f in findings)
    return {
        "report": "misp_threat_intelligence_sharing",
        "generated_at": datetime.utcnow().isoformat() + "Z",
        "total_findings": len(findings),
        "severity_summary": dict(sev),
        "finding_categories": dict(cats),
        "findings": findings,
    }


def main():
    ap = argparse.ArgumentParser(description="MISP Threat Intelligence Sharing Agent")
    ap.add_argument("--input", required=True, help="Input JSON with MISP events or config")
    ap.add_argument("--output", help="Output JSON report path")
    ap.add_argument("--misp-url", help="MISP instance URL for live operations")
    ap.add_argument("--api-key", help="MISP API key")
    args = ap.parse_args()
    report = generate_report(args.input)
    out = json.dumps(report, indent=2)
    if args.output:
        Path(args.output).write_text(out, encoding="utf-8")
        print(f"Report written to {args.output}")
    else:
        print(out)


if __name__ == "__main__":
    main()
Keep exploring