Sunday, March 8, 2026

Cognitive CTI - Building a Scalable, Self-Hosted Threat Intelligence Pipeline with AI

Introduction

Threat Intelligence is a fairly superfluous component to security for most individuals or organisations that are growing the security function. This is typically due to cost, scaling it into wider systems, or seeing the true value of running their own solution, when it is baked into anti-virus or provided by a Managed Detection and Response (MDR) provider.

This post outlines a modern solution to creating a scalable threat intelligence pipeline that does not compromise on functionality that organisations are implementing in this day and age. Whether you are looking to conduct solo research, proof-of-concept, or a larger team that has matured to the point of requiring a threat intelligence pillar to complement the security program, everyone should have access to threat intelligence at all stages of maturity.

The blog post is split into five sections:
  1. System Requirements - The section highlights the main system requirements of the implementation
  2. Architecture - The section focuses on the architectural view of the implementation, limitations, and potential adaptations that can fit into a wider ecosystem that has access to dedicated finances.
  3. Implementation - The section focuses on the implementation and the ideas behind each section in more detail
  4. Technical Challenges - The section discusses on the technical constraints and workarounds that were encountered during development
  5. Bibliography - This section provides hyperlinks to the resources used that aided in the development of this pipeline
The split between the architectural layout and the implementation, is to entice all readers from leadership and design audiences through the architecture overview, as well as, the engineers and researchers who will are looking to use this as a foundation for their own implementation.

All code and system files for the cognitive CTI project can be found at github.com/jwhitt3r/cognitiveCTI.

System Requirements

