# DPRK-Linked Graphalgo Campaign Abuses HashiCorp Terraform Registry with Malicious Providers and Go Modules to Deliver Go RAT with Slack and Arbitrum Sepolia Blockchain C2

> North Korea-linked operators behind the Graphalgo fake-recruiter campaign published two malicious Terraform providers (gocommunity-io/dockerd and the kreuzwerker typosquat kreuzwenker/docker) and two Go modules (gocommunity.io/orderedbtree, gogets.dev/btreex) that drop an environmentally keyed Go remote access trojan, the first documented use of the HashiCorp Terraform Registry as a malware distribution vector. The RAT takes encrypted commands from a smart contract on the Arbitrum Sepolia testnet and from Slack workspaces, and shares its encryption key, infrastructure and tradecraft with a parallel npm wave (mathmain, mathsbase, math-universe and others) and with the PolinRider/NullReceiver cluster.

- **Published:** 2026-09-23T00:00:00Z
- **Last reviewed:** 2026-09-30T13:04:14.942Z
- **Canonical:** https://intel.threadlinqs.com/threat/TL-2026-2635
- **Card image:** https://intel.threadlinqs.com/og/threat/threat-intel-lazarus-graphalgo-terraform-attack-tl-2026-2635.webp?v=a60c4caf (Threadlinqs threat intel: Lazarus Group Graphalgo Terraform Malware: Detection & IOCs (TL-2026-2635) — High severity, 41 IOCs, 9 detection rules.)
- **ID:** TL-2026-2635
- **Severity:** HIGH
- **Category:** SUPPLY_CHAIN
- **Status:** ACTIVE
- **Actor:** Lazarus Group (North Korea)
- **Detections:** 9 (1 shown free below) · **IOCs:** 41 (3 shown free below)

## Description

Aikido Security (Oliver Smith, 22 September 2026) reported that the Graphalgo campaign, first documented by ReversingLabs in February 2026 as a Lazarus Group fake-recruiter operation targeting cryptocurrency and Web3 developers, has spread from npm and PyPI to the HashiCorp Terraform Registry and the Go module ecosystem. Two Terraform providers were published: gocommunity-io/dockerd (222 downloads) and kreuzwenker/docker (1,449 downloads). The second typosquats the legitimate kreuzwerker/docker provider, which has about 56 million downloads. Two Go modules were also published: gocommunity.io/orderedbtree (11 August 2026) and gogets.dev/btreex (8 September 2026). According to Aikido and The Hacker News, this is the first known use of Terraform providers to distribute malware.

Delivery chain: the providers hide an entry point in /internal/provider/resource_docker_container_funcs.go. It stays dormant unless the SHA-256 hash of the concatenated Terraform variables containerName and networkID equals b9966e3762e9a0d5d263b8cb3cca07294f81af9714d40ddf4628cb85d74e8ad5. That hash is the AES key used to decrypt embedded file paths, so the packages only work for the victims they were built for. This is environmental keying, and the researchers read it as a targeted attack rather than mass compromise. Once triggered, the code unpacks an archive disguised as a SQLite file (examples/resources/docker_container/import-resource.sqlite3 in the providers; btreex.sql in the btreex module). It AES-decrypts each file in the archive and runs the result as a Go package with a detached `go run .` command, so the process outlives the Terraform or Go tooling that started it. To make the packages look mature and trustworthy, the operators stood up two fake Go vanity-import ecosystems, gocommunity.io and gogets.dev, each registered within one day of its GitHub organization. The btreex module also carries forged commits backdated to November 2025. GitHub accounts involved: gocommunity-io, go-pack-tech, kreuzwenker, victormmpp (forged commits), gogets-dev, markcary3, steveb082 and go-community-admin.

