Graphalgo — Terraform & Go Supply-Chain Hunt
Executive Summary
A DPRK-linked actor (the Graphalgo cluster, publicly attributed to Lazarus by ReversingLabs since May 2025) has expanded its fake-recruiter supply-chain campaign beyond npm and PyPI into the HashiCorp Terraform Registry and the Go module ecosystem — the first publicly documented case of malware distributed through Terraform providers. Two trojanized Terraform providers (gocommunity-io/dockerd, and kreuzwenker/docker, a one-character typosquat of the legitimate kreuzwerker/docker provider with 56M downloads) and two malicious Go modules (gocommunity.io/orderedbtree, gogets.dev/btreex) carry a Go RAT that is functionally a Go port of the existing Graphalgo npm/PyPI malware, sharing C2 infrastructure and a threat-actor public key with the JavaScript samples.
The attacker's objective is direct: reach a developer or DevOps workstation via a fake Web3 job-interview lure, get the victim to run a coding task that pulls in the trojanized package, and land a targeted, self-deleting RAT that is gated to fire only under narrow, attacker-chosen conditions (a specific SHA256 hash of Terraform variables, or a crafted numeric input) — explicitly to frustrate researchers who find the backend payload without its matching front end. The RAT's dual C2 (an Ethereum smart contract on the Arbitrum Sepolia testnet, and an attacker-controlled Slack workspace polled via conversations.history) is a sophisticated, low-noise dead-drop design built to survive endpoint remediation: because commands are pulled, not pushed, an attacker retains reachability even after the malicious package itself is removed.
The highest-value defensive angle is not trying to detect the AES-encrypted payload or the blockchain traffic content (both are opaque to EDR by design) — it is catching the narrow, low-FP process-lineage signature of a Terraform provider or Go module spawning a detached go run ., and locking down provider/module installation to a vetted mirror before a targeted developer or CI/CD runner is ever exposed. Terraform providers routinely run with direct access to cloud IAM credentials, so a single successful infection on a DevOps host is a materially worse outcome than the same RAT on a typical developer laptop.
terraform init/terraform apply or go get/go run against untrusted registries are the primary exposure. Lock provider/module installation to an internal mirror (Section 13, Playbook A) before relying on detection alone — the activation gate on this malware is specifically designed to evade sandbox and default-tenant detection.
Source Review & Web Hunter Notes
| Tier | Source | Key Finding | Carry Forward |
|---|---|---|---|
| Tier 1 | Aikido Security — "Graphalgo campaign spreads to Terraform providers and Go Modules" (01-aikido-vendor-advisory.txt) | Primary technical disclosure: package names, activation-gate SHA256, payload hashes, Slack/blockchain C2 mechanics, threat-actor public key, GitHub pivot table | yes |
| Tier 2 | The Hacker News — "Attackers Use Malicious Terraform Providers to Deliver Go Malware via HashiCorp Registry" (02-thehackernews.txt) | Corroborates Aikido's package/download-count figures; also carries independent CloudSEK reporting on a related-but-distinct GHAPPIER/PolinRider overlap incident | partial — overlap incident kept out of this pack's shipped IOCs, see below |
| Tier 1 | ReversingLabs — "Inside the 'graphalgo' fake crypto developer recruitment campaign" (03-reversinglabs-graphalgo.txt) | Original May-2025 attribution of the graphalgo npm/PyPI cluster to DPRK's Lazarus Group; naming conventions and RAT-delivery mechanics that this Go port shares | yes — attribution and background only |
| Tier 1 | SentinelOne — "TraderTraitor Backdoors Resurface on Victim With No Crypto Ties" (04-sentinelone-tradertraitor.txt) | A related but DISTINCT DPRK sub-cluster (TraderTraitor/UNC4899) weaponizing .terraform.lock.hcl to point at rogue provider registries, delivering a different malware family (FLATROOF/ROOFDECK, Rust, macOS) | narrative context only — illustrates Terraform Registry abuse as a broader emerging DPRK TTP; its own IOCs are NOT shipped in this pack (see below) |
Web Hunter decisions
- Prompt-injection scan: all four fetched sources were scanned for text directed at an AI agent (instructions to run commands, fetch other URLs, change output paths, claim authorization, etc.). None was found in any of the four sources. No source was dropped from citation for this reason.
- Provenance discipline: every atomic indicator shipped in this pack (Section 4, Section 10) was copied verbatim from one of the four saved snapshots and mechanically re-verified against them before publish. No indicator was invented.
- v0.2 revision — infrastructure de-duplication (this pass): the TraderTraitor typosquatted Terraform-registry domains (
registry.hashicorp-aws.com,registry.hashicorp-aws.io,registry.hashicorp-terraform.io) were removed entirely aftercheck_coverage.py --check-packflagged them as shared infrastructure already owned by the publishedTraderTraitor-FLATROOF-ROOFDECK-Hunt.htmlpack. That is correct: those domains are TraderTraitor's own C2/distribution infrastructure, a distinct malware family (FLATROOF/ROOFDECK, Rust, macOS), not Graphalgo's. The query built on them (former Q9) was rewritten around a Graphalgo-owned indicator instead (see Section 8, Q9). The SentinelOne source is kept for narrative "Terraform Registry as an emerging attack surface" context only — it ships no IOCs here. - Same reasoning applied to the GHAPPIER/PolinRider overlap: on review, the IP
193.247.144[.]38and the trailing byte marker68656c6c6f6970626f742121("helloipbot!!") are CloudSEK's GHAPPIER-loader indicators (via The Hacker News), overlapping with a distinct, long-running cluster called PolinRider — delivered through a different incident (the compromised@dforge-core/dforge-mcpnpm package), not the Graphalgo Terraform/Go RAT.check_coverage.pydid not flag these two specifically (the publishedPolinRider-Hunt.htmlcurrently ships them only asREPLACE_WITH_placeholders, not live values), but the underlying attribution problem is identical, so they were removed from this pack on the same reasoning rather than waiting for the gate to catch a future collision once PolinRider-Hunt.html is populated. Hunters tracking that overlap should use PolinRider-Hunt.html. - Single-source discipline: Aikido is the sole source for the campaign's payload hashes, C2 workspace names, and the threat-actor public key. Per standing IOC-governance policy, every indicator in this pack ships at Detect, never Prevent, until an independent second source corroborates it or a 14-day clean bake completes in your own tenant (see Section 11).
- No atomic IOC was converted to a placeholder — every remaining indicator in this pack is independently re-confirmed against the live sources and is exclusively Graphalgo's own infrastructure.
Hunt Brief & Attack Chain
Hypotheses (ordered by fidelity): (1) a terraform-provider-* binary executes from a plugin cache path and spawns a detached go run . — highest confidence, lowest FP; (2) the exact typosquat string kreuzwenker appears in a process command line — near-zero FP; (3) a known-malicious payload hash is written to disk; (4) DNS resolution to the threat actor's fake Go ecosystem domains or its three named attacker Slack workspaces; (5) a CI/CD runner spawns terraform/go as a pivot toward production cloud credentials; (6) a non-Slack process polls api.slack.com broadly (medium confidence, DNS-level only); (7) a developer host talks to public blockchain RPC infrastructure with no Web3 business justification (low confidence, high FP, correlation-only).
| Step | Telemetry | Hunt Angle |
|---|---|---|
| 1. Social-engineering lure (fake Web3 recruiter on LinkedIn/Facebook, fake coding-interview task) | None (pre-endpoint) | Security-awareness training; HR/recruiting-process controls (Section 12, Strategic tier) |
2. Victim runs terraform init/apply or go get/go build against the malicious package in the provided repo | ProcessRollup2, CommandLine | Q1, Q5, Q7 |
| 3. Activation gate: SHA256(containerName+networkID) == fixed value, or a crafted numeric field | None — not visible to EDR (in-memory Terraform variable comparison) | Coverage gap (Section 15) — cannot be hunted directly; only its downstream effects (steps 4-8) are observable |
| 4. AES-decrypt embedded file path/payload using the gate hash as key material; unzip archive contents | File-write telemetry on the decrypted archive | Q6 (hash match on import-resource.sqlite3 / btreex.sql) |
5. Execute decrypted code via detached go run . | ProcessRollup2 parent-child lineage | Q2 |
6. Second-stage RAT gathers host info; check-in posted to Slack channel frontend-devs in portfolio-devs.slack.com | DnsRequest (slack.com), network telemetry | Q4 (broad), Q9 (precise, named workspace) |
7. Ephemeral keypair generated; shared key derived from two hardcoded threat-actor public keys; all further Slack traffic moves to qa-announcements in portfolio-testers.slack.com | DnsRequest (slack.com, recurring) | Q4 (broad), Q9 (precise, named workspace) |
8. Parallel blockchain C2: read/write Arbitrum Sepolia contract 0xAD02b5cDE693529d3bdA0266299501ad0193036C via setCPubKey; poll every 3s (blockchain) / 10s (Slack) | DnsRequest/NetworkConnectIP4 to blockchain RPC infra | Q8 (behavioral only) |
| 9. Execute attacker-issued Go or Node.js commands, or self-delete | ProcessRollup2 (node/go child processes), file-delete telemetry | Partial — downstream child-process hunts only; content of commands is opaque (encrypted) |
Consolidated IOC Table
All indicators below ship at action detect, not prevent — this pack is single-primary-sourced (Aikido) for the core Graphalgo Terraform/Go indicators. Promote to Prevent only after independent second-source corroboration or a clean 14-day bake in your own tenant (Section 11).
| Type | Value | Confidence | Action | Context | Expiry |
|---|---|---|---|---|---|
| sha256 | 5f892a5424e88a21a3eb3d7f82ebf04d8ac31cdb19ada25153be4165df977d0f | high | detect | import-resource.sqlite3 — AES-encrypted/zipped payload embedded in both malicious Terraform providers | 2027-03-24 |
| sha256 | ab01686d87565250fc4989faddb877d793667b07ec217a61cbd798f5695d62f5 | high | detect | btreex.sql — ZIP archive masquerading as a SQL file, payload for gogets.dev/btreex | 2027-03-24 |
| domain | gocommunity.io | high | detect | Threat-actor-controlled fake Go package-ecosystem site; social-engineering legitimacy prop | 2027-03-24 |
| domain | gogets.dev | high | detect | Threat-actor-controlled fake Go package-ecosystem site; social-engineering legitimacy prop | 2027-03-24 |
| domain | portfolio-devs.slack.com | high | detect | Attacker-controlled Slack workspace, channel frontend-devs — initial check-in C2 | 2027-03-24 |
| domain | portfolio-testers.slack.com | high | detect | Attacker-controlled Slack workspace, channel qa-announcements — post-handshake C2 | 2027-03-24 |
| domain | mediumstar.slack.com | medium | hunt | Historical, previously-unreported attacker Slack workspace noted by Aikido | 2026-12-24 |
| package | gocommunity-io/dockerd (Terraform provider, 222 downloads) | high | hunt | Trojanized Terraform provider, hidden entry point in resource_docker_container_funcs.go | 2027-03-24 |
| package | kreuzwenker/docker (Terraform provider, 1,449 downloads) | high | hunt | Typosquat of kreuzwerker/docker (56M downloads); same hidden entry point | 2027-03-24 |
| package | gocommunity.io/orderedbtree (Go module, published 2026-08-11) | high | hunt | RAT embedded in plaintext, no encryption layer | 2027-03-24 |
| package | gogets.dev/btreex (Go module, published 2026-09-08, commits forged to Nov 2025) | high | hunt | RAT hidden inside btreex.sql; requires a crafted price-integer input to activate | 2027-03-24 |
| crypto-key | 302a300506032b656e...d1b526702 (88 hex chars, ed25519 SPKI, full value in Section 10) | high | enrich | Threat-actor public key, shared across this Go RAT and the npm/PyPI Graphalgo samples since April 2026 (first seen in "modern-events") | 2026-12-24 |
| other | Arbitrum Sepolia testnet contract 0xAD02b5cDE693529d3bdA0266299501ad0193036C | high | hunt | Blockchain dead-drop C2; setCPubKey method, serviceData1/serviceData2 fields, ~3s poll interval | 2027-03-24 |
v0.2: the typosquatted Terraform-registry domains (TraderTraitor) and the GHAPPIER/PolinRider IP + trailing byte marker were removed from this table — they are other campaigns' own infrastructure, not Graphalgo's. See Section 2 for the full reasoning.
Affected Surface & Telemetry Matrix
| Surface | Required Telemetry | Priority | Gap Risk |
|---|---|---|---|
| Developer workstations (Windows/macOS/Linux) running Terraform | ProcessRollup2/SyntheticProcessRollup2, DnsRequest, PeFileWritten/ZipFileWritten | Critical | Low — standard Falcon sensor coverage |
| DevOps / platform-engineering workstations with cloud IAM credentials cached | Same as above + cloud provider audit logs (CloudTrail/Azure Activity Log) | Critical | Medium — cloud-side correlation requires a SIEM join outside Falcon |
| CI/CD runners (GitHub Actions self-hosted, GitLab Runner, Jenkins agents) | ProcessRollup2 parent-child lineage, NetworkConnectIP4 | Critical | Medium — ephemeral/containerized runners may not run a persistent sensor |
| Go module proxy / GOPROXY egress path | DnsRequest, NetworkConnectIP4 | High | Low |
| Terraform Registry / private provider mirror egress path | DnsRequest, NetworkConnectIP4 | High | Low |
| Slack API traffic (conversations.history polling) | DnsRequest only via Falcon; full URL-path visibility requires a TLS-terminating proxy or Slack's own audit log | High | High — see Coverage Map |
| Blockchain RPC traffic (Arbitrum Sepolia) | DnsRequest/NetworkConnectIP4 only; on-chain call data is opaque to EDR | High | High — see Coverage Map |
| In-memory activation-gate comparison (SHA256 of Terraform variables) | None available via EDR | Informational | High — not observable by design |
ATT&CK Mapping
| Tactic | Technique | Name | Observed Behavior |
|---|---|---|---|
| Resource Development | T1608.001 | Stage Capabilities: Upload Malware | Trojanized Terraform providers and Go modules published to the HashiCorp Registry and pkg.go.dev |
| Resource Development | T1585.001 | Establish Accounts: Social Media Accounts | Fake Web3 recruiter profiles on LinkedIn/Facebook; fake GitHub organizations (gocommunity-io, gogets-dev) and companion websites |
| Initial Access / Execution | T1195.002 | Supply Chain Compromise: Compromise Software Supply Chain | Malicious Terraform providers and Go modules delivered as fake coding-interview dependencies |
| Execution | T1204.002 | User Execution: Malicious File | Victim runs terraform init/apply or go get/build against the trojanized package |
| Defense Evasion | T1036 | Masquerading | kreuzwenker/docker (1,449 downloads) typosquats the legitimate kreuzwerker/docker provider (56M downloads) |
| Defense Evasion | T1027 | Obfuscated/Encrypted Files or Information | Payload AES-encrypted, keyed by a runtime-computed SHA256 gate hash; inert until the exact trigger condition is met |
| Defense Evasion | T1140 | Deobfuscate/Decode Files or Information | Malware AES-decrypts the embedded file path and unzips the archive at runtime |
| Command and Control | T1102.002 | Web Service: Bidirectional Communication | Slack conversations.history polling and Arbitrum Sepolia smart-contract read/write used as dual dead-drop C2 channels |
| Command and Control | T1071.001 | Application Layer Protocol: Web Protocols | HTTPS to api.slack.com and blockchain RPC endpoints carries all C2 traffic |
Native Audit-Log Hunts
These checks require no Falcon query — they read the developer's own project files and are cheap enough to run fleet-wide via RTR or an MDM/RMM script before or alongside the CQL hunts.
Go dependency audit (Linux/macOS/Windows)
grep -RIn "gocommunity\.io\|gogets\.dev" $(find / -name "go.sum" -o -name "go.mod" 2>/dev/null) 2>/dev/null # PowerShell equivalent: Get-ChildItem -Recurse -Include go.sum,go.mod -ErrorAction SilentlyContinue | Select-String -Pattern "gocommunity\.io|gogets\.dev"
Terraform lock-file audit
grep -RIn "kreuzwenker\|gocommunity-io" \ $(find / -name ".terraform.lock.hcl" 2>/dev/null) 2>/dev/null
GitHub organization / vendoring audit
Check whether any internal repo forks, vendors, or references code from the threat-actor-controlled GitHub identities: gocommunity-io, go-pack-tech, kreuzwenker, gogets-dev, victormmpp, markcary3, steveb082, go-community-admin.
Slack workspace visibility — an honest limit
This campaign's Slack C2 uses attacker-owned workspaces (portfolio-devs.slack.com, portfolio-testers.slack.com, mediumstar.slack.com) reachable from any host with outbound HTTPS. There is no native audit-log visibility into this from your own Slack tenant — your Slack admin console cannot see traffic to someone else's workspace. Rely on DNS/proxy/CASB egress logs (Q4, Q9) instead. Your own Slack Enterprise Grid App Manager audit is still worth running as a separate hygiene check, for any bot/app token with a conversations.history or channels:history scope your security team did not provision.
CrowdStrike LogScale CQL Hunt Queries
Looks for: A terraform-provider-* binary executing from a user-writable Terraform plugin cache path (.terraform.d/plugins, AppData\terraform.d\plugins) or downloaded directly from registry.terraform.io -- the exact vector Aikido documents for gocommunity-io/dockerd and kreuzwenker/docker.
// HUNT: Terraform Provider Plugin Execution From User-Writable Cache Paths // MITRE: T1195.002, T1204.002 // CONF: high // FP: low // COST: low // REQUIRES: ProcessRollup2/SyntheticProcessRollup2 on Windows, Linux and macOS developer + CI hosts // FALSE POSITIVES: an internal Terraform provider mirror published under the same terraform-provider-* naming convention // LOOKBACK: 7d (set window in the Falcon time picker, not in-query) #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | FileName = /^terraform-provider-.*$/i | (CommandLine = *.terraform.d*plugins* or CommandLine = *terraform.d\plugins* or CommandLine = *registry.terraform.io*) | table([ComputerName, UserName, FileName, ParentBaseFileName, CommandLine, SHA256HashData, @timestamp]) | sort(@timestamp, order=desc)
Looks for: A terraform-provider-* process spawning 'go' with a 'go run .' command line -- the documented second-stage launch mechanism (detached go run . executing the AES-decrypted Go RAT source).
// HUNT: Detached 'go run .' Execution Spawned By a Terraform Provider Process // MITRE: T1195.002, T1027, T1140 // CONF: high // FP: low // COST: low // REQUIRES: ProcessRollup2/SyntheticProcessRollup2 with parent-child lineage (ParentBaseFileName) // FALSE POSITIVES: provider SDK developers legitimately running 'go run .' inside a provider's own source tree -- rare on a compiled, installed provider's parent process // LOOKBACK: 7d (set window in the Falcon time picker, not in-query) #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ParentBaseFileName = /^terraform-provider-.*$/i | (FileName = /^go(\.exe)?$/i or CommandLine = *go run .*) | table([ComputerName, UserName, ParentBaseFileName, FileName, CommandLine, SHA256HashData, @timestamp]) | sort(@timestamp, order=desc)
Looks for: DNS resolution to the threat actor's two fake Go package-ecosystem domains (gocommunity.io, gogets.dev), stood up to lend legitimacy to the malicious module publications.
// HUNT: DNS Resolution to Fake Go Package-Ecosystem Domains (gocommunity.io / gogets.dev) // MITRE: T1195.002, T1608.001 // CONF: high // FP: low // COST: low // REQUIRES: DnsRequest telemetry // FALSE POSITIVES: none expected -- both domains are threat-actor-registered and provide no legitimate service // LOOKBACK: 7d (set window in the Falcon time picker, not in-query) #event_simpleName=DnsRequest | DomainName = /^(www\.)?(gocommunity\.io|gogets\.dev)$/i | table([ComputerName, UserName, DomainName, ContextBaseFileName, @timestamp]) | sort(@timestamp, order=desc)
Looks for: A process other than a known Slack/browser client resolving slack.com -- a coarse proxy for the malware's bot-token conversations.history check-in and 10-second poll loop. Falcon NG-SIEM cannot see the API path itself (see Coverage Map, Section 15), so this is DNS-level only. FP: legitimate Slack CLI tools and CI notification integrations also resolve slack.com from non-browser processes -- see the in-query TUNING line before alerting.
// HUNT: Non-Slack-Client Process Polling api.slack.com (Bot-Token Check-in Pattern) // MITRE: T1102.002, T1071.001 // CONF: medium // FP: high // COST: low // REQUIRES: DnsRequest with ContextBaseFileName process attribution // FALSE POSITIVES: legitimate Slack CLI tools, CI/CD Slack-notification integrations (PagerDuty, GitHub Actions Slack notifiers), embedded browser webviews // TUNING: exclude known Slack-integration tooling before promoting -- ContextBaseFileName=/^(slack-cli|slack-notify|pagerduty-agent|zapier-agent)(\.exe)?$/i -- and scope to developer/CI host groups only, not the whole fleet // LOOKBACK: 7d (set window in the Falcon time picker, not in-query) #event_simpleName=DnsRequest | DomainName = /^(www\.)?(api\.)?slack\.com$/i | ContextBaseFileName != /^(chrome|msedge|firefox|safari|slack|outlook|teams)(\.exe)?$/i | table([ComputerName, UserName, DomainName, ContextBaseFileName, @timestamp]) | sort(@timestamp, order=desc)
Looks for: Execution of a docker provider binary whose command line references 'kreuzwenker' -- the exact one-character typosquat of the legitimate kreuzwerker/docker provider (56M downloads) that Aikido identified as the delivery vector.
// HUNT: Typosquat Terraform Provider Execution (kreuzwenker vs. legitimate kreuzwerker) // MITRE: T1195.002, T1036 // CONF: high // FP: low // COST: low // REQUIRES: ProcessRollup2/SyntheticProcessRollup2 with CommandLine visibility // FALSE POSITIVES: none expected -- 'kreuzwenker' is not a real HashiCorp Registry namespace // LOOKBACK: 7d (set window in the Falcon time picker, not in-query) #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | FileName = /^terraform-provider-docker.*$/i | CommandLine = *kreuzwenker* | table([ComputerName, UserName, FileName, CommandLine, SHA256HashData, @timestamp]) | sort(@timestamp, order=desc)
Looks for: A file write matching the exact filename and SHA256 hash of either published payload archive (import-resource.sqlite3 from the Terraform providers, or btreex.sql from gogets.dev/btreex).
// HUNT: Known-Malicious Payload Archive Written to Disk (import-resource.sqlite3 / btreex.sql) // MITRE: T1027, T1140 // CONF: high // FP: low // COST: low // REQUIRES: PeFileWritten/ZipFileWritten/NewExecutableWritten file-write telemetry with hash // FALSE POSITIVES: none expected -- exact hash match against published campaign artifacts // LOOKBACK: 30d (set window in the Falcon time picker, not in-query) #event_simpleName=/PeFileWritten|ZipFileWritten|NewExecutableWritten/ | (FileName = /^import-resource\.sqlite3$/i or FileName = /^btreex\.sql$/i) | SHA256HashData = /^(5f892a5424e88a21a3eb3d7f82ebf04d8ac31cdb19ada25153be4165df977d0f|ab01686d87565250fc4989faddb877d793667b07ec217a61cbd798f5695d62f5)$/i | table([ComputerName, UserName, FileName, SHA256HashData, @timestamp]) | sort(@timestamp, order=desc)
Looks for: A CI/CD runner process (GitHub Actions Runner, GitLab Runner, Jenkins agent) spawning terraform or go -- the developer/CI-runner pivot the campaign specifically targets for its direct line to production cloud credentials. Use as a triage pivot alongside Q1/Q2/Q3/Q9, not a standalone alert. FP: CI jobs run terraform/go constantly -- this query is intentionally broad; see the in-query TUNING line before alerting.
// HUNT: CI/CD Runner Process Spawns Terraform or Go Toolchain (Developer/CI-Runner Pivot) // MITRE: T1195.002, T1204.002 // CONF: medium // FP: medium // COST: medium // REQUIRES: ProcessRollup2/SyntheticProcessRollup2 parent-child lineage on CI-runner hosts // FALSE POSITIVES: normal 'terraform apply' / 'go build' CI jobs that legitimately reach the public registry, module proxy, or cloud APIs // TUNING: exclude known-good CI egress (internal Terraform Cloud/Enterprise host, approved GOPROXY mirror) before alerting; scope to hosts tagged ci-runner -- this query is intentionally broad as a pivot, not a standalone detection // LOOKBACK: 7d (set window in the Falcon time picker, not in-query) #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ParentBaseFileName = /^(Runner\.Listener|Runner\.Worker|gitlab-runner|jenkins-agent|agent)(\.exe)?$/i | FileName = /^(terraform|go)(\.exe)?$/i | table([ComputerName, UserName, ParentBaseFileName, FileName, CommandLine, TargetProcessId_decimal, @timestamp]) | sort(@timestamp, order=desc)
Looks for: A behavioral hunt for the blockchain dead-drop C2 pattern Aikido documents (Arbitrum Sepolia testnet contract polled every 3 seconds) -- DNS to common public RPC provider infrastructure from a host with no documented Web3 business need. The specific RPC endpoint used by this campaign was not published, so this is a category-level behavioral signal, not a confirmed IOC. FP: Web3 developers, wallets, and unrelated tooling resolve these domains constantly -- see the in-query TUNING line; correlate with Q1/Q2/Q5/Q6 before escalating.
// HUNT: Developer/DevOps Host Resolving Public Blockchain RPC Infrastructure (Behavioral Dead-Drop C2 Hunt) // MITRE: T1102.002, T1071.001 // CONF: low // FP: high // COST: low // REQUIRES: DnsRequest telemetry // FALSE POSITIVES: legitimate Web3/blockchain developers, wallets, trading bots, or CI jobs that talk to public RPC providers for unrelated reasons -- these domains are NOT themselves Graphalgo indicators, they are a generic infrastructure category the vendor describes the malware abusing (blockchain dead-drop C2) // TUNING: broad by design -- scope to hosts with no documented Web3/blockchain business need and correlate hits with Q1/Q2/Q5/Q6 process indicators before escalating; do not run this as a standalone alert // LOOKBACK: 7d (set window in the Falcon time picker, not in-query) #event_simpleName=DnsRequest | DomainName = /^(.*\.)?(infura\.io|alchemy\.com|ankr\.com|arbitrum\.io)$/i | table([ComputerName, UserName, DomainName, ContextBaseFileName, @timestamp]) | sort(@timestamp, order=desc)
Looks for: DNS resolution to the three specific attacker-controlled Slack workspace subdomains Aikido confirmed (portfolio-devs.slack.com, portfolio-testers.slack.com, mediumstar.slack.com) -- a precise, near-zero-FP complement to Q4's broad slack.com heuristic.
// HUNT: DNS Resolution to Named Graphalgo Attacker Slack Workspaces // MITRE: T1102.002, T1071.001 // CONF: high // FP: low // COST: low // REQUIRES: DnsRequest telemetry // FALSE POSITIVES: none expected -- exact match on three specific, named attacker-controlled Slack workspace subdomains (Aikido), not a generic slack.com heuristic. Complements Q4: Q4 is a broad, high-FP catch-all for ANY non-Slack-client process resolving slack.com; this query is a narrow, near-zero-FP match on Graphalgo's OWN three confirmed workspaces // LOOKBACK: 7d (set window in the Falcon time picker, not in-query) #event_simpleName=DnsRequest | DomainName = /^(portfolio-devs|portfolio-testers|mediumstar)\.slack\.com$/i | table([ComputerName, UserName, DomainName, ContextBaseFileName, @timestamp]) | sort(@timestamp, order=desc)
CrowdStrike Custom IOA Recommendations
| Name | Pattern | Exclusions | Deployment Path |
|---|---|---|---|
| IOA — Terraform Plugin-Cache Execution | Same as Q1: FileName=terraform-provider-* under a user-writable plugin cache path | Internal provider-mirror path, if it shares the same folder naming | Custom IOA, Detect for 14 days, then Block if clean |
| IOA — Detached go run . From Terraform Provider Parent | Same as Q2: ParentBaseFileName=terraform-provider-* spawning go with 'run .' | Provider SDK developer build hosts (tag exception) | Custom IOA, Detect for 14 days, then Block if clean |
| IOA — Typosquat Provider Execution (kreuzwenker) | Same as Q5: CommandLine contains 'kreuzwenker' | None identified | Custom IOA, Detect — technically zero-FP and a strong Block candidate once your IOC-governance process independently corroborates this single-sourced indicator (Section 2) |
| Custom IOC — Known-Malicious Payload Hashes | SHA256HashData match on either published hash (Q6) | None identified | Custom IOC, Detect — single-sourced, hold at Detect per Section 4's governance note |
| Custom IOC — Fake Go Ecosystem Domains | DomainName match, Q3 domain set (gocommunity.io, gogets.dev) | None identified | Custom IOC blocklist, Detect immediately given no plausible legitimate use |
| Custom IOC — Named Graphalgo Slack Workspaces | DomainName match, Q9 domain set (portfolio-devs/portfolio-testers/mediumstar.slack.com) | None identified | Custom IOC blocklist, Detect immediately given no plausible legitimate use |
Q4, Q7, and Q8 remain investigate-only hunt queries — each carries FP risk medium or higher and is designed as a correlation pivot, not a standalone detection. Do not promote them to Custom IOA/IOC.
Machine-Readable IOC Appendix
type,value,action,severity,expiration,description,tags sha256,5f892a5424e88a21a3eb3d7f82ebf04d8ac31cdb19ada25153be4165df977d0f,detect,critical,2027-03-24,Graphalgo Terraform-provider payload archive (import-resource.sqlite3),campaign:Graphalgo sha256,ab01686d87565250fc4989faddb877d793667b07ec217a61cbd798f5695d62f5,detect,critical,2027-03-24,Graphalgo btreex.sql payload archive (gogets.dev/btreex),campaign:Graphalgo domain,gocommunity.io,detect,high,2027-03-24,Graphalgo fake Go ecosystem domain,campaign:Graphalgo domain,gogets.dev,detect,high,2027-03-24,Graphalgo fake Go ecosystem domain,campaign:Graphalgo domain,portfolio-devs.slack.com,detect,high,2027-03-24,Graphalgo attacker Slack workspace - check-in C2,campaign:Graphalgo domain,portfolio-testers.slack.com,detect,high,2027-03-24,Graphalgo attacker Slack workspace - post-handshake C2,campaign:Graphalgo domain,mediumstar.slack.com,hunt,medium,2026-12-24,Graphalgo historical attacker Slack workspace,campaign:Graphalgo
# Behavioral signatures (not directly importable as Falcon IOCs) Threat-actor public key (ed25519 SPKI, shared across Graphalgo npm/PyPI + this Go port, since April 2026): 302a300506032b656e032100bad013df6eec5d686f4cc8551e0a5c87a0135164bdd1dafb1c75141d1b526702 Terraform-provider activation-gate hash (SHA256 of containerName+networkID, used as AES key material -- NOT a file hash, cannot be hunted directly, see Section 15): b9966e3762e9a0d5d263b8cb3cca07294f81af9714d40ddf4628cb85d74e8ad5 Blockchain C2 method + fields (Arbitrum Sepolia testnet): contract 0xAD02b5cDE693529d3bdA0266299501ad0193036C method setCPubKey fields serviceData1, serviceData2 poll ~3s Slack C2 polling cadence: endpoint conversations.history (via api.slack.com) poll ~10s packet types: Start / Chunk / End (file-transfer framing) Execution behavior: detached "go run ." launched by a terraform-provider-* or Go-module parent process self-deletion capability on attacker command
# Named packages, providers, modules, and identities Terraform providers (HashiCorp Registry): gocommunity-io/dockerd (222 downloads) kreuzwenker/docker (1,449 downloads -- typosquat of kreuzwerker/docker, 56M downloads) Go modules: gocommunity.io/orderedbtree (published 2026-08-11) gogets.dev/btreex (published 2026-09-08, commits forged to Nov 2025) Malicious file path inside both providers: /internal/provider/resource_docker_container_funcs.go Threat-actor GitHub organizations / accounts: gocommunity-io (org) go-pack-tech (committed malware) gogets-dev (org) markcary3 (committed malware) kreuzwenker (committed malware) victormmpp (published dropper repo, forged commits) steveb082 (org member) go-community-admin (org member) Related but DISTINCT clusters -- named for context only, NOT Graphalgo, and NOT this pack's IOCs to ship (their own indicators belong to their own published packs -- see Section 2 for the full reasoning): GHAPPIER loader / PolinRider overlap -- via compromised npm package @dforge-core/dforge-mcp (v0.2.21); see PolinRider-Hunt.html TraderTraitor / UNC4899 / PUKCHONG / Jade Sleet -- FLATROOF + ROOFDECK (Rust, macOS), weaponized .terraform.lock.hcl; see TraderTraitor-FLATROOF-ROOFDECK-Hunt.html
# Dependency & lock-file audit one-liners # Go module references (Linux/macOS) grep -RIn "gocommunity\.io\|gogets\.dev" $(find / -name "go.sum" -o -name "go.mod" 2>/dev/null) 2>/dev/null # Terraform lock-file typosquat references (Linux/macOS) grep -RIn "kreuzwenker\|gocommunity-io" \ $(find / -name ".terraform.lock.hcl" 2>/dev/null) 2>/dev/null # Terraform provider cache inventory (Linux/macOS) find ~/.terraform.d/plugins -maxdepth 4 -iname "terraform-provider-*" 2>/dev/null # Terraform provider cache inventory (Windows PowerShell) Get-ChildItem "$env:APPDATA\terraform.d\plugins" -Recurse -Filter "terraform-provider-*.exe" -ErrorAction SilentlyContinue
Detection Validation Gates
| Gate | Check |
|---|---|
| Telemetry ready | Confirm ProcessRollup2/SyntheticProcessRollup2, DnsRequest, and PeFileWritten/ZipFileWritten/NewExecutableWritten are enabled and flowing for the developer and CI-runner host groups before relying on Q1-Q9. |
| Benign baseline | Run Q1, Q3, Q5, Q6, Q9 over a trailing 30-day window first. Expect zero hits in an environment with no exposure; any hit is a priority triage item, not noise to tune away. |
| Positive test | Detonate the published hashes/domains in an isolated lab VM against a test Falcon sensor (or replay the process-lineage pattern with a benign stand-in binary named terraform-provider-test spawning go run .) to confirm Q1/Q2/Q5/Q6 actually fire before promoting to IOA. |
| Promotion path | Q1, Q2, Q5 → Custom IOA after a 14-day false-positive-free bake in Detect mode. Q6 → Custom IOC (hash) at Detect, held per the single-source governance note in Section 4. Q3, Q9 → Custom IOC domain blocklist at Detect immediately (no plausible legitimate use). Q4, Q7, Q8 stay Investigate-only. |
Hardening — Tiered & Deployable
- Lock Terraform provider installation to a vetted source. Deploy a
provider_installationblock in the CLI configuration file (.terraformrc/terraform.rc) on every developer and CI host, restricting installation to an internal filesystem/network mirror and excluding direct registry.terraform.io access for unvetted namespaces. MITRE M1033 (Limit Software Installation) + M1051 (Update Software). Authority: HashiCorp Terraform CLI Configuration documentation (developer.hashicorp.com/terraform/cli/config/config-file) — ⚠ best-practice, no formal CIS/DISA benchmark item exists yet for Terraform CLI. See Playbook A (Section 13). - Block network egress to the confirmed C2 and typosquat infrastructure in Section 4/10 at the firewall/proxy and as Falcon Custom IOCs. MITRE M1037 (Filter Network Traffic). Authority: CIS Controls v8, Control 9 (network/DNS filtering) & Control 13 (network monitoring). Verify: attempt DNS resolution to one of the blocked domains from a test host — expect NXDOMAIN/block and an alert.
- Enforce dependency-integrity checks in CI: run
terraform init -lockfile=readonlyso any drift from.terraform.lock.hclfails the build, and keep Go's checksum database enabled (GOSUMDBmust not beoff,GOFLAGS=-mod=readonly). MITRE M1045 (Code Signing) + M1051 (Update Software). Authority: HashiCorp dependency lock-file documentation; Go module authentication docs (go.dev/ref/mod#authenticating). Verify:go env GOSUMDBreturnssum.golang.org, notoff; a modified lock file causesterraform init -lockfile=readonlyto fail.
- Deploy execution control (WDAC/AppLocker on Windows, Gatekeeper policy on macOS, AppArmor/SELinux mandatory access control on Linux) requiring known-good publishers or path allowlisting for
go,terraform, andterraform-provider-*binaries outside package-manager-managed paths. MITRE M1038 (Execution Prevention) + M1045 (Code Signing). Authority: CIS Microsoft Windows 11 Benchmark (AppLocker/WDAC sections) for Windows; CIS Apple macOS Benchmark (Gatekeeper) for macOS; CIS Distribution Benchmark (mandatory access control) for Linux. See Playbook C (Section 13). - Restrict outbound Slack API and blockchain-RPC access from developer/CI hosts to sanctioned SaaS endpoints via egress proxy allowlisting; require Slack Enterprise Grid App Manager approval for any bot token with history-read scopes. MITRE M1021 (Restrict Web-Based Content) + M1031 (Network Intrusion Prevention). Authority: CIS Controls v8 Control 9; Slack's own App Manager / approved-apps administration guidance (vendor). Verify: a non-approved outbound HTTPS connection to api.slack.com from a dev host is blocked and logged by the proxy.
- Rotate and scope down credentials on any host where a flagged package was ever installed: GitHub/GitLab tokens, npm/PyPI/Go publish tokens, cloud provider credentials, SSH keys — prioritize cloud/IaC credentials on Terraform-vector hosts. MITRE M1026 (Privileged Account Management). Authority: vendor incident-response guidance (Aikido advisory) and this pack's own Containment Runbook (Section 14). Verify: token-issuance audit log shows a rotation timestamp after the exposure window closes.
- Stand up a private Terraform provider registry / internal Go module proxy (Terraform Enterprise private registry, Artifactory, JFrog, or Athens) as the only sanctioned source, gated by a policy-as-code check (Sentinel/OPA) that blocks
terraform plan/applyagainst any non-mirrored provider source. MITRE M1033 (Limit Software Installation) + M1016 (Vulnerability Scanning). Authority: HashiCorp Terraform Enterprise / Sentinel documentation; NIST SP 800-218 (Secure Software Development Framework) practice PW.4. Verify: aterraform initreferencing registry.terraform.io directly is rejected by the policy check. - Extend software-composition-analysis / SBOM tooling to Terraform providers and Go modules, not just npm/PyPI — the vendor's own advisory notes Terraform Registry coverage is a known gap even in commercial SCA tooling as of this report. MITRE M1016 (Vulnerability Scanning). Authority: NIST SP 800-218 SSDF; CISA/NSA joint guidance on securing the software supply chain. Verify: SBOM/SCA inventory includes Terraform provider sources and their SHA256 checksums, not only language-package-manager dependencies.
- Security-awareness training covering the DPRK fake-recruiter / fake-coding-interview lure pattern for engineering and DevOps staff, with a documented, low-friction reporting channel. MITRE M1017 (User Training). Authority: general CISA/FBI/Treasury public guidance on DPRK IT-worker and developer-targeting fraud schemes — ⚠ best-practice, no formal benchmark item for this specific control. Verify: LMS completion tracking; a tabletop walkthrough conducted within the quarter.
Deployable Playbooks
Playbook A — Terraform CLI Provider-Installation Lockdown
# ~/.terraformrc (Linux/macOS) or %APPDATA%\terraform.rc (Windows)
# Restrict provider installation to an internal mirror; block direct registry.terraform.io
# fetches for anything not explicitly excluded. Verified syntax: developer.hashicorp.com/
# terraform/cli/config/config-file
provider_installation {
network_mirror {
url = "https://terraform-mirror.internal.example.com/"
}
direct {
exclude = ["registry.terraform.io/*/*"]
}
}
Prerequisites: An internal Terraform provider mirror (filesystem_mirror or network_mirror endpoint) deployed and reachable; an inventory of currently-used providers pre-loaded into the mirror so existing builds do not break on rollout.
Reboot required: No.
Rollback: Remove or comment out the provider_installation block (or restore the previous .terraformrc/terraform.rc backup); the next `terraform init` reverts immediately to default direct-registry installation.
Playbook B — Falcon Custom IOC Bulk Import
# Falcon IOC Management -- bulk import via API (requires an API client with iocs:write scope)
# The CSV payload is the grab-csv block in Section 10; convert to Falcon's JSON indicator
# schema before POSTing, or use the IOC Manager console's CSV import.
curl -s -X POST "https://api.crowdstrike.com/iocs/entities/indicators/v1" \
-H "Authorization: Bearer $FALCON_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"indicators": [
{
"type": "sha256",
"value": "5f892a5424e88a21a3eb3d7f82ebf04d8ac31cdb19ada25153be4165df977d0f",
"action": "detect",
"severity": "critical",
"platforms": ["windows", "linux", "mac"],
"description": "Graphalgo Terraform-provider payload archive (import-resource.sqlite3)"
}
]
}'
Prerequisites: Falcon IOC Management enabled for the tenant; an API client credential scoped to iocs:write, or console access with the IOC Manager role.
Reboot required: No.
Rollback: Delete the imported indicator entries via IOC Management (console) or `DELETE /iocs/entities/indicators/v1` (API); does not touch endpoint state and takes effect immediately.
Playbook C — Windows Execution-Control Rule (AppLocker, Audit-First)
# Windows -- AppLocker path-rule sketch (Audit mode first; promote to Enforce after a bake period)
# Deny execution of terraform-provider-* / go binaries from user-writable roots; allow only
# from managed installation paths.
New-AppLockerPolicy -XMLPolicy (
@"
<AppLockerPolicy Version="1">
<RuleCollection Type="Exe" EnforcementMode="AuditOnly">
<FilePathRule Id="9c4c2f4e-0001-4a3a-9c1a-000000000001" Name="Deny terraform-provider-* from user-writable roots"
Description="Graphalgo hardening - Section 13 Playbook C" UserOrGroupSid="S-1-1-0" Action="Deny">
<Conditions>
<FilePathCondition Path="%LOCALAPPDATA%\*terraform-provider-*.exe" />
</Conditions>
</FilePathRule>
<FilePathRule Id="9c4c2f4e-0001-4a3a-9c1a-000000000002" Name="Deny go.exe from Temp/Downloads"
Description="Graphalgo hardening - Section 13 Playbook C" UserOrGroupSid="S-1-1-0" Action="Deny">
<Conditions>
<FilePathCondition Path="%TEMP%\go.exe" />
</Conditions>
</FilePathRule>
</RuleCollection>
</AppLockerPolicy>
"@) | Set-AppLockerPolicy -Merge
gpupdate /force
Prerequisites: AppLocker's Application Identity service running and the GPO already in Audit mode on the target ring; a baseline capture of legitimate signed Terraform/Go binary install paths so the deny rules do not need to also cover them.
Reboot required: No reboot; the Application Identity service may need a one-time restart if this is its first AppLocker deployment on the host (`Restart-Service AppIDSvc`), and `gpupdate /force` applies the policy without a reboot.
Rollback: Remove the two FilePathRule entries (or revert the RuleCollection to AuditOnly / unlinked) and run `gpupdate /force`; no endpoint state changes are made by the policy itself, only future execution attempts are affected.
Containment Runbook
| Phase | Actions | Owner | Evidence |
|---|---|---|---|
| 1. Isolate (0-1 hr) | Network-contain the host in Falcon (Network Containment). Capture an RTR triage package (process list, network connections, recently written files) before isolation completes. Disable any Slack bot/app tokens tied to the affected workspace if it is your own tenant. Do not assume killing the visible process ends attacker reachability — per Aikido, C2 channels (Slack, blockchain) can remain reachable independent of the original package. | IR lead / SOC L2 | Falcon RTR triage export, network-containment ticket ID + timestamp |
| 2. Eradicate (1-24 hr) | Rotate every credential that touched the host: GitHub/GitLab tokens, npm/PyPI/Go publish tokens, cloud provider credentials, SSH keys — prioritize cloud/IaC credentials on Terraform-vector hosts. Remove the malicious package/provider/module from all repos and local caches. Hunt for the same file hashes/domains fleet-wide (Q3, Q6, Q9). Review recent Terraform applies, GitHub Actions runs, and package publishes made from the host's credentials during the exposure window. | IR lead + platform/DevOps engineering | Credential-rotation log, repo diff/audit export, CI run-history export |
| 3. Recover (24-72 hr) | Reimage the host — package removal alone is insufficient because the decrypted second stage runs via a detached `go run .` independent of the file on disk. Reprovision from a known-good image. Re-enable network access only after Playbook A (provider-installation lockdown) is enforced on the rebuilt host. Confirm Q1-Q9 return clean for 7 consecutive days before closing the incident. | Endpoint engineering + IR lead | Reimage ticket, 7-day clean hunt-query export, IR lead sign-off |
Detection Coverage Map
| Technique / Behavior | CQL | IOA | Coverage |
|---|---|---|---|
| Package/provider publish-side compromise (T1195.002, T1608.001) | — | — | GAP — Falcon has no visibility into the HashiCorp Registry or GitHub publish-side activity; this is a pre-endpoint supply-chain event |
| Provider/module install & execution (T1204.002) | Q1, Q5, Q7 | IOA-1, IOA-3 | Good — standard process telemetry, low FP |
| Activation-gate comparison (SHA256 of Terraform variables / crafted input) | — | — | GAP — an in-memory value comparison inside the provider/module process; not observable by EDR, and the vendor notes this is a deliberate anti-analysis design choice |
| AES decrypt + unzip + detached execution (T1027, T1140) | Q2, Q6 | IOA-2, Custom IOC (hash) | Good — process lineage and exact hash match both fire independent of the encryption |
| Slack C2 check-in and polling (T1102.002, T1071.001) | Q4 | — | Partial — DNS-level only; Falcon NG-SIEM cannot see the conversations.history API path itself, which requires a TLS-terminating proxy or Slack's own audit log to confirm |
| Blockchain dead-drop C2 (T1102.002) | Q8 | — | GAP / Partial — behavioral only, high FP; on-chain call data (serviceData1/serviceData2 contents) is opaque to EDR and requires blockchain-analytics tooling or RPC-provider logs for real confirmation |
| Typosquat provider execution (T1036) | Q5 | IOA-3 | Good — exact string match, near-zero FP |
| Named attacker Slack workspaces (T1102.002) | Q9 | Custom IOC (domain) | Good — exact domain match on 3 confirmed workspaces, near-zero FP; complements Q4's broader Partial coverage for unknown/future workspaces |
| Developer/CI-runner pivot to cloud credentials | Q7 | — | Partial — process-level pivot only; confirming actual cloud-credential misuse requires joining against CloudTrail/Azure Activity Log outside Falcon |
| Self-deletion / anti-forensics | — | — | GAP — no dedicated query; only retrospective RTR triage captured before containment (Section 14, Phase 1) recovers this evidence |
Validation gates (Section 11) apply to every row above marked Good or Partial before promotion to Prevent/Block.
Hunt Summary Ticket
TITLE: Hunt for Graphalgo Terraform/Go Supply-Chain RAT (DPRK-linked)
SEVERITY: High (targeted, low-volume, direct cloud-credential exposure risk)
SCOPE: Developer workstations (Win/Mac/Linux) running Terraform or Go; CI/CD runners
HYPOTHESIS: A developer or CI host executed a trojanized Terraform provider (gocommunity-io/dockerd,
kreuzwenker/docker) or Go module (gocommunity.io/orderedbtree, gogets.dev/btreex),
launching a Go RAT with dual Slack/blockchain C2.
QUERIES: Q1-Q9 (Section 8) -- Q1/Q2/Q5/Q6/Q9 highest confidence; Q4/Q7/Q8 investigate-only
DO FIRST: Run Q1, Q3, Q5, Q6, Q9 fleet-wide over 30d. Any hit = isolate immediately (Section 14).
FINDINGS: <populate per engagement>
GAPS: Activation-gate comparison, blockchain call-data content, and Slack API path are not
observable via Falcon (Section 15) -- do not certify "no activity" from CQL results alone.
ACTIONS: Deploy Playbook A (provider-installation lockdown) before closing out a clean sweep.
OWNER: HuntPack
VERSION: v0.2 (Draft) -- 2026-09-24
Changelog
References
| Tier | Source | Used For | Access Date |
|---|---|---|---|
| 1 | Aikido Security — Graphalgo campaign spreads to Terraform providers and Go Modules | Primary technical disclosure — package names, hashes, C2 mechanics, GitHub pivots | 2026-09-24 |
| 2 | The Hacker News — Attackers Use Malicious Terraform Providers to Deliver Go Malware via HashiCorp Registry | Corroboration of package/download-count figures; narrative context on the related GHAPPIER/PolinRider overlap (CloudSEK) -- that cluster's own IOCs are not shipped here, see PolinRider-Hunt.html | 2026-09-24 |
| 1 | ReversingLabs — Inside the 'graphalgo' fake crypto developer recruitment campaign | Original DPRK/Lazarus attribution and campaign background | 2026-09-24 |
| 1 | SentinelOne — TraderTraitor Backdoors Resurface on Victim With No Crypto Ties | Narrative context only -- Terraform Registry abuse as a broader emerging DPRK TTP; that cluster's own IOCs are not shipped here, see TraderTraitor-FLATROOF-ROOFDECK-Hunt.html | 2026-09-24 |
| ref | MITRE ATT&CK | Technique and mitigation (M-number) citations, Sections 6 & 12 | 2026-09-24 |
| ref | HashiCorp — Terraform CLI Configuration | provider_installation block syntax, Playbook A (Section 13) | 2026-09-24 |
Generated 2026-09-24 · HuntPack v0.2 (Draft) · Graphalgo Terraform & Go Supply-Chain Hunt