The Go Proxy Says This Malware Shipped in November 2025. It Shipped in September.
On September 22, Aikido researcher Oliver Smith published an analysis of Go malware distributed through two Terraform providers and two Go modules. The packages are gocommunity-io/dockerd and kreuzwenker/docker on the HashiCorp Registry, and gocommunity.io/orderedbtree and gogets.dev/btreex as Go modules. Aikido states this is the first instance of systematic malware distribution through Terraform providers it is aware of. The malware is a Go port of Graphalgo, the campaign ReversingLabs first documented in February 2026 and which The Hacker News reports has been attributed to North Korean threat actors.
The Terraform-first framing is the one that travelled. It is genuinely new, and we will get to why the target selection matters. But it is not the part of this disclosure that will still be causing problems next year.
That part is a single sentence in Aikido's writeup about how the Go module ecosystem records time. We checked it against the primary source, and it holds.
A timestamp the ecosystem treats as fact
Aikido reports that gogets.dev/btreex was first published on 8 September 2026, and that the threat actor used forged commits in the gogets-dev/btreex repository to backdate them to November 2025. Because Go modules treat commit dates as authoritative, the module proxy and pkg.go.dev display the falsified release date.
This is checkable without trusting anyone's report, because Go's module proxy is a public, immutable, checksum-backed mirror. Querying proxy.golang.org directly at the time of writing returns six versions of gogets.dev/btreex — v1.0.0 through v1.2.3 — and the version metadata carries the fabricated history intact:
$ curl https://proxy.golang.org/gogets.dev/btreex/@v/v1.0.0.info
{"Version":"v1.0.0","Time":"2025-10-25T19:01:05Z", ... }
$ curl https://proxy.golang.org/gogets.dev/btreex/@v/v1.2.3.info
{"Version":"v1.2.3","Time":"2025-11-27T04:01:05Z", ... }
A module the researchers say first appeared on 8 September 2026 presents to every Go toolchain, every dependency dashboard, and every human reviewer as a library that has existed since late October 2025 and matured through six releases over a month. The gocommunity.io/orderedbtree module presents sixteen versions.
Both modules were still being served by the proxy when we queried it — which is expected behaviour, not a failure. The Go module proxy is deliberately immutable: once a version is fetched, it stays fetchable so that builds remain reproducible. Removal is not the mechanism. The mechanism is the vulnerability database, and a GOPRIVATE or GONOSUMDB policy that keeps unvetted vanity domains out of your build in the first place.
Why the forged date is the expensive part
Nearly every heuristic defenders apply to an unfamiliar dependency is a function of time. Package age. Release cadence. Whether a library existed before the project that depends on it. "Is this brand new?" is the first question a reviewer asks about a package name they do not recognise, and it is the question most supply-chain dashboards answer automatically.
Against a package whose git history was authored by the attacker, every one of those checks returns a reassuring answer. Ten months of apparent history, a steady version progression, no suspicious burst of releases in the week the incident started. The signal defenders rely on most is the signal the attacker controls most cheaply — forging a commit date requires setting an environment variable.
The npm registry does not have this problem in the same form: npm records server-side publish timestamps that the publisher cannot choose. Go's design decision to derive module time from VCS commit metadata is a reasonable one for a decentralised, domain-addressed ecosystem with no central registry to authoritatively stamp anything. It is also a decision that hands the attacker the clock. If your tooling treats "first seen" as "created," it is reading a field the adversary filled in.
The practical instruction is narrow and worth acting on: for Go dependencies, the date your scanner shows you is a claim, not a measurement. The date you should trust is the one your own proxy cache, artifact store, or SBOM recorded the first time you pulled it.
Inert until it isn't
The Terraform providers themselves are built to fail every dynamic test you would run against them. Aikido describes hidden entry points in internal/provider/resource_docker_container_funcs.go that activate only when the SHA-256 hash of the containerName and networkID Terraform variables, concatenated, matches one specific hardcoded value. That hash then doubles as the AES key used to decrypt the next stage — a ZIP archive shipped as examples/resources/docker_container/import-resource.sqlite3, unpacked, decrypted, and executed via a detached go run ..
Two properties follow from that design, and both are worth naming precisely.
First, it is a targeted payload. The provider is inert for anyone whose infrastructure variables do not produce the expected hash. Running it in a sandbox proves nothing. The malware is not hiding from analysis so much as declining to appear for anyone it was not sent to.
Second, the trigger doubles as the key, so there is no decryptable second stage to recover without already knowing the victim's container name and network ID. An analyst who obtains the package obtains ciphertext. This is the same shape SafeDep described from the npm side of the campaign, noting it recovered the implant but not the later code delivered through the C2 channels, and therefore could not say what tasks an operator ran on a victim.
One of the two providers, kreuzwenker/docker, is a single-character typosquat of kreuzwerker/docker, a provider The Hacker News reports at 56 million downloads. Its malicious counterpart accumulated 1,449; gocommunity-io/dockerd reached 222. Those are small numbers, and for a targeted campaign, small numbers are the design goal rather than a failure to scale.
C2 over a testnet and a Slack workspace
The second stage is a Go RAT with two command channels. Aikido documents an Ethereum smart contract on the Arbitrum Sepolia testnet at a hardcoded address, polled every three seconds, alongside a Slack bot token polled every ten. On check-in the implant reports platform, architecture, hostname, username, home directory — and, notably, whether node is present on the host, because C2 messages can instruct it to execute either Go or JavaScript.
The key handling is the part that deserves attention from anyone building detections. Each client generates an ephemeral keypair and derives a shared key against the operator's public key, so every infected host decrypts only messages addressed to it. Aikido's assessment is that this limits leakage and bottlenecks the operator: all clients consume all messages and no-op on the ones they cannot decrypt. That is a deliberate trade of throughput for operational security, and it is consistent with everything else here — a campaign optimised for staying small and staying quiet.
Aikido's victim telemetry supports that reading. Plaintext check-in messages contained 18 unique hostnames across 725 messages: ten macOS, five Linux, three Windows. Eighteen machines. The researchers note they lacked sufficient information to notify victims, and assess the operation as small and targeted.
Blockchain dead drops keep recurring in this class of malware because they solve takedown for the attacker. Aikido's recommendation is the right one to lift verbatim: organisations without a business need to interact with blockchains should consider alerting on network communication with HTTP-based blockchain services. An Arbitrum Sepolia testnet RPC call from a DevOps workstation is not a false positive waiting to happen. It is an event.
Why DevOps workstations, specifically
The campaign has been distributing through npm for months. In the same week as this disclosure, Checkmarx published analysis of indexed-btree, a related npm package that impersonated sorted-btree and reached close to two million weekly downloads before removal. Its innovation was skipping install scripts entirely: the loader lived inside BTree.prototype.set, firing when the library was used rather than when it was installed, and only when called with one specific key value. Checkmarx frames this as the predicted consequence of npm v12 blocking lifecycle scripts — the execution moved from install time to import time, which is where most scanners stop looking.
So why extend into Terraform? Aikido's reasoning is the clearest statement of target selection in the report: developer workstations are consistently high-value, but Terraform users are more likely to be involved in infrastructure deployment, so targeting DevOps workstations may give an even more direct pathway to critical production credentials.
That is the structural point. A Terraform provider is not a library that gets imported into an application and reviewed in a pull request. It is an executable binary that runs on the machine of whoever is applying infrastructure changes, at the moment they are applying them, holding whatever cloud credentials that operation requires. Provider code runs with the operator's authority by design. There is no sandbox between terraform apply and the credentials in the operator's environment, because the entire point of the provider is to use them.
This is the same authority-inheritance problem we keep finding in agent tooling from the other direction. When the MemTensor release pipeline was poisoned, the payload's value came from what the build machine could already reach. When HashiCorp's own Terraform MCP server crossed tenant and token boundaries, the failure was credential isolation, not code execution. The component that executes is rarely the interesting part; the credential context it executes inside always is.
Fake ecosystems as social engineering
One detail separates this from routine typosquatting. The threat actor registered two domains — gogets[.]dev and gocommunity[.]io — each within a day of creating the corresponding GitHub organisation, and stood up websites presenting them as Go package ecosystems offering vanity import paths. Aikido reports that neither provides any mechanism for external users to add packages and both appear non-functional.
They are not ecosystems. They are context. Go's vanity import paths mean a module can be named after any domain its author controls, and a developer evaluating gogets.dev/btreex who visits gogets.dev finds something that looks like a community project rather than an empty parking page. Combined with the backdated commit history, the package arrives with a plausible home, a plausible age, and a plausible release cadence — three independent legitimacy signals, all of them manufactured, none of them expensive.
Aikido's read is that the dedicated infrastructure suggests continued intent to publish malware targeting the Go supply chain. Building a fake ecosystem is not a one-package investment.
The delivery route remains social. Per The Hacker News' account of Graphalgo, developers are approached on LinkedIn and Facebook or through job postings by operators posing as non-existent Web3 companies, then asked to complete a coding task using a benign-looking GitHub repository that pulls the malicious dependency. The package never has to be discovered organically. It only has to survive the thirty seconds a candidate spends glancing at a dependency before running the take-home exercise they were told would be evaluated.
What to do
- Search your state and lockfiles for the four packages by name. Terraform providers
gocommunity-io/dockerdandkreuzwenker/docker(note the-wen-spelling versus the legitimatekreuzwerker/docker); Go modulesgocommunity.io/orderedbtreeandgogets.dev/btreex. All versions are affected. - Do not rely on package age or publish date for Go dependencies. The proxy reports what the commit says. Anchor on the first date your own cache, artifact repository, or SBOM observed the module — that timestamp the attacker does not control.
- Treat provider execution as credentialed execution. Run
terraform applyfrom CI with scoped, short-lived credentials rather than from workstations holding long-lived cloud keys. Pin providers by version and checksum in the lock file, and review lock-file changes as seriously as code changes. - Alert on blockchain RPC traffic from build and DevOps hosts. If nothing in your estate legitimately talks to Ethereum testnet endpoints, that traffic is a high-fidelity signal with near-zero background noise.
- Assume install-script controls are no longer the relevant boundary. The
indexed-btreetechnique executes on library use; the Terraform payload executes on matching input. Neither trips install-time analysis. If your supply-chain tooling only inspects lifecycle hooks, it is covering the technique attackers moved away from. - Extend name-similarity checks past npm and PyPI. Terraform providers, Go modules, and container images all resolve by name, and the registries differ in what they verify. The slopsquatting problem gets worse here, because an AI assistant suggesting a plausible-sounding Go import has no registry-level trust signal to be wrong about.
- If a developer ran an unvetted "coding assessment" repository, treat the workstation as suspect. That is the documented entry point for this campaign, and the targeted trigger means absence of observed malicious behaviour is not evidence of absence.
The Terraform first is real and will be the headline. The durable finding is the one that survives this campaign entirely: in an ecosystem that derives package age from commit metadata, package age is an attacker-supplied field. Every defence built on "this library has been around a while" is reading a number someone else wrote. The Go proxy will keep serving btreex with its November 2025 date because immutability is the feature, and that date will keep being wrong.
Sources:
- Aikido — “Graphalgo campaign spreads to Terraform providers and Go Modules,” Oliver Smith (published 22 September 2026, updated 24 September)
- Go module proxy — version list and
.infometadata forgogets.dev/btreex(backdated timestamps verified directly) - The Hacker News — “Attackers Use Malicious Terraform Providers to Deliver Go Malware via HashiCorp Registry” (23 September 2026, download counts and DPRK attribution)
- Checkmarx Zero — “npm ‘btree’ Malware Campaign Affects Millions of Downloads, No Need for Install Script,” Bruno Dias (17 September 2026)
- ReversingLabs — Graphalgo campaign analysis (cited by Aikido for shared infrastructure and public key)
- HashiCorp — Terraform Registry documentation