Second-stage RAT: the Go implant generates an ephemeral X25519 key pair and derives shared keys by combining it with two threat actor public keys. One of these (302a300506032b656e032100bad013df6eec5d686f4cc8551e0a5c87a0135164bdd1dafb1c75141d1b526702) is shared with the Graphalgo npm malware and first appeared in the npm package modern-events in April 2026. The RAT runs two command channels at once. (1) Slack bot C2: it polls the Slack conversations.history API every 10 seconds, checking in on channel frontend-devs in portfolio-devs.slack.com and reading commands from qa-announcements in portfolio-testers.slack.com; mediumstar.slack.com was used earlier. (2) A blockchain dead drop: every 3 seconds it reads the Arbitrum Sepolia testnet contract 0xAD02b5cDE693529d3bdA0266299501ad0193036C through the setCPubKey method and the serviceData1/serviceData2 fields. Each decrypted C2 message has a header that tells the implant to run the body as more Go code, run it as JavaScript through node, or delete itself. Files arrive in start, chunk and end packets that are reassembled on the host. The implant sends reconnaissance to Slack: platform, architecture, hostname, username, home directory, hardware attributes, whether Node.js is available, and a timestamp. Aikido counted 1,240 encrypted Slack messages and 1,402 contract transactions since testing began on 16 July 2026 (a Windows host named "Frank"). Across 725 messages it found 18 unique victim hostnames: 3 Windows, 5 Linux and 10 macOS.

Related npm wave and cluster overlap: in parallel, JFrog, SafeDep, Checkmarx and Socket flagged npm packages built the same way, including indexed-btree, mathsbase, mathmain, math-universe, modern-events, quick-events, crypto-hasher, events-router, sort-btree, graphcore-js and graphlib-js. SafeDep counted 24 packages and 60+ malicious versions between March and September 2026, with download counts inflated by a farm of GitHub Actions workers. In that wave the loader only decrypts when the caller passes the Pascal matrix [[1,1,1],[1,2,3],[1,3,6]] to lusolve(). The JSON-serialized lower-triangular LU factor becomes the scrypt password for AES-256-GCM decryption. The decrypted stage spawns detached Node.js children, polls Slack every 10 seconds, and reads Base Sepolia and Ethereum Sepolia contracts (0xac0bfC4C48A679b667732128278EACBA1c191894, 0xE390863Dac96a7118C71227C2b099B50cF602D31) through Infura and Alchemy RPC endpoints. A second payload uses the NullReceiver technique to resolve its C2 address, 193.247.144.38, from an attacker wallet, and ends with the byte sequence 68656c6c6f6970626f742121 ("helloipbot!!"). Both are shared with the long-running DPRK-linked PolinRider campaign; Socket lists 193.247.144.38 as PolinRider C2. The Hacker News also cites overlaps with Contagious Interview, a CloudSEK-reported GHAPPIER npm loader (version 0.2.21, live for 35 minutes 38 seconds on 9 September 2026) that uses the same staging host and Vercel domain request, and a SentinelOne report on TraderTraitor using weaponized Terraform lock files and custom provider registries to deliver Rust backdoors.

The initial lure follows Graphalgo's established pattern. Developers are approached on LinkedIn, Facebook or Reddit by fake Web3 companies (for example Veltrix Capital) and given a benign-looking coding task whose dependency pulls in the malicious package. On 17 September 2026 the Rust Foundation warned that these actors set up legitimate-looking company profiles with plausible LinkedIn presences. Remediation guidance from Aikido: isolate affected hosts, because C2 persists over the Slack API and the smart contract; rotate all Go, GitHub/GitLab, npm/PyPI publish, cloud and SSH credentials; audit activity during the exposure window; and reimage, because the detached `go run .` process survives removal of the package.

## MITRE ATT&CK

- T1583.001 Acquire Infrastructure: Domains
- T1583.006 Acquire Infrastructure: Web Services
- T1585 Establish Accounts
- T1608.001 Stage Capabilities: Upload Malware
- T1195.002 Supply Chain Compromise: Compromise Software Supply Chain
- T1566.003 Phishing: Spearphishing via Service
- T1204.005 User Execution: Malicious Library
- T1059.007 Command and Scripting Interpreter: JavaScript
- T1036.005 Masquerading: Match Legitimate Resource Name or Location
- T1036.008 Masquerading: Masquerade File Type
- T1480.001 Execution Guardrails: Environmental Keying
- T1027.013 Obfuscated Files or Information: Encrypted/Encoded File
- T1140 Deobfuscate/Decode Files or Information
- T1070.004 Indicator Removal: File Deletion
- T1082 System Information Discovery
- T1033 System Owner/User Discovery
- T1102.002 Web Service: Bidirectional Communication
- T1102.001 Web Service: Dead Drop Resolver
- T1573.002 Encrypted Channel: Asymmetric Cryptography
- T1195.001 Supply Chain Compromise: Compromise Software Dependencies and Development Tools
- T1105 Ingress Tool Transfer

