Explore top 10 tips to secure your open-source projects now. Read More
×
Every Linux developer who works with Go has run the same workflow a thousand times. You find a library that solves your problem, you see a decent star count on GitHub, and you run go get. It is frictionless and efficient. Lately, however, it is becoming one of the most effective ways for an attacker to get code running on your build servers.
The recent "Operation Muck and Load" campaign is a perfect example of why this workflow is risky. Researchers uncovered over 200 GitHub repositories distributing malicious Go modules. These were not exploiting a vulnerability in the Go compiler or a bug in a specific package. They were exploiting the assumption that anything hosted on GitHub deserves the benefit of the doubt.
The attack starts long before any malware is downloaded. Attackers first publish a Go module that looks like something you would genuinely use. The repository has a believable name, a polished README, and enough commit history to suggest an active project. Much of that activity is manufactured through commit farming, giving the impression that multiple developers have been maintaining the code over time.
Once a developer imports the module, the real attack begins. Hidden inside the package is obfuscated code that launches PowerShell rather than simply performing its advertised function. Instead of connecting directly to a command-and-control server—which would be easy for security teams to block via firewall rules—the script first checks a public "dead drop" page that stores the current server location. This technique, classified by MITRE as a Dead Drop Resolver, lets the attackers change infrastructure whenever they want without modifying the malware itself. Only after resolving that address does the downloader retrieve the final payload, such as a Remote Access Trojan.
Supply chain attacks are difficult to detect because nothing initially looks broken. The build succeeds, the application runs, and developers move on. By the time suspicious behavior appears, the malicious dependency may already be embedded across multiple projects.
It is easy to look at a Windows-based RAT and assume your Linux servers are safe. Do not be that confident. 
Go provides several tools to help you keep an eye on your dependencies. If you are not using them, you are flying blind.
The go mod verify command checks that the dependencies in your local cache have not been modified since they were downloaded. If a local file has been tampered with, this command will immediately flag it.
Before you add a module, see what it’s actually pulling in. Use go list -m all to get a full tree of your project's dependencies. If you are importing a simple logging tool and it suddenly pulls in 50 sub-dependencies you’ve never heard of, that is a massive red flag.
In enterprise environments, do not just rely on public proxies. Consider using an internal GOPROXY or an artifact repository that caches and scans modules. This gives you a single choke point where you can enforce security policies and conduct vulnerability scans before code ever reaches your build pipeline.
If your build process is suddenly reaching out to the internet to fetch "resolver" content from random public websites, your security team needs to know. Monitor your CI/CD runners for unexpected outbound network traffic during the go build phase. Legitimate builds should talk to known proxies or git servers, not random public dead drops.
Go's module ecosystem is not broken, but blind trust is. The repositories in Operation Muck and Load did not exploit a flaw in the language; they exploited the assumption that anything hosted on GitHub deserves the benefit of the doubt.
Before adding a new dependency, spend a minute looking at who maintains it, how the project history has evolved, and what your build process is actually downloading. Verify your hashes, pin your versions, and keep an eye on your outbound traffic. That is often enough to avoid becoming the next victim of a supply chain attack.