The motivation for this project sat with the idea of, could a self-hosted pipeline leveraging automation and artificial intelligence relate to systems built and maintained by an MDR (hey, they're the ones that actively enable this function at scale and sell it, what better role model). A number of constraints were placed on it, this is to ensure that it would enable any individual or teams to implement and adapt to meet the needs they may have. The following constraints were in place:
  • Self-hosted
  • Containers
  • Codified
  • Free (ignore electricity)
  • Modular
The minimum resources used for this project, are:
  • 32GB RAM
  • GPU (for faster model processing)
  • Docker
The system used in this project was:
  • Windows 11 Pro (For Hyper-V)
  • 128GB RAM
  • AMD Ryzen 7950X
  • Nvidia RTX 4070 Ti
The GPU was only used to improve processing, extensive testing occurred using the CPU for its AI processing. However, it is encouraged that the GPU be used for actual more timely intel pipeline.

Architecture

The following section provides a high-level architectural overview of the pipeline, its components, and how they interact with one another. The diagram is intentionally abstracted away from the implementation detail as to provide an accessible illustration of the system as to allow wider audiences with enough context to understand the system, its purpose, and where it can be adapted to fit into a wider ecosystem. The implementation sections that follow provide the detail for engineers and researchers looking to build on this foundation.

Figure 1 illustrates the end-to-end flow from threat data collection through to output delivery of correlated and summarised threat intelligence.

Figure 1: High-level Architectural Diagram

Intelligence Layer

As with all intelligence sources, some are more valuable than others. We organise our sources into five layers, each serving a distinct purpose in building a comprehensive threat picture. The layering ensures that the pipeline is not over-reliant on any single source type and that intelligence is collected at every level of the threat landscape as to provide a complete picture during analysis.

  • Layer 1 - Vendor: Government advisories and vendor security bulletins. These are your CISA, NCSC, CERT-EU, and Microsoft MSRC publications. The highest trust, most actionable intelligence available, typically carrying patch priorities and active exploitation warnings
  • Layer 2 - Independent Research: Security researchers and independent analysts who publish deep technical investigations. Krebs on Security, Bleeping Computer, SentinelOne Labs, and Unit 42 fall into this category. These are the sources breaking new campaigns and tracking threat actor evolution
  • Layer 3 - Sector Specific: Feeds that are relevant to the organisation's industry. This layer is intentionally left flexible, what sits here depends entirely on the sector you are operating in and the threat landscape that is relevant to your environment
  • Layer 4 - Indicators of Compromise: Machine-readable threat feeds providing structured indicators, IPs, domains, file hashes, malware samples, and vulnerability data. AlienVault OTX, Abuse.ch (MalwareBazaar, ThreatFox, URLhaus), and the NVD CVE database are the primary sources here. These are not articles to read, they are data points to correlate against
  • Layer 5 - Threat Actor: Direct collection from threat actor communication channels. Public Telegram channels, dark web monitoring, and community-sourced intelligence lists provide early warning on ransomware victim announcements, data leak postings, and operational chatter. This is the lowest trust layer, but often the earliest signal that something is happening
The value of this layered approach is that it mirrors how threat intelligence is consumed in practice. Leadership cares about Layer 1 advisories and Layer 3 sector relevance. Analysts and researchers work across Layers 2 through 5. The pipeline treats them all as input and lets the downstream processing determine what is actionable.

TI Platform

All intelligence flows into OpenCTI, an open-source threat intelligence platform that acts as the central aggregation point. OpenCTI normalises incoming data into the STIX standard regardless of its original format, whether that is an RSS article, a structured IOC feed, or a Telegram post scraped via RSS Bridge. The platform maintains a knowledge graph of relationships between threat actors, malware families, attack techniques, vulnerabilities, and campaigns.

RSS feeds and dedicated connectors handle the ingestion from each of the intelligence layers, running continuously to ensure the platform reflects the current threat landscape. OpenCTI's role in this architecture is aggregation and normalisation, it is not performing the analysis. It provides the structured foundation that the downstream pipeline queries against.

Tiered Routing

This is what makes the pipeline work. Without it, we would be processing nearly 5,000 items per 12-hour cycle, something that exacerbated hallucinations in Small Language Models (SLM) and caused significant pipeline latency during testing. The vast majority of those are individual CVE entries from automated feeds, notably, Microsoft CVE listings, AlienVault for limited value pulses, or low-value noise from Telegram channels, processing all of them would take hours and produce meaningless output.

Tiered routing classifies each report by its source and content before it reaches the processing pipeline. High-value intelligence articles (vendor advisories, research blogs, IOC-containing Telegram posts) are directed into full AI analysis. Structured IOC feeds from platforms like AlienVault OTX are routed into a lighter metadata extraction path that leverages their existing entity tagging. Bulk CVE data and low-value Telegram chatter are dropped from the processing pipeline entirely.

The result is a reduction from thousands of reports down to approximately 50 to 80 items that warrant cognitive processing, with limited loss of intelligence value. Reports that are filtered out remain available in OpenCTI for manual investigation and graph analysis. Tiered routing controls what receives automated enrichment, not what is retained.

Extract, Load, Transform Pipeline

The processing pipeline follows an Extract, Load, Transform pattern, where the transformation stage is augmented by AI.

Extract pulls report metadata, associated STIX entities, and full article content from the TI platform and source websites. For reports with an external reference URL, the pipeline fetches the full article HTML and strips it to clean plaintext suitable for analysis. Reports without a fetchable URL fall back to the description provided by OpenCTI.

Transform (AI) is where the cognitive layer comes in. Each qualifying report is analysed by a language model that produces a structured intelligence assessment, covering severity classification, executive summary, identified threat actors and malware families, mapped MITRE ATT&CK techniques, targeted sectors and regions, and extracted indicators of compromise. A second, more capable model then performs cross-report correlation across the entire batch, identifying shared threat patterns, emerging trends, and campaign-level connections that no individual report reveals on its own.

Load stores the enriched, structured intelligence into a relational database with full entity relationships, enabling historical querying, trend analysis, and integration with downstream systems.

Cognitive Layer

The cognitive layer is where the AI processing sits. It is designed to be swappable, local open-source models for environments where data cannot leave the network, or enterprise cloud models where analytical quality is the priority.

The layer supports multiple model roles. Smaller, faster models handle the high-volume per-report extraction tasks, while larger reasoning-focused models handle the lower-volume but higher-complexity correlation and trend analysis. This keeps the pipeline fast where it needs to be fast and smart where it needs to be smart, you are not burning your most capable model on tasks that a smaller one handles adequately.

Looking ahead, this layer can accommodate additional capabilities as they mature. Model Context Protocol integration for dynamic data retrieval during analysis, persistent memory stores for cross-session learning, and connections to broader data lakes for enrichment. These are on the roadmap rather than built today, but the architecture does not need to change to support them, but the architecture does not need to change to support them.

Output

The pipeline outputs to two channels. Instant messaging delivers individual report cards and cross-report correlation briefings to analyst and leadership communication channels in real-time. Database storage provides the persistence layer for historical analysis, reporting, and integration with security tooling such as SIEM platforms.

The output layer is decoupled from the processing pipeline, additional delivery channels such as email digests, PDF reports, API endpoints, or SIEM integrations can be added without modifying the core analytical workflow. The same structured data that powers the Discord summaries can be reformatted for any downstream consumer.

Implementation

The following section focuses on the implementation detail of each component in the pipeline. This is targeted at engineers and researchers looking to understand the technical decisions, constraints, and workarounds that shaped the final implementation. Where the architecture section provides the conceptual overview, this section provides the specifics required to replicate, adapt, or extend the pipeline.

The diagram below illustrates the full implementation pipeline as deployed in N8N. Each section that follows corresponds to a colour-coded segment of the diagram, walking through the nodes, their purpose, and the reasoning behind each design decision.

Figure 2: Low-level Architectural Diagram

The pipeline is broken down into the following sections:

  • Ingestion: Scheduled trigger and OpenCTI GraphQL extraction
  • Tiered Routing: Source classification and volume reduction
  • Tier 2 - AlienVault Light Path: Metadata extraction without AI processing
  • Tier 1 - Article Enrichment: Full article fetching and HTML stripping
  • Tier 1 - AI Analysis: Per-report structured intelligence extraction via Ollama
  • Tier 1 - Correlation: Cross-report pattern identification and trend analysis
  • Database Storage: PostgreSQL persistence with entity and tag relationships
  • Discord Output: Formatted intelligence delivery to communication channels
  • Technical Challenges: N8N constraints, Large Language Model (LLM) reliability, and workarounds

Ingestion

Figure 3: TI Platform Ingestion

The pipeline is triggered twice daily at 07:00 UTC and 19:00 UTC via a Schedule Trigger node, creating 12-hour batch windows. Each trigger initiates an HTTP POST to the OpenCTI GraphQL API requesting the last 12 hours of reports.

The GraphQL query requests 5,000 reports ordered by creation date. This number is deliberate. Initial testing with first: 500 only captured approximately 10% of the available reports, burying the RSS feed articles that the pipeline was designed to process underneath the volume of automated CVE. The query pulls the full report metadata including STIX entity relationships (AttackPattern, Malware, ThreatActor, IntrusionSet, Vulnerability, Campaign, Tool, Indicator, StixCyberObservable), external references, TLP markings, labels, and the createdBy identity that the tiered routing relies on.

The Parse & Flatten node transforms the nested GraphQL response into a flat structure. Each report is extracted with its entities, labels, TLP marking, external reference URLs, and author identity. This is where the dual-pattern parsing for objectLabel and objectMarking is handled, OpenCTI returns these in inconsistent formats depending on how the data was ingested, so the parsing logic accounts for both array-of-objects and array-of-strings patterns.

Tiered Routing

Figure 4: Pipeline Tiered Routing

The Route by Source Tier node is a Code node that classifies each report into one of four processing tiers based on two attributes: the createdBy identity ID and regex pattern matching on the report content.

The classification logic is as follows:

  • Tier 1 (Full AI): Any report with a named createdBy identity that is not Microsoft or AlienVault. This captures all RSS feed articles from CISA, Krebs on Security, Bleeping Computer, Schneier, and the other Layer 1-3 sources. Additionally, Telegram posts (which arrive with a null createdBy) are promoted to Tier 1 if they contain IOC patterns: IP addresses, file hashes (32-64 character hex strings), CVE identifiers, or threat-relevant keywords (ransomware, APT, malware, exploit, zero-day, backdoor, trojan, botnet, C2, stealer, loader, RAT, rootkit, among others)
  • Tier 2 (AlienVault Light): Reports where createdBy matches the AlienVault identity ID (c998e145-ba64-47e8-8262-3333dee66dbb). These are routed to a lighter processing path
  • Tier 3 (Filter - Microsoft): Reports where createdBy matches the Microsoft Defender TI identity ID (67209902-ed0f-4679-85f9-768d05a639b7). These are individual CVE entries that constitute approximately 69% of the total volume (~3,300 per cycle). Running AI analysis on "CVE-2026-27969 affects Vitess versions X.Y.Z" adds zero intelligence value
  • Tier 4 (Filter- Telegram noise): Telegram posts without IOC patterns or threat keywords. Defacement mirrors, bragging posts, and general chatter

The IF Not Skipped node drops Tier 3 and Tier 4, and the IF AlienVault node splits the remaining reports into the two processing paths. The result is a reduction from ~4,900 reports to approximately 50-80 items across both paths.

It is worth noting that filtering is performed at the Code node level rather than in the GraphQL query. OpenCTI's GraphQL filters do not support the complex exclusion logic required (author ID matching combined with regex content scanning), so the full 5,000 reports are fetched and classified in N8N where the logic can be expressed freely.

Tier 2 - AlienVault Light Path

Figure 5: Alienvault Insertion to PostgreSQL

AlienVault OTX pulses arrive with structured entity tagging already applied by the OTX platform. Running these through an LLM for summarisation adds minimal value, the structured data is already there. However, the metadata provides valuable context from these pulses is valuable context for the correlation stage.

The AlienVault Light Process node sorts pulses by entity count (descending) and caps the output at 20. This prioritises the most IOC-rich pulses, the ones most likely to overlap with Tier 1 reports during correlation. Without the cap, 940+ AlienVault pulses would overwhelm the correlation prompt.

For each pulse, the node extracts threat actors, malware families, attack techniques, and vulnerabilities directly from the OpenCTI entity graph, no LLM required. A severity assessment is applied heuristically: pulses with named threat actors or malware families are marked as high, the remainder as medium.

The AV Prepare for Postgres node formats the extracted metadata into the same schema used by the Tier 1 path, allowing both tiers to be stored in the same threat_reports table. The upsert operation uses opencti_id as the conflict key to prevent duplicates across pipeline runs.

Tier 1 - Article Enrichment

Figure 6: Article Fetch and Extraction

The Prepare Article URL node extracts the first external reference URL from each report. These URLs point to the original source article, whether that is a CISA advisory, a Krebs on Security blog post, or a Telegram channel message.

The Fetch Full Article node performs an HTTP GET with a realistic browser User-Agent header. The User-Agent is necessary because several sources (notably Cloudflare-protected sites) return 403 responses to requests that lack a recognisable browser signature. A 10-second timeout is applied, and the node is configured to continue on error, reports that fail to fetch are not dropped from the pipeline.

The Extract Text & Merge node strips the fetched HTML to plaintext. The stripping is aggressive: <script>, <style>, <noscript>, <nav>, <header>, <footer>, and <aside> elements are removed entirely, then all remaining HTML tags are stripped. HTML entities (&amp;, &lt;, &gt;, &quot;, &nbsp;) are decoded, and whitespace is normalised. The resulting text is truncated to 4,000 characters to fit within the context window of the analysis model.

If the fetch fails or the extracted text is shorter than 100 characters, the node falls back to the OpenCTI description. This ensures that every Tier 1 report has some content for the AI to work with, even if the source article was blocked.

Figure 7: Cognitive Layer for AI Usage

Tier 1 - AI Analysis

This is the first SLM touchpoint in the pipeline. The AI Agent - Analyse Report node sends each report to Ollama running llama3.2 (3B parameters) with JSON format enforced.

Per-Report Prompt

The prompt provides the model with four inputs: the report title, the enriched description (full article text or OpenCTI fallback), the STIX entities already associated with the report in OpenCTI, and the source labels. The model is instructed to return a structured JSON object containing:

  • report_type - one of seven categories (threat-report, malware-analysis, vulnerability-alert, security-news, law-enforcement, best-practice, vendor-advisory)
  • executive_summary - 2-3 sentence CISO-readable summary
  • technical_summary - TTP detail or N/F
  • threat_actors, malware_families, attack_techniques - extracted or inferred from content
  • targeted_sectors, targeted_regions - from content or context
  • iocs_mentioned, cves_mentioned - specific indicators
  • severity_assessment - critical, high, medium, low, or info with defined criteria
  • tags - minimum three keywords for categorisation
  • kill_chain_phases - mapped phases or N/F

System Prompt Guardrails

SLMs require explicit constraints that larger models handle implicitly. The system prompt includes the following guardrails, all of which were added iteratively based on observed failure modes during testing:

  • No empty arrays- the model must use ["N/F"] for absent fields rather than [], this prevents downstream null handling issues in the entity extraction pipeline
  • No MITRE ATT&CK on geopolitical content- without this instruction, the model assigns T1566 (Phishing) and T1071 (Application Layer Protocol) to virtually every report, including law enforcement actions and general security news. This was one of the more frustrating behaviours to pin down
  • Nation states are not threat actors- the model was classifying countries involved in military conflicts as cyber threat actors, this rule restricts the threat_actors field to named cyber groups (APT37, Lazarus, FIN7, etc.)
  • Severity criteria- critical requires an active zero-day or major breach in progress, high requires a named APT campaign or new malware family, medium covers law enforcement actions and disclosed breaches, low applies to best practices and patch advisories, info covers opinion pieces and general news
Researchers Note: These guardrails are specific to llama3.2 at 3B parameters. Larger models (8B+) or cloud models may not require the same level of explicit instruction, though testing against your chosen model is recommended before removing any of them.

Merge AI Summary

The Merge AI Summary node pairs the AI output with the original report metadata using index tracking. The AI response is parsed with a JSON extraction that accounts for common LLM output failures: markdown code fences around the JSON, preamble text before the opening brace, and invalid control characters. If parsing fails entirely, a fallback object is constructed with the raw AI output as the executive summary and N/F values for all structured fields.

Tier 1 - Correlation

Figure 8: Cognitive Layer for AI Usage

This is the second LLM touchpoint and the most analytically demanding step in the pipeline. The Build Correlation Prompt node assembles all Tier 1 AI summaries and Tier 2 AlienVault metadata into a single prompt, then sends it to phi4 (14B parameters) for cross-report analysis.

Why a Separate Model

llama3.2 at 3B parameters is sufficient for single-document extraction but falls over with multi-document reasoning. Initial testing with llama3.2 for correlation produced two consistent failure modes: the model fixated on the first or most distinctive report in the batch and ignored the remaining 30+, and it copied template options literally (returning "correlation_type": "TTP|ACTOR|MALWARE|SECTOR|CAMPAIGN|INFRASTRUCTURE" instead of selecting one value). phi4 at 14B parameters with a lower temperature (0.3) and an 8,192-token context window resolved both issues.

Pre-Computed Overlap Hints

Rather than relying entirely on the model to discover relationships across 37+ reports, the Build Correlation Prompt node pre-scans all reports and identifies shared elements before the LLM sees the data:

DETECTED OVERLAPS:

  • Actor "Lazarus" appears in: Report Title A | Report Title B
  • Malware "Cobalt Strike" appears in: Report Title C | Report Title D
  • CVE "CVE-2026-1234" appears in: Report Title E | Report Title F

This is computed by building frequency maps across threat actors, malware families, ATT&CK technique IDs, CVE identifiers, and targeted sectors. Any element appearing in two or more reports is flagged. This gives the model a cheat sheet rather than asking it to find needles in a haystack. It still needs to assess whether the overlaps are meaningful and identify thematic connections that the exact-match scan cannot detect, but it is not starting from scratch.

Researchers Note: The pre-computed hints were added after observing that phi4 would identify only 2 correlations from a batch of 37 reports without them. With the hints, the same batch produced 6-8 correlations with significantly better quality. The model uses them as starting points and then builds on top of them with its own analysis.

Database Storage

Figure 9: Database Storage for PostgreSQL
The storage pipeline handles three tables: threat_reports, entities, and report_tags, with a linking table report_entities for the many-to-many relationship between reports and entities.

Report Insertion

The Prepare for Postgres node flattens the AI-enriched data into the threat_reports table schema. The Postgres - Insert Report node performs an upsert keyed on opencti_id, meaning re-runs of the pipeline update existing reports rather than creating duplicates. This matters for the twice-daily schedule, reports that appeared in the 07:00 cycle and are still within the 12-hour window at 19:00 get updated rather than duplicated.

Entity Extraction and Linking

The Prepare Entities & Tags node extracts all entities from both the OpenCTI graph (STIX entities already associated with the report) and the AI output (threat actors, malware families, attack techniques identified by the model). These are deduplicated using a type::name composite key.

The entity insertion uses a CTE (Common Table Expression) that inserts or updates the entity in the entities table and then creates the link in report_entities in a single query:

WITH ent AS (

INSERT INTO entities (entity_type, name, stix_id, last_seen)

VALUES ('threat-actor', 'Lazarus Group', 'intrusion-set--...', NOW())

ON CONFLICT (entity_type, name) DO UPDATE SET last_seen = NOW()

RETURNING id

)

INSERT INTO report_entities (report_id, entity_id, relationship)

SELECT '123', ent.id, 'mentions' FROM ent

ON CONFLICT DO NOTHING;

Tags follow the same pattern but with a simpler insert into report_tags.

SQL Injection Handling

N8N's Postgres node does not support parameterised queries in executeQuery mode. All user-data fields use .replaceAll("'", "''") escaping in N8N template expressions. This is necessary because STIX patterns frequently contain single quotes (e.g., [file:hashes.'SHA-256' = '...']) that would break unescaped queries.

Researchers Note: This is not ideal from a security perspective. Parameterised queries would be preferred, but N8N does not support them in raw query mode at the time of writing. The escaping approach handles the known edge cases from STIX patterns, but if you are adapting this pipeline for a production environment with untrusted input, consider using N8N's built-in upsert operations where possible instead of raw queries.

Discord Output

Figure 10: Output Locations

The pipeline delivers intelligence to two Discord channels via the Discord Bot API.

Threat Intelligence Digest (#security-updates)

Figure 11: Summary Reports Example in Discord

The Format Summaries for Discord node generates a per-report card for each Tier 1 report. Each card contains:
  • A colour-coded severity icon (red square for critical, orange for high, yellow for medium, green for low, blue for info)
  • The report title as a masked hyperlink to the source article with embed suppression
  • An executive summary in a blockquote
  • Metadata line with threat actors, malware, TTPs, CVEs, report type, and source, separated by pipe characters

Threat Correlation Report (#correlation-reports)

Figure 12: Correlation Reports Example in Discord

The Format Correlation for Discord node generates the correlation output in three sections:
  • Threat Landscape - the model's multi-paragraph assessment of the batch
  • Emerging Trends - each trend with a risk-level icon, description, and evidence (linked report titles)
  • Cross-Report Correlations - each correlation with a confidence percentage, correlation type, the linked reports, and the shared elements in a code block

Both formatters implement Discord's 2,000-character message limit by splitting on section boundaries. A Wait node with a 1.5-second delay is placed before each Discord send to avoid rate limiting.

Link Formatting

Discord masked links use the syntax [title](<url>) where the angle brackets suppress the URL embed preview. The linkTitle function in the correlation formatter implements fuzzy matching against the URL map, normalising both the report title and map keys by stripping non-alphanumeric characters before comparison. This accounts for the model occasionally modifying titles through minor punctuation changes or truncation.

Technical Challenges

The following section covers the technical constraints and workarounds that were encountered during development. These are not edge cases, they are recurring issues that any implementation using N8N, Ollama, and local LLMs will hit.

N8N JavaScript VM Constraints

N8N's sandboxed JavaScript Code node runs an older V8 environment with several limitations that affect how code is written:

  • No const or let - all variables must use var
  • No arrow functions - all functions must use function() syntax
  • Emoji handling - String.fromCharCode() with surrogate pairs does not reliably produce emoji in the N8N runtime. Unicode escape sequences (\ud83d\udfe5) within string literals are required
  • No fetch API - HTTP requests must be handled via dedicated HTTP Request nodes, not within Code nodes

These constraints mean that all Code nodes in the pipeline are written in ES5-compatible JavaScript. This is not a stylistic choice, it is a reliability requirement. Newer syntax will work in some N8N versions and silently break in others.

LLM JSON Output Reliability

Local Language Models do not reliably produce valid JSON, even with format: "json" enforced in the Ollama API call. The following failure modes were observed and handled:

  • Markdown code fences - the model wraps its JSON in ```json ``` blocks. The parse node strips these before parsing
  • Preamble text - the model outputs "Here is the analysis:" before the JSON. The parse node finds the first { and last } and parses the substring
  • Trailing commas - [item1, item2,] is common. The parse node removes trailing commas before closing brackets and braces
  • Control characters - newlines and tabs inside JSON string values break parsing. The parse node strips all control characters (0x00-0x1F) before parsing
  • Pipe-separated enum values - the model lists all options instead of selecting one. The parse node splits on | and takes the first valid value

The Parse Correlation Results node implements all of these as sequential cleanup steps before attempting JSON.parse(). If parsing still fails, a regex-based fallback extracts the threat_landscape_summary field directly from the raw text.

Researchers Note: These are not occasional glitches. With llama3.2 at 3B parameters, roughly 1 in 10 responses requires at least one of these cleanup steps. The pipeline needs to handle them as a matter of course, not as exception handling.

Model Context and Memory

Ollama's phi4 at 14B parameters requires approximately 9-10GB of RAM for model weights, with additional memory for the KV cache that scales with the context window size. The initial configuration used numCtx: 16384 (16K tokens), which caused the Linux OOM killer to terminate the Ollama process when running alongside the full OpenCTI stack.

The resolution was to reduce numCtx to 8192, which halved the KV cache memory footprint. The correlation prompt for 37 reports fits within 8K tokens with the compressed report format. For implementations with more available memory or GPU offloading, numCtx can be increased to accommodate larger batches.

Running both llama3.2 and phi4 simultaneously doubles the memory requirement. Setting OLLAMA_MAX_LOADED_MODELS=1 in the Ollama environment ensures one model is unloaded before the other is loaded, at the cost of a ~10 second model swap delay between the analysis and correlation stages.

Article Fetch Reliability

Not all source articles are successfully fetched. Cloudflare-protected sites return 403 responses even with a browser User-Agent, paywalled content returns truncated HTML, and some RSS feed URLs point to landing pages rather than the article itself. The pipeline handles this through graceful fallback, if the fetch fails or produces insufficient text, the OpenCTI description is used instead. This means the AI analysis for those reports is working with less context, but the report is not lost.

Title Hallucination in Correlation

This was the most persistent quality issue encountered through the build. When presented with multiple reports, both llama3.2 and phi4 default to creating shorthand references ("Report 1", "the first report", "the Adobe vulnerability report") rather than using the exact title provided. This breaks the Discord formatter's URL linking, which relies on exact title matching against the URL map.

The mitigation is multi-layered: the prompt format uses --- REPORT: Exact Title ---headers, the system prompt explicitly forbids numbered references with wrong and correct examples, and the prompt includes an actual title from the current batch. The parse node additionally checks for numbered references in the output and can flag them. With phi4 at temperature 0.3, this combination produces correct titles in the majority of cases, though occasional mismatches still occur and are handled by the fuzzy title matching in the Discord formatter.

Researchers Note: Title hallucination is a well-documented behaviour in local language models when processing multiple documents. If you are implementing this with a cloud model (Claude, GPT-4o), you may find that a single instruction is sufficient. With local models at the 3-14B parameter range, the repeated reinforcement across system prompt, user prompt, and examples is necessary. However, there maybe further tweaks to the architecture required to scale to meet the enterprise demand.

Bibliography

[6] - OpenCTI






Monday, February 16, 2026

WannaCry - Campaign Intelligence, Reverse Engineering, and Detection

During 2017, WannaCry became a national headline for the United Kingdom and many other nations targeting companies, such as FedEx, Honda, Nissan, and the National Health Service (NHS) in the United Kingdom [1]. A Ransomware and Cryptoworm, that utilized the ETERNABLUE exploit and the DOUBLEPULSAR backdoor to propagate across unpatched Microsoft Windows systems is the centre of this article. 

The post is broken down into the following areas:
  • Threat Intelligence - A Diamond Model mapping, a campaign timeline, and, impact assessment of WannaCry
  • Basic Static Analysis - Conducting fingerprinting of the application and the subsequent mapping of the binary to MITRE ATT&CK and the Malware Behaviour Catalogue
  • Basic Dynamic Analysis - Initial detonation with and without networking
  • Advanced Static Analysis - A reverse engineering of the binary
  • Advanced Dynamic Analysis - A further dynamic test reviewing new findings based on the work of the Advanced Static Analysis section
  • Indicators of Compromise - A comprehensive list of Indicators-of-Compromise
  • Malware Behaviour Catalogue - Categorising the WannaCry malware against the MBC catalogue
  • Classification - The classification of the malware based on the Computer Antivirus Research Organization (CARO) malware naming scheme
  • Defender Recommendations - Recommendations for a proactive and reactive approach to WannaCry, with example YARA rules
  • Conclusion - A wrap up of the findings
  • References - A collection of URL references and their usage in the work

Threat Intelligence

The following breaks down the threat profile of WannaCry, firstly, providing a Diamond Model mapping, a campaign timeline that looks at the pre-attack, attack, aftermath, financial, and attribution/legal components of the campaign, and, closing with an impact assessment.

Diamond Model

The table below provides a breakdown of each core components of WannaCry.

Category Data Point(s)
Adversary  The United States of America and the United Kingdom attribute these attacks to North Korea and the Lazarus Group [2] [3]
Infrastructure  Killswitch Domain: iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea
Tor C2 Domains: 
gx7ekbenv2riucmf[.]onion
57g7spgrzlojinas[.]onion
xxlvbrloxvriy2c5[.]onion
76jdd2ir2embyv47[.]onion
cwwnhwhlz52maqm7[.]onion
Victim  The victim list is broad, however, for the United Kingdom, Manufacturing, Government, Healthcare, and Logistics were most prevalent
Capability The ETERNALBLUE [4] and DOUBLEPULSAR [5], along with the Ransomware were the core capabilities in the attack

This relationships of data can be illustrated within the Diamond Model as follows:

Diamond Model
Figure 1: Diamond Model

Campaign Timeline

The following timeline provides a high-level view of the WannaCry campaign from the initial vulnerability disclosure through to the withdrawal of cryptocurrency from the three identified Bitcoin wallets. All times are presented in UTC where available.

Researchers Note: There is a discrepancy across sources regarding the exact time of the first infection on 12 May 2017. Some sources cite 07:24 UTC (converted from 3:24 AM EDT), whilst others reference 07:44 UTC based on Asian telemetry. Both are included below.


Pre-Attack


Date Event
2016-08-13 The Shadow Brokers emerge publicly, announcing stolen NSA Equation Group tooling via Twitter and GitHub [6].
2017-02 (approx.) An early variant of WannaCry (version 1.0) appears in a limited number of targeted attacks. This version does not use ETERNALBLUE, instead relying on compromised credentials to propagate through networks [7].
2017-03-14 Microsoft releases security bulletin MS17-010, patching the SMBv1 vulnerability across all supported Windows versions from Vista through Server 2016 [8].
2017-04-14 The Shadow Brokers release the Lost in Translation dump, containing ETERNALBLUE, DOUBLEPULSAR, and several other NSA exploits [9].
2017-04-21 to 2017-04-25 Thousands of systems already compromised with DOUBLEPULSAR, growing to hundreds of thousands by 25 April [10] [11].
2017-05-05 The Spanish CERTSI (Computer Emergency Response Team for Security and Industry) publishes an early warning bulletin citing WannaCry ransomware attacks [12].
2017-05-05 Reports of the attack began circulating on Twitter, specifically, Spanish companies [12].


Attack Day - 12 May 2017


Time (UTC) Event
~07:24 First WannaCry 2.0 infections recorded in Europe (reported as 3:24 AM EDT by multiple sources). [10]
~07:44 Evidence of initial infection in Asia. The worm begins propagating via ETERNALBLUE over SMBv1 port 445, scanning both local network ranges and random public IP addresses [10].
~11:00 Reports begin circulating on Twitter. The first named organisations are Spanish companies, including Telefónica, Vodafone, and Banco Bilbao Vizcaya Argentaria [12].
15:03 Marcus Hutchins (MalwareTech) registers the killswitch domain iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea[.]com, halting propagation for samples that successfully resolve the domain before executing [13] [14].
16:00 NHS England declares cyber attack a major incident [15]


Immediate Aftermath - 13 to 22 May 2017


Date Event
2017-05-13 Microsoft releases emergency out-of-band patches for unsupported operating systems, including Windows XP, Windows 8, and Server 2003 [10]. The three hardcoded Bitcoin wallet addresses are publicly identified:
  • 13AM4VW2dhxYgXeQepoHkHSQuy6NgaEb94
  • 12t9YDPgwueZ9NyMgw519p7AA8isjr6SMw
  • 115p7UMMngoj1pMvkpHijcRdfJNXj6LrLn
2017-05-14 WannaCry variants without the killswitch begin appearing. US-CERT publishes Alert TA17-132A with indicators and YARA rules [16].
2017-05-15 The attack count exceeds 200,000 systems across 150+ countries. The three wallets have received approximately 151 transactions totalling ~24.76 BTC (~$42,641 USD) [12].
2017-05-18 Payments slow significantly. 271 transactions totalling ~44.16 BTC (~$79,711 USD) across the three wallets, with fewer than 10 payments over $200 in 24 hours [17].
2017-05-19 Hackers try to DDoS the killswitch domain using a Mirai botnet variant and begin to work on a new version of WannaCry [8].
2017-05-22 Hutchins improves the DDoS resilience of the killswitch domain. Independently, researchers from University College London and Boston University publish a method to recover WannaCry encryption keys, later automated as the WannaKey tool [8].


Financial - Wallet Activity & Withdrawal

By 14 June 2017, a total of 327 payments had accumulated across the three wallets, totalling approximately 51.63 BTC (~$130,635 USD at the time). The wallets then sat dormant for roughly 10 weeks.

Date / Time (UTC) Event
2017-08-03, ~03:06 The first outgoing transaction is recorded on the blockchain, moving 8.73 BTC from the WannaCry wallets. Between 03:00 and 03:30 UTC, all three wallets are emptied through six transactions completed within approximately 15 minutes. [18]
2017-08-03, 07:08 The first transaction hits Changelly, a cryptocurrency exchange with no email registration requirement. This is a 0.1 BTC transaction, likely a test before the bulk conversions. [18]
2017-08-03, 07:44 to 09:53 Eight transactions move approximately 13.53 BTC through ShapeShift, converting BTC to Monero (XMR). Separately, approximately 38.33 BTC are routed through Changelly. The total conversion yields approximately 820.80 XMR via ShapeShift, plus an unknown quantity via Changelly. The use of Monero is significant as it is a privacy-focused cryptocurrency that hides both ends of a transaction and the amount transferred. [18] [19]
2017-08-03 (afternoon) ShapeShift confirms the WannaCry attackers used their service in breach of their terms, blacklists the associated addresses, and begins cooperating with law enforcement. Changelly confirms approximately 5.2 BTC moved through their exchange and begins cooperating with Europol. [20]
2017-08-17 The Monero holdings are consolidated in three transactions on ShapeShift.[19]
2017-11-02 Approximately 536 XMR is exchanged to Bitcoin Cash (BCH) on ShapeShift across nine transactions, completing the chain-hopping laundering cycle: BTC → XMR → BCH. [19]

Of note, the chain-hopping approach used here is a technique referred to in cryptocurrency forensics as converting funds through a privacy-focused cryptocurrency to break the transaction trail. The BTC to Monero conversion through ShapeShift and Changelly, followed by a later conversion from Monero to Bitcoin Cash, demonstrates a deliberate laundering workflow designed to complicate blockchain analysis for investigators.


Attribution & Legal


Date Event
2017-12-19 The United States and United Kingdom publicly attribute WannaCry to North Korea. White House Homeland Security Advisor Tom Bossert formally names North Korea in a Wall Street Journal op-ed [2] [3].
2018-09-06 The U.S. Department of Justice unseals a criminal complaint charging Park Jin Hyok, a North Korean programmer working for Chosun Expo Joint Venture (affiliated with Lab 110, a component of DPRK military intelligence). The complaint connects Park to the Lazarus Group and names him as a conspirator in WannaCry, the 2014 Sony Pictures attack, and the 2016 Bangladesh Bank SWIFT heist. The U.S. Treasury simultaneously sanctions Park and Chosun Expo [21].
2021-02-17 The DOJ expands the indictment, adding two further Lazarus Group members, Jon Chang Hyok and Kim Il, both members of the Reconnaissance General Bureau (RGB). The expanded indictment details additional financial crimes totalling over $1.3 billion in attempted theft across banks, cryptocurrency services, and extortion campaigns conducted from 2017 through 2020 [21].

Basic Static Analysis

The following section provides a basic analysis of the Ransomware.wannacry.exe.malware binary, focused on fingerprinting the binaries hashes, framework coverage, imports, strings, and virtual vs raw size.

Fingerprinting & Framework Coverage

Using Mandiant's CAPA tool [22], we can profile a lot of the binary to help build the broad strokes of the binary. 

Running the command, capa Ransomware.wannacry.exe.malware, provides the following:

The MD5, SHA1, and SHA256, allows us as the researcher to identify if there are any samples known in the wild, our typical first stop is to use VirusTotal to help identify the binary. As the link below shows, we searched using the SHA256 identifier, which points directly to the WannaCry.exe

Additionally, we can confirm that the binary is a Windows PE binary using the x86 architecture. These components all help identify our binaries execution requirements for both stages of the dynamic testing, but also our Advanced Static Analysis component. 

Researchers Note: While we can trust many of the VirusTotal findings, we should use it as a component in the investigation but also look to verify the findings.

In addition to its initial profiling, CAPA provides a thorough output of the coverage against the MITRE ATT&CK [23] and Malware Behaviour Catalogue (discussed towards the end of the post) [24].

MITRE ATT&CK

  • Defense Evasion
    • Obfuscated Files or Information::Indicator Removal from Tools [T1027.005] 
  • Discovery
    • File and Directory Discovery [T1083]
    • System Information Discovery [T1082]
    • System Network and Configuration Discovery [T1016] 
  • Execution
    • Shared Modules [T1129]
    • System Services::Service Execution [T1569.002] 
  • Persistence 
    • Create or Modify System Processes: Windows Service [T1543.003]

Portable Executable and String Analysis

The next point of call after we have fingerprinted the binary is to look at the strings to help provide us with any leads on what it could be doing. While the traditional tool strings can be used, Mandiant released FLARE-FLOSS, that de-obfuscates the Malware to get a reverted binary for analysis [25].  

Using the command : floss Ransomware.wannacry.exe.malware -n 10 the following strings of interest were found: 

  • URL:  www[dot]iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea[dot]com
  • String Interpolation C:\%s\qeriuwjhrf and cmd.exe /c
  • Cryptography FunctionsCryptGenKey, CryptDecrypt/Encrypt, Crypt Destroy, and CryptAcquireContextA
  • Persistence: tasksche.exe and icacls . /grant Everyone:F /T /C /Q 

Of note, there are many other strings of interest, however, these are also covered by PEStudio, such as the imports table illustrated in Figure 2. 

Figure 2, illustrates the cryptographic controls that are being imported into the Malware. This helps strengthen the evidence of its cryptographic capabilities, whether this is for encrypting the system or for encrypted communication channels.

PEStudio Import of Cryptographic Controls
Figure 2: PEStudio Import of Cryptographic Controls

Basic Dynamic Analysis

The following section is broken into two parts, networking and no networking. This is to identify if there is any difference in the malware and any trigger points that could occur. 

Networking

Executing the binary with no administrator privileges does not detonate the binary, therefore, the binary requires administrator privileges for it to be detonated.
 
When we execute the binary using our administrator permissions, the malware reaches out to the inetsim running in Remnux. The packet capture highlighted below in red shows the URL that is being used as a mechanism to prevent execution of the binary, this is both in the DNS capture and within the HTTP capture. 
 
Figure 3: Wireshark capture of WannaCry.exe

No Networking 

Removing the network connectivity and re-executing the binary, we get the successful execution of the binary and having the relevant artifacts generated.
 
Administrator detonation of WannaCry.exe
Figure 4: Administrator detonation of WannaCry.exe

Advanced Static Analysis

Figure 4, illustrates the usage of the URL found in the strings section of the Basic Static Analysis section. Reviewing the findings, we can see that the binary tries to reach out to the URL and if successful will terminate the process. This is confirmed in the Basic Dynamic Analysis, whereby running the Malware binary with the simulated network and no-simulated network experiments.

Ghidra Analysis highlighting the use of the URL in the binary

Figure 5: Ghidra Analysis highlighting the use of the URL in the binary 

With the identification of the JNZ function highlighted in blue in Figure 6 a patch can be added to allow packets to be captured simply by changing the JNZ to a JZ.
 
Figure 6: WannaCry.exe patching with Ghidra
 
Re-detonating the malware, packets will begin to stream to our inetsim and Wireshark within the Remnux system.
 
With the removal of the killswitch check, the next part is focused on the extraction of the resources from each of the payloads. WannaCry is a two payload infection, one payload creates an initial infection, persistence, and worm, which then loads the second stage for encryption.

Stage One 

The initial infection binary, mssec.exedb349b97c37d22f5ea1d1841e3c89eb4, uses a service for its persistence mechanism named, Microsoft Security Center (2.0) Service this can be seen both in the source code highlighted in Figure 7 but also captured in Dynamic Analysis Persistence Figure 21.
 
mssec stage one persistence service
Figure 7: mssec stage one persistence service
 
Additionally, stage one has a Portable Executable within its resources as illustrated in Figure 8. Simply extracting the R resource from mssec.exe either with the built in functionality of PEStudio or wrestool we have the second stage binary to work with.
 
r.exe resource
Figure 8: r.exe resource extraction via PEStudio

Stage Two 

Repeating the same process again on the r.exe binary hosts an encrypted ZIP archive referred to as XIA within its resource section.
 
Figure 9: xia.zip resource extraction via PEStudio
 
By opening the zip with 7zip, the core components of the ransomware are found, see Figure 10 
 
XIA Encrypted Zip
Figure 10: XIA Encrypted Zip

To extract these for analysis a password is required. To gain access to this password the reverse engineering of the stage two payload is required or capturing the function via a debugger, such as x32dbg. I'll be showing the use of both instances, but the debugger will be informed by the findings of Ghidra.
 
Extract encrypted zip shown in Ghidra
Figure 11: Extract encrypted zip shown in Ghidra
 
Figure 12: WNcry@2o17 moved to EBX
 
While not necessary in this instance as we can simply test the string with the encrypted archive, a step through the r.exe binary shows that the EAX register is doing the comparison to the EDI register, that holds the WNcry@2o17 string before the extraction of each of the contents found within the archive.

String comparison in x32dbg
Figure 13: String comparison in x32dbg

The final extraction of the contents, the identification of each of the files demonstrates the Command and Control (C2) mechanism, along with the ransomware details.

Figure 14: File breakdown for XIA contents

The following list provides a description of the purpose of each file:
  • r.wnry - This is the ransomware message
  • s.wnry - This is a tor client
  • b.wnry - This is the bitmap image that is loaded once the contents has been encrypted
  • c.wnry - The data in this provides tor onion addresses
  • u.wnry - This is the decryption tool
  • t.wnry - This is the encryption tool
  • taskdl.exe - This is the WNCRYT file deletion tool
  • taskse.exe - This launches the decryption tool 

Dumping the contents of the c.wnry, the C2 Tor onion addresses are identified:

  • gx7ekbenv2riucmf[.]onion
  • 57g7spgrzlojinas[.]onion
  • xxlvbrloxvriy2c5[.]onion
  • 76jdd2ir2embyv47[.]onion
  • cwwnhwhlz52maqm7[.]onion

Lastly, the identification of the Bitcoin addresses can be easily done via the strings utility within Ghidra, by taking the Bitcoin address from the basic dynamic analysis tests we did and searching for one of them, the identification of the other two is easily found. 

By searching for references of the Bitcoin address strings, the following function conducts the randomisation of which address to use during the execution of the ransomware as shown in Figure 15. 

bitcoin addresses and randomisation function
Figure 15: Bitcoin addresses and randomisation function

Going to the memory addresses of 0040f488, 0040f464, and 0040f440, the full untruncated addresses are:
Figure 16: Bitcoin addresses at function locations

Advanced Dynamic Analysis

Leveraging the patched binary, we have a much larger remit of findings, most notably, the malware begins to scan the network for other hosts, conducts its initial connection for SMB connections to begin to leverage the ETERNALBLUE payload. In addition to its reconnaissance and lateral movement, the malware creates persistence on the system and loads the second stage payload.

Network Reconnaissance

As we can see illustrated in Figure 17, the initial reach out to the domain is done, as the binary has been patched it will trigger a zero within the code, allowing the continuation of execution, the binary then begins to look for other hosts on the network, as well as, some hosts on the Internet.
 
Figure 17: TCP connections going to port 445
 
Within the host itself, we can see the connections being made within Procmon as illustrated in Figure 18.
 
Procmon of TCP connectivity
Figure 18: Procmon of TCP connectivity
 
Finally, the service is making the connection on Port 9050, which can be seen with SysInternals as shown in Figure 19.
 
Sysinternals port 9050
Figure 19: SysInternals port 9050

Exploitation

In addition to the network reconnaissance, Wireshark can now see traffic within going to port 445 to identify vulnerable targets to exploit using MS17-010, also known as, ETERNALBLUE.
 
It is well-established that WannaCry leverages the ETERNALBLUE vulnerability, this payload exploits the SMBv1 connection within Windows devices so that it can spread and conduct lateral movement. 
 
Figure 20: ETERNALBLUE exploitation caught via Wireshark
 
Using a secondary Windows machine, specifically, Metasploitable3, due to its susceptibility of ETERNALBLUE, we can evidence the worm activity on the network that is purposefully exploited via the ETERNALBLUE SMB vulnerability
 
Metasploitable3 EternalBlue and WannaCry
Figure 21: Metasploitable3 ETERNALBLUE and WannaCry

Persistence 

During the execution of the Malware, two persistence mechanisms are triggered, one using the Microsoft Security Center (2.0) Service that is generated from the initial binary as highlighted in Figure 22. 

Figure 22: Stage One persistence with Microsoft Security Center (2.0) Service

The second can be identified by the second stage payloads that can be identified by filtering on all CreateFile operations within Procmon for the process name of the Malware binary. It is worth noting that the location name is generated on execution of the Malware and will therefore be different each time.
 
WannaCry.exe second stage storage location
Figure 23: WannaCry.exe second stage storage location 

For the Malware to maintain its foothold, it runs the tasksche.exe application from the storage location shown above, it creates a new service, in this instance named xpaaslxwir046 that can be seen within Task Manager, as illustrated in the figure below.
 
Task Manager shows persistence mechanism
Figure 24: Task Manager shows persistence mechanism 

Indicators of Compromise

Below is a collection of Indicators of Compromise (IoC) relating to the WannaCry directory upon infection:

File MD5 SHA1 SHA256
b.wnry c17170262312f3be7027bc2ca825bf0c f19eceda82973239a1fdc5826bce7691e5dcb4fb d5e0e8694ddc0548d8e6b87c83d50f4ab85c1debadb106d6a6a794c3e746f4fa
c.wnry ae08f79a0d800b82fcbe1b43cdbdbefc f6b08523b1a836e2112875398ffefffde98ad3ca 055c7760512c98c8d51e4427227fe2a7ea3b34ee63178fe78631fa8aa6d15622
r.wnry 3e0020fc529b1c2a061016dd2469ba96 c3a91c22b63f6fe709e7c29cafb29a2ee83e6ade 402751fa49e0cb68fe052cb3db87b05e71c1d950984d339940cf6b29409f2a7c
s.wnry ad4c9de7c8c40813f200ba1c2fa33083 d1af27518d455d432b62d73c6a1497d032f6120e e18fdd912dfe5b45776e68d578c3af3547886cf1353d7086c8bee037436dff4b
t.wnry 5dcaac857e695a65f5c3ef1441a73a8f 7b10aaeee05e7a1efb43d9f837e9356ad55c07dd 97ebce49b14c46bebc9ec2448d00e1e397123b256e2be9eba5140688e7bc0ae6
taskdl.exe 4fef5e34143e646dbf9907c4374276f5 47a9ad4125b6bd7c55e4e7da251e23f089407b8f 4a468603fdcb7a2eb5770705898cf9ef37aade532a7964642ecd705a74794b79
taskse.exe 8495400f199ac77853c53b5a3f278f3e be5d6279874da315e3080b06083757aad9b32c23 2ca2d550e603d74dedda03156023135b38da3630cb014e3d00b1263358c5f00d
u.wnry 7bf2b57f2a205768755c07f238fb32cc 45356a9dd616ed7161a3b9192e2f318d0ab5ad10 b9c5d4339809e0ad9a00d4d3dd26fdf44a32819a54abf846bb9b560d81391c25


Malware Behaviour Catalogue

The following Malware Behaviour Catalogue (MBC) mappings are based on the static and dynamic analysis conducted in this post. Behaviours marked with ★ extend the existing MBC WannaCry corpus entry. For MITRE ATT&CK technique mappings, see ATT&CK: WannaCry - Techniques Used.


Enhanced ATT&CK Techniques

ObjectiveBehaviourIDEvidence
ImpactData Encrypted for ImpactE1486WannaCry encrypts files for ransom using CryptEncrypt API. Identified via static analysis of cryptographic imports and confirmed during dynamic detonation.
Defence EvasionObfuscated Files or InformationE1027 ★The XIA ZIP archive embedded in the stage two payload is password-encrypted (WNcry@2o17), requiring reverse engineering or debugging to extract contents.
PersistenceCreate or Modify System Process::Windows ServiceE1543.003 ★Creates "Microsoft Security Center (2.0) Service" for stage one persistence. Confirmed in Ghidra and captured in dynamic analysis via Task Manager.
Lateral MovementExploitation of Remote ServicesE1210 ★Exploits ETERNALBLUE (MS17-010) over SMBv1 on port 445 to propagate. Captured via Wireshark and confirmed on a Metasploitable3 target.
DiscoveryRemote System DiscoveryE1018 ★Scans for hosts on port 445 for lateral movement targets. Observed in Procmon and Wireshark TCP connection logs.
ExecutionSystem Services::Service ExecutionE1569.002 ★Uses service execution for both the stage one persistence service and the stage two tasksche.exe service.
Command and ControlIngress Tool TransferE1105 ★Drops a Tor client (s.wnry) from the encrypted XIA archive to establish a C2 communication channel.
Defence EvasionFile and Directory Permissions ModificationE1222 ★Executes icacls . /grant Everyone:F /T /C /Q to grant full permissions to all users, identified via string analysis.


MBC Behaviours

ObjectiveBehaviourIDEvidence
Defence EvasionHidden Files and Directories::File AttributeF0005WannaCry uses the +h attribute to hide its dropped files from directory listings.
PersistenceRegistry Run Keys / Startup FolderF0012WannaCry creates two registry run keys to ensure persistence across reboots.
Defence EvasionSelf DeletionF0007Killswitch mechanism: if the hardcoded domain resolves via DNS, the binary terminates and deletes itself. Confirmed via Ghidra reversing and the networking vs. no-networking dynamic tests.
DiscoverySelf DiscoveryB0038WannaCry checks the size of the file it loads into memory before proceeding with execution.
DiscoverySelf Discovery::Validate DataB0038.002Checks a string, key length, and magic number before decrypting an embedded DLL.
DiscoverySelf Discovery::Validate Data LengthB0038.003Checks the data length of a PE section before decrypting an embedded DLL.
ExecutionInstall Additional ProgramB0023 ★Stage one extracts the stage two PE from its resources (r.exe), which then extracts and deploys the XIA archive contents including tasksche.exetaskdl.exe, and the .wnry components.
Command and ControlC2 CommunicationB0030 ★Communicates via Tor using five onion addresses extracted from c.wnry. Port 9050 connections observed via SysInternals during advanced dynamic analysis.


Micro-Behaviours

Micro-ObjectiveMicro-BehaviourIDEvidence
CryptographyEncrypt DataC0027CryptEncrypt API imported and used for file encryption. Identified via PEStudio import table and FLOSS string analysis.
CryptographyDecrypt DataC0031CryptDecrypt API imported for decryption of embedded DLL and archive contents.
CryptographyGenerate Pseudo-random SequenceC0021CryptGenKey API used for key generation. Bitcoin address randomisation function identified in Ghidra.
File SystemCreate FileC0016Stage two payload files written to disk. Procmon CreateFile operations captured during dynamic analysis showing the storage location for second stage components.
ProcessCreate ProcessC0017Launches tasksche.exe as a new service process and executes commands via cmd.exe /c.


Classification

For the classification of the Malware based on its findings, the Computer Anti-Virus Research Organization (CARO) malware naming scheme [26] identifies WannaCry as:

Ransom:Win32/WannaCrypt

The justification for this breakdown is as follows:

  • Ransom - Based on the ransomware note provided upon infection
  • Win32 - Based on the architecture during the basic analysis stage, i.e., i386 + PE format
  • WannaCrypt - Based on the identification provided by other security researchers

Defender Recommendations

Dealing with WannaCry can be difficult for organisations that have limited automated containment strategies. The best defence to reduce impact of ransomware is to be up-to-date on all security patches, Disable SMBv1 with the steps documented at Microsoft Knowledge Base Article 2696547, consider adding a rule on your router or firewall to block incoming SMB traffic on port 445 [27]. Combining the proactive approach of being update, the reactive approach is to to contain as quickly as possible and limit the blast radius through network segmentation. However, these do not make a full effective approach and large scale systems can have multiple sources of connectivity that can be related to shadow IT, such as third-party connectors.

Helping detect WannaCry, the four YARA rule have been made based on the findings taken from the analysis above. In addition, US-CERT created a number of YARA Rules, which can be found: here.

/*
    WannaCry Ransomware Detection Rules
    Author: Jack Whitter-Jones
    Date: 2026-02-08
    Classification: Ransom:Win32/WannaCrypt
    
    These rules are split by infection stage to reduce false positives
    and allow granular detection across the kill chain.
    
    References:
        - https://www.cloudflare.com/learning/security/ransomware/wannacry-ransomware/
        - https://attack.mitre.org/software/S0366/
        - https://learn.microsoft.com/en-us/unified-secops/malware-naming
*/


// ---------------------------------------------------------------------------
// Rule 1: WannaCry Stage One Dropper (mssecsvc.exe)
//
// Targets the initial binary responsible for the killswitch check,
// service persistence, ETERNALBLUE propagation, and stage two extraction.
// ---------------------------------------------------------------------------
rule WannaCry_Stage1_Dropper
{
    meta:
        description     = "Detects WannaCry stage one dropper (mssecsvc.exe)"
        author          = "Jack Whitter-Jones"
        date            = "2026-02-08"
        classification  = "Ransom:Win32/WannaCrypt"
        severity        = "Critical"
        hash_md5        = "db349b97c37d22f5ea1d1841e3c89eb4"
        hash_sha256     = "24d004a104d4d54034dbcffc2a4b19a11f39008a575aa614ea04703480b1022c"

        // MITRE ATT&CK mapping (concatenated to avoid duplicate keys)
        mitre_tactics       = "Defense Evasion, Discovery, Execution, Persistence"
        mitre_techniques    = "T1027.005, T1083, T1082, T1016, T1129, T1569.002, T1543.003"

    strings:
        // -- Killswitch domain (the single most unique identifier) --
        $killswitch = "iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea" ascii nocase

        // -- Service persistence --
        $svc_name   = "mssecsvc2.0" ascii wide
        $svc_display = "Microsoft Security Center (2.0) Service" ascii wide

        // -- Staging path pattern --
        $path_fmt   = "C:\\%s\\qeriuwjhrf" ascii

        // -- Command execution for permissions --
        $icacls     = "icacls . /grant Everyone:F /T /C /Q" ascii

        // -- SMB exploitation marker: ETERNALBLUE handshake bytes --
        // These are the first bytes of the trans2 SESSION_SETUP exploit
        // payload specific to MS17-010 as used by WannaCry
        $smb_exploit = { 00 00 00 00 00 00 00 00 FF 53 4D 42 72 }

        // -- PE resource containing stage two --
        // The 'R' resource is a PE that starts with MZ; look for MZ
        // at a typical resource offset boundary after the main PE headers
        $nested_pe  = { 4D 5A 90 00 03 00 00 00 }

    condition:
        // Must be a PE file
        uint16(0) == 0x5A4D and

        // Reasonable size bound for stage one (~3.6 MB)
        filesize > 1MB and filesize < 5MB and

        // Import table must reference networking and service APIs
        pe.imports("ws2_32.dll") and
        pe.imports("advapi32.dll") and

        (
            // High-confidence: killswitch + service persistence
            ($killswitch and 1 of ($svc_*)) or

            // High-confidence: killswitch + staging path + permissions escalation
            ($killswitch and $path_fmt and $icacls) or

            // Structural: nested PE in resources + service creation + networking
            ($nested_pe and 1 of ($svc_*) and pe.imports("wininet.dll"))
        )
}


// ---------------------------------------------------------------------------
// Rule 2: WannaCry Stage Two Ransomware Payload (tasksche.exe / r.exe)
//
// Targets the second-stage binary that contains the encrypted XIA archive
// holding the ransomware components (.wnry files, tor client, etc.)
// ---------------------------------------------------------------------------
rule WannaCry_Stage2_Payload
{
    meta:
        description     = "Detects WannaCry stage two ransomware payload"
        author          = "Jack Whitter-Jones"
        date            = "2026-02-08"
        classification  = "Ransom:Win32/WannaCrypt"
        severity        = "Critical"
        mitre_tactics       = "Impact, Persistence, Command and Control"
        mitre_techniques    = "T1486, T1543.003, T1573"

    strings:
        // -- Encrypted archive password (unique to WannaCry) --
        $zip_pass = "WNcry@2o17" ascii wide

        // -- Ransomware component filenames (inside the XIA archive) --
        $wnry_r = "r.wnry" ascii
        $wnry_s = "s.wnry" ascii
        $wnry_t = "t.wnry" ascii
        $wnry_u = "u.wnry" ascii
        $wnry_b = "b.wnry" ascii
        $wnry_c = "c.wnry" ascii

        // -- Operational binaries --
        $taskdl = "taskdl.exe" ascii
        $taskse = "taskse.exe" ascii
        $tasksche = "tasksche.exe" ascii

        // -- Bitcoin wallet addresses --
        $btc1 = "13AM4VW2dhxYgXeQepoHkHSQuy6NgaEb94" ascii
        $btc2 = "12t9YDPgwueZ9NyMgw519p7AA8isjr6SMw" ascii
        $btc3 = "115p7UMMngoj1pMvkpHijcRdfJNXj6LrLn" ascii

        // -- Tor C2 onion addresses --
        $tor1 = "gx7ekbenv2riucmf" ascii
        $tor2 = "57g7spgrzlojinas" ascii
        $tor3 = "xxlvbrloxvriy2c5" ascii
        $tor4 = "76jdd2ir2embyv47" ascii
        $tor5 = "cwwnhwhlz52maqm7" ascii

        // -- Crypto API usage pattern (sequential imports suggest
        //    key generation → encrypt → decrypt workflow) --
        $capi_gen     = "CryptGenKey" ascii
        $capi_encrypt = "CryptEncrypt" ascii
        $capi_acquire = "CryptAcquireContextA" ascii
        $capi_import  = "CryptImportKey" ascii

    condition:
        uint16(0) == 0x5A4D and
        filesize < 5MB and

        (
            // High-confidence: archive password is nearly unique to WannaCry
            $zip_pass or

            // High-confidence: 4+ .wnry component names in one binary
            4 of ($wnry_*) or

            // Strong signal: Bitcoin wallets + tor addresses
            (2 of ($btc*) and 2 of ($tor*)) or

            // Behavioural combination: crypto workflow + operational tools + C2
            (
                3 of ($capi_*) and
                ($taskdl or $taskse or $tasksche) and
                2 of ($tor*)
            )
        )
}


// ---------------------------------------------------------------------------
// Rule 3: WannaCry Encrypted Archive (XIA resource)
//
// Detects the encrypted ZIP archive embedded as a PE resource. This is
// useful for catching the payload in transit or extracted on disk.
// ---------------------------------------------------------------------------
rule WannaCry_XIA_Archive
{
    meta:
        description = "Detects WannaCry encrypted XIA ZIP archive containing ransomware components"
        author      = "Jack Whitter-Jones"
        date        = "2026-02-08"
        severity    = "High"

    strings:
        // ZIP local file header magic
        $pk_header = { 50 4B 03 04 }

        // Filenames that would appear in the ZIP central directory
        $zf_r = "r.wnry" ascii
        $zf_s = "s.wnry" ascii
        $zf_t = "t.wnry" ascii
        $zf_u = "u.wnry" ascii
        $zf_b = "b.wnry" ascii
        $zf_c = "c.wnry" ascii
        $zf_taskdl = "taskdl.exe" ascii
        $zf_taskse = "taskse.exe" ascii

    condition:
        $pk_header at 0 and
        filesize < 5MB and
        5 of ($zf_*)
}


// ---------------------------------------------------------------------------
// Rule 4: WannaCry Network Artefact (killswitch beacon)
//
// For use with network traffic captures or proxy logs. Detects the
// killswitch domain request that WannaCry makes before execution.
// ---------------------------------------------------------------------------
rule WannaCry_Killswitch_Beacon
{
    meta:
        description = "Detects WannaCry killswitch domain in network captures or memory"
        author      = "Jack Whitter-Jones"
        date        = "2026-02-08"
        severity    = "Critical"

    strings:
        $domain = "iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea.com" ascii nocase

    condition:
        $domain
}

Conclusion

This post has demonstrated an analysis of core components of WannaCry, via a threat intelligence profile, a detailed walkthrough of the analysis and reverse engineering of the WannaCry malware. This included the identification of Indicators of Compromise, how classification of Malware is conducted using the Computer Antivirus Research Organization (CARO) naming scheme based on the findings of the analysis, along with defender recommendations both proactively and reactively to combat the campaign due to its continued prevalence.

As many articles have illustrated the technical components of WannaCry, many miss a foundational threat profile of the campaign and how the impact of the attack led to financial gain. This post ties many research areas together to constitute a detailed end to end understanding of the Lazarus Group's objectives for the campaign, notably, financial gain. 

While the analysis covers a broad range of areas it does not fully complete the picture, specifically, the further analysis of the ETERNALBLUE [4] and DOUBLEPULSAR [5] exploits. Additionally, an interesting area of research is the cryptocurrency analysis against the crypto wallets that were used in this campaign. A detailed visual showing a tree diagram of all the wallets and connected activity would be interesting. Of which both will be explored in subsequent posts.

References

Cognitive CTI - Building a Scalable, Self-Hosted Threat Intelligence Pipeline with AI

Introduction Threat Intelligence is a fairly superfluous component to security for most individuals or organisations that are growi...