## Free detection (Splunk SPL, Microsoft KQL, Sigma)

**Graphalgo Malicious Terraform Provider / Go Module Retrieval (kreuzwenker/docker, gocommunity-io/dockerd, orderedbtree, btreex) — Exploitation** (`TL-2026-2635-SPL-001`)

Detects a developer workstation or CI runner resolving, downloading or referencing the DPRK-linked (Lazarus / Graphalgo) supply-chain artifacts. Coverage: the Terraform Registry providers kreuzwenker/docker (a typosquat of kreuzwerker/docker, 1,449 downloads) and gocommunity-io/dockerd; the Go modules gocommunity.io/orderedbtree and gogets.dev/btreex, whether pulled through GOPROXY (proxy.golang.org) or resolved directly against the attacker-run vanity-import domains gocommunity.io and gogets.dev; and the actor-controlled GitHub organisations and accounts that host the release binaries. This is the earliest point in the kill chain you can observe (terraform init / go get / go mod download), before the environmentally keyed loader fires. The packages are only malicious, so any hit means a hostile dependency has entered the build graph and the host should be triaged even if the keyed payload never decrypted.

- **Languages:** Splunk SPL, Microsoft KQL, Sigma
- **ATT&CK:** T1195.002, T1204.005, T1036.005, T1583.001, T1608.001
- **Data sources:** web_proxy (zscalernss-web / bluecoat:proxysg:access:syslog / squid:access), stream:dns, sysmon (EventCode 1, 22), sysmon:linux, DeviceNetworkEvents, DeviceProcessEvents, DnsEvents
- **Severity:** high
- **Confidence:** high

### Splunk SPL

````spl
(index=proxy sourcetype IN ("zscalernss-web","bluecoat:proxysg:access:syslog","squid:access") (url="*registry.terraform.io/v1/providers/kreuzwenker/docker*" OR url="*registry.terraform.io/v1/providers/gocommunity-io/dockerd*" OR url="*proxy.golang.org/gocommunity.io/orderedbtree*" OR url="*proxy.golang.org/gogets.dev/btreex*" OR url="*gocommunity.io/*" OR url="*gogets.dev/*" OR url="*github.com/kreuzwenker/*" OR url="*github.com/gocommunity-io/*" OR url="*github.com/gogets-dev/*"))
OR (index=dns sourcetype="stream:dns" (query="gocommunity.io" OR query="*.gocommunity.io" OR query="gogets.dev" OR query="*.gogets.dev"))
OR (index=sysmon sourcetype IN ("XmlWinEventLog:Microsoft-Windows-Sysmon/Operational","sysmon:linux") ((EventCode=1 (CommandLine="*kreuzwenker/docker*" OR CommandLine="*gocommunity-io/dockerd*" OR CommandLine="*gocommunity.io/orderedbtree*" OR CommandLine="*gogets.dev/btreex*")) OR (EventCode=22 (QueryName="gocommunity.io" OR QueryName="*.gocommunity.io" OR QueryName="gogets.dev" OR QueryName="*.gogets.dev"))))
``` Graphalgo supply-chain retrieval: Terraform Registry typosquat + attacker Go vanity-import ecosystems ```
| eval evidence=coalesce(url, query, QueryName, CommandLine)
| eval artifact=case(match(evidence,"(?i)kreuzwenker"),"kreuzwenker/docker (typosquat of kreuzwerker/docker)", match(evidence,"(?i)gocommunity"),"gocommunity-io/dockerd or gocommunity.io/orderedbtree", match(evidence,"(?i)gogets"),"gogets.dev/btreex", true(),"other Graphalgo reference")
| eval endpoint=coalesce(src, src_ip, Computer, host)
| stats count as events min(_time) as first_seen max(_time) as last_seen values(evidence) as evidence values(sourcetype) as sourcetypes values(user) as users values(Image) as processes by endpoint artifact
| convert ctime(first_seen) ctime(last_seen)
| sort - events
````

### Microsoft KQL

