threat intelligence

Analyzing Threat Intelligence Feeds

Analyzes structured and unstructured threat intelligence feeds to extract actionable indicators, adversary tactics, and campaign context. Use when ingesting commercial or open-source CTI feeds, evaluating feed quality, normalizing data into STIX 2.1 format, or enriching existing IOCs with campaign attribution. Activates for requests involving ThreatConnect, Recorded Future, Mandiant Advantage, MISP, AlienVault OTX, or automated feed aggregation pipelines.

ctiiocmispmitre-att&cknist-csfrecorded-futurestixtaxii
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

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-01

For REST API feeds (e.g., Recorded Future):

  • Query /v2/indicator/search with risk_score_min=65 to 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 → indicator object with pattern: "[ipv4-addr:value = '...']"
  • Domain → indicator with pattern: "[domain-name:value = '...']"
  • File hash → indicator with pattern: "[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.
Source materials

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 [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

Scripts 1

agent.py6.8 KB
Display-only source. This catalog never executes bundled scripts.
#!/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()
Keep exploring