Threat intelligence only compounds when it is structured and connected. A pile of PDFs about an actor is worth far less than a STIX bundle where indicators, techniques, and campaigns are linked and queryable across reports.
Tier: Advanced · Workflow area: Incident response, DFIR & CTI
This playbook uses AI to extract IOCs and TTPs into STIX and to synthesize an actor profile across multiple reports — with strict grounding, because a fabricated relationship between two campaigns can misdirect an entire threat model.
Skip AI when the source already ships a STIX bundle — ingest and verify theirs rather than re-extracting.
Extract to a STIX-shaped schema with per-field citations. Use structured output so the result is machine-ingestible and every object is traceable to its source.
import anthropic
from pydantic import BaseModel
class Indicator(BaseModel):
type: str # ipv4-addr | domain-name | file:hashes.'SHA-256' | ...
value: str
source_sentence: str # verbatim quote proving provenance
class Technique(BaseModel):
attack_id: str # e.g. T1566.001
behavior: str
source_sentence: str
confidence: str # high | low
class Extraction(BaseModel):
indicators: list[Indicator]
techniques: list[Technique]
unresolved: list[str] # claims that could not be grounded
client = anthropic.Anthropic()
response = client.messages.parse(
model="claude-opus-4-8",
max_tokens=4000,
thinking={"type": "adaptive"},
messages=[{"role": "user", "content": f"Extract IOCs and ATT&CK "
f"techniques from this report. Quote the source sentence for "
f"each. Do not add anything not in the text.\n\n{report}"}],
output_format=Extraction,
)
data = response.parsed_output
Expected output: typed indicators and techniques, each carrying the verbatim sentence that justifies it, plus an unresolved list of claims the model could not ground.
Verify every IOC verbatim against the report. String-match each value and its source_sentence in the original text. A single transposed character in a hash or domain poisons the bundle and any detection built from it.
Verify technique IDs against ATT&CK. Confirm each attack_id exists and its definition matches the quoted behavior. Invented or misattributed IDs are common; the matrix is the arbiter.
Synthesize the actor profile across reports — grounded. Feed several verified extractions and ask for a linked profile.
From these VERIFIED extractions (multiple reports on one actor), synthesize
an actor profile: known aliases, targeted sectors, characteristic TTPs,
infrastructure patterns. For every assertion, cite which report(s) support
it. Where reports CONFLICT, show the conflict rather than resolving it.
Do not assert a link between two campaigns unless a report states it.
Mark inferred connections as INFERRED, not established.
Build the STIX bundle and relationships from verified objects only. Only IOCs and techniques that passed verification become STIX objects; only report-stated links become relationship objects. Load into the TIP.
Attribution and campaign-linking are where CTI does the most damage when wrong. Never let the model assert that two campaigns are the same actor unless a source says so — a fabricated link reshapes threat models and response priorities across the org.
INFERRED or asserted link between campaigns. If the model claims two reports describe one actor but neither report says so, drop the relationship — coincidental TTP overlap is not attribution.CTI frequently carries TLP restrictions and may reference a client's own incident, so extraction stays inside the boundary. Run both the extraction and synthesis on a local Ollama model or the client's approved endpoint, and honor TLP markings strictly — TLP:RED reports never touch a shared or third-party tool. Public IOCs and ATT&CK IDs can be verified against public references, but keep any client-specific context (which of their assets a report touches) inside their environment. Load the finished STIX bundle into the client's own MISP/OpenCTI instance, and mark inferred links clearly so their analysts decide what attribution to trust.