```kql
let lookback = 14d;
let badDomains = dynamic(["gocommunity.io","gogets.dev"]);
// Graphalgo Terraform providers, Go modules and actor GitHub orgs/accounts
let refRegex = @"(?i)(kreuzwenker/docker|gocommunity-io/dockerd|gocommunity[.]io/orderedbtree|gogets[.]dev/btreex|github[.]com/(kreuzwenker|gocommunity-io|gogets-dev|go-pack-tech|markcary3|steveb082|go-community-admin|victormmpp))";
union
(DeviceNetworkEvents
 | where Timestamp > ago(lookback)
 | where RemoteUrl has_any (badDomains) or RemoteUrl matches regex refRegex
 | project Timestamp, Device=DeviceName, Account=InitiatingProcessAccountName, Evidence=RemoteUrl, Process=InitiatingProcessFileName, CommandLine=InitiatingProcessCommandLine, Source="DeviceNetworkEvents"),
(DeviceProcessEvents
 | where Timestamp > ago(lookback)
 | where ProcessCommandLine matches regex refRegex or ProcessCommandLine has_any (badDomains)
 | project Timestamp, Device=DeviceName, Account=AccountName, Evidence=ProcessCommandLine, Process=FileName, CommandLine=ProcessCommandLine, Source="DeviceProcessEvents"),
(DnsEvents
 | where TimeGenerated > ago(lookback)
 | where Name has_any (badDomains)
 | project Timestamp=TimeGenerated, Device=Computer, Account="", Evidence=Name, Process="", CommandLine=ClientIP, Source="DnsEvents")
| extend Artifact = case(Evidence contains "kreuzwenker", "kreuzwenker/docker (typosquat of kreuzwerker/docker)", Evidence contains "gocommunity", "gocommunity-io/dockerd or gocommunity.io/orderedbtree", Evidence contains "gogets", "gogets.dev/btreex", "Graphalgo actor GitHub account")
| summarize Events=count(), FirstSeen=min(Timestamp), LastSeen=max(Timestamp), EvidenceSet=make_set(Evidence, 20), Processes=make_set(Process, 10), Sources=make_set(Source) by Device, Account, Artifact
| order by Events desc
```

### Sigma

```yaml
title: Graphalgo Malicious Terraform Provider or Go Module Retrieval
id: 3f9c2a61-7d4e-4b8a-9e15-6c2d8f0a41b7
status: experimental
description: 'Detects HTTP requests to the Terraform Registry, the Go module proxy, GitHub release hosting or the attacker vanity-import domains for the DPRK-linked Graphalgo packages kreuzwenker/docker (typosquat of kreuzwerker/docker), gocommunity-io/dockerd, gocommunity.io/orderedbtree and gogets.dev/btreex.'
references:
  - https://www.aikido.dev/blog/graphalgo-terraform-go-modules
  - https://thehackernews.com/2026/09/attackers-use-malicious-terraform.html
author: Threadlinqs Team
date: 2026-09-23
tags:
  - attack.initial-access
  - attack.t1195.002
  - attack.execution
  - attack.t1204.005
  - attack.stealth
  - attack.t1036.005
  - attack.resource-development
  - attack.t1583.001
  - attack.t1608.001
logsource:
  category: proxy
detection:
  selection_registry_path:
    c-uri|contains:
      - '/v1/providers/kreuzwenker/docker'
      - '/v1/providers/gocommunity-io/dockerd'
      - '/gocommunity.io/orderedbtree/@v/'
      - '/gogets.dev/btreex/@v/'
      - '/kreuzwenker/terraform-provider-docker'
      - '/gocommunity-io/terraform-provider-dockerd'
  selection_vanity_domain:
    cs-host|endswith:
      - 'gocommunity.io'
      - 'gogets.dev'
  condition: 1 of selection_*
falsepositives:
  - Threat-intel or malware-analysis staff deliberately retrieving the packages
  - SCA, registry-mirror or dependency-scanning crawlers indexing newly published providers
level: high
```

**Known false positives:**

- Threat-intel, red-team or malware-analysis staff intentionally pulling the packages into an analysis sandbox
- Software-composition-analysis or registry-mirror crawlers that enumerate newly published Terraform providers and Go modules
- Engineers who mistype the legitimate kreuzwerker/docker provider are true positives, not false positives: the typosquat itself is the attack

**Tuning notes:** Run this across proxy, DNS and endpoint telemetry for at least 90 days back, because the Go modules have been live since 2026-08-11. Proxy URL matching needs TLS inspection for registry.terraform.io and proxy.golang.org. Without it, rely on the DNS (gocommunity.io / gogets.dev) and command-line branches. Go honours GOPROXY/GOPRIVATE, so if you use an internal Go proxy (Athens, Artifactory, Nexus), add its access log index and match the same module paths. Also sweep source control offline: grep .terraform.lock.hcl for registry.terraform.io/kreuzwenker/docker or gocommunity-io/dockerd, and grep go.mod/go.sum for gocommunity.io/ and gogets.dev/. Suppress only named analysis hosts. Do not suppress on user.

### 8 more detection rules (Splunk SPL, Microsoft KQL, Sigma), unlock with TL Creds

- Graphalgo Encrypted Payload Archive Masquerading as SQLite/SQL File in Terraform Plugin or Go Module Cache — Exploitation (locked: unlock with TL Creds)
- Graphalgo npm Encrypted Loader ([indicator] / [indicator] / math-universe family) Installed with Detached Node Child Spawn — Post-Exploitation (locked: unlock with TL Creds)
- Blockchain Testnet RPC Dead-Drop Polling (Arbitrum / Base / Ethereum Sepolia) or Graphalgo C2 Contract Reference from Non-Browser Process — C2 (locked: unlock with TL Creds)
- Terraform Provider Plugin or Go Program Spawning Detached '[indicator]' — Graphalgo Environmentally Keyed Loader Execution (locked: unlock with TL Creds)
- Terraform Provider or go-build Binary Writing and Deleting Go Source Files — Graphalgo Payload Decryption Staging and Self-Deletion (Post-Exploitation) (locked: unlock with TL Creds)
- PolinRider / NullReceiver C2 ([indicator]) and Community-Correlated DPRK Infrastructure Contact — C2 (locked: unlock with TL Creds)
- Ephemeral go-build Binary Spawning node, go or Recon Utilities — Graphalgo RAT JavaScript/Go Tasking (Post-Exploitation) (locked: unlock with TL Creds)
- Slack API C2 Polling (conversations.history ~10s cadence) from Non-Browser Client or Graphalgo Workspaces — C2 (locked: unlock with TL Creds)

Sign up free to unlock the detection rules: https://threadlinqs.com/?modal=signup

## Sample indicators (3 of 41)

Defanged, TLP:CLEAR.

- IP address: `193.247.144[.]38`
- Domain: `gocommunity[.]io`
- File hash: `5f892a5424e88a21a3eb3d7f82ebf04d8ac31cdb19ada25153be4165df977d0f`

Full set of 41 indicators unlocks with TL Creds: https://threadlinqs.com/tl-creds

## Sources

- [Graphalgo Malware Spreads to Terraform and Go](https://www.aikido.dev/blog/graphalgo-terraform-go-modules)
- [Attackers Use Malicious Terraform Providers to Deliver Go Malware via HashiCorp Registry](https://thehackernews.com/2026/09/attackers-use-malicious-terraform.html)
- [Why Does an npm Math Library Need an Encrypted Loader?](https://safedep.io/mathmain-encrypted-loader/)
- [PolinRider Spreads Through Compromised GitHub Accounts and Packagist](https://socket.dev/blog/polinrider-github-packagist)
- [Fake recruiter campaign targets crypto developers with RAT (Graphalgo)](https://www.reversinglabs.com/blog/fake-recruiter-campaign-crypto-devs)
- [Graphalgo Malware Uses Malicious Terraform Providers and Go Modules to Deploy RAT](https://gbhackers.com/graphalgo-malware/)
- [Graphalgo Terraform Malware Targets Cloud Credentials](https://www.purpleshieldsecurity.com/post/graphalgo-terraform-provider-malware)
- [Graphalgo Malware Hits Terraform and Go Packages in New Supply Chain Attack](https://cyberpress.org/graphalgo-malware-poisons-terraform-go/)

## Full data

The other 8 detection rules (Splunk SPL / Microsoft KQL / Sigma) unlock with TL Creds; a free account includes daily creds: https://threadlinqs.com/?modal=signup
Indicator values unlock with TL Creds, at a lower price on Red and Purple; AI agents can query rules and IOCs through the Threadlinqs MCP server (Purple): https://intel.threadlinqs.com/mcp

Canonical: https://intel.threadlinqs.com/threat/TL-2026-2635
