Go Typosquat Used One-Letter Package Mistake to Hide DNS Backdoor
A malicious Go module used a one-letter typo to impersonate the popular shopspring/decimal library and deliver a DNS-based backdoor. The fake module, github.com/shopsprint/decimal, changed only the final letter in the vendor name, replacing shopspring with shopsprint.
Socket’s Threat Research Team found that the package existed in the Go ecosystem since 2017, but the malicious code was added in version 1.3.3 on August 19, 2023. The backdoor runs when a binary importing the package starts, giving attackers a quiet command channel through DNS TXT records.
Access content across the globe at the highest speed rate.
70% of our readers choose Private Internet Access
70% of our readers choose ExpressVPN
Browse the web from multiple devices with industry-standard security protocols.
Faster dedicated servers for specific actions (currently at summer discounts)
The package targeted a high-value area of the Go ecosystem. The legitimate shopspring/decimal library handles fixed-point decimal arithmetic, which developers often use in financial software, billing systems, cryptocurrency tools, analytics platforms, and other projects that cannot tolerate floating-point rounding errors.
What happened?
The attack used typosquatting, a common supply chain tactic where a malicious package mimics the name of a trusted package. In this case, the attacker copied the public API and source body of the real decimal library closely enough that affected projects could compile and run without obvious errors.
The malicious module stayed benign for years before it was weaponized. Socket says the first seven releases did not contain the backdoor, which likely helped the package look like a maintained fork or harmless mirror.
The malicious version, 1.3.3, appeared only minutes after version 1.3.2. That timing gave the update a normal maintenance pattern while quietly adding new imports and an init function that started the backdoor loop.
Go typosquat attack at a glance
| Detail | Information |
|---|---|
| Malicious module | github.com/shopsprint/decimal |
| Legitimate module | github.com/shopspring/decimal |
| Main trick | One-letter typosquat, shopspring changed to shopsprint |
| Malicious version | v1.3.3 |
| Weaponized date | August 19, 2023 |
| Backdoor method | DNS TXT record command channel |
| C2 domain | dnslog-cdn-images[.]freemyip[.]com |
| Current proxy status | Withdrawn from the Go Module Proxy after disclosure |
How the backdoor starts
The malicious code lives in an init function. In Go, init functions run automatically when a package is initialized, before the main function starts and before exported package functions are used.
That means a developer does not need to call a suspicious function directly. If a project imports the typosquatted module and runs the resulting binary, the backdoor can start in the background.
Socket says the malicious release added three imports that do not belong in a decimal math library: net, os/exec, and time. Those imports enable DNS lookups, process execution, and repeated polling.
How the DNS-based command channel works
The payload queries the DNS TXT record for dnslog-cdn-images[.]freemyip[.]com every five minutes. TXT records normally store arbitrary text, which can include verification strings, email security records, or other configuration data.
In this attack, the attacker used the TXT response as a command source. The returned value gets passed to os/exec.Command and executed by the infected process.
The output is captured and discarded, which keeps the process quiet. The code also sleeps and retries if the DNS lookup fails, reducing visible errors that could alert developers or operators.
Why DNS makes this harder to spot
Many organizations monitor outbound HTTP and HTTPS traffic more closely than DNS traffic. Build systems, developer laptops, and production services all need DNS, so TXT lookups may not receive immediate attention.
That gives attackers a low-noise path for command delivery. They can change TXT records through the dynamic DNS provider and wait for infected binaries to poll again.
The domain used in this campaign sat under freemyip[.]com, a free dynamic DNS provider. The provider itself has legitimate uses, but Socket says its subdomains have also appeared in abusive infrastructure.
Why the Go Module Proxy mattered
The original GitHub repository and the shopsprint owner account disappeared. In many ecosystems, that might stop new downloads through normal source control paths.
Go’s module system adds a different challenge. The Go Module Proxy caches module artifacts to support reproducible builds. Socket initially found that the malicious version remained served through normal Go tooling even after the GitHub source vanished.
After coordinated disclosure, the Go security team withdrew the module from proxy.golang.org. That blocks new fetches through the public Go proxy, but teams still need to check old module caches, vendored dependencies, and previously built binaries.
Why this matters for developers
The risk is not limited to one developer typing a package path incorrectly. A bad dependency can travel through a project’s dependency tree, CI pipeline, container image, or internal module cache.
If a CI runner built or tested code with the malicious module, the backdoor ran with that runner’s permissions. That environment may hold Git tokens, cloud credentials, package registry tokens, deployment secrets, SSH keys, or signing material.
If production software imported the package, the backdoor ran with the service account permissions of that application. The blast radius depends on what the process could access.
Warning signs in affected code
- The import path github.com/shopsprint/decimal appears in go.mod, go.sum, source files, vendored code, or build logs.
- A decimal math package imports net, os/exec, or time without a clear reason.
- Build or production hosts make DNS TXT queries to dnslog-cdn-images[.]freemyip[.]com.
- Developer or CI environments contact freemyip[.]com without a business reason.
- Compiled Go binaries contain both decimal library symbols and dynamic DNS indicators.
Any confirmed use of github.com/shopsprint/decimal v1.3.3 should trigger incident response. Teams should not assume the host stayed safe just because no visible errors appeared.
What teams should check first
| Area | What to review |
|---|---|
| Source repositories | Search go.mod, go.sum, imports, vendored folders, and dependency lock data for github.com/shopsprint/decimal. |
| Developer machines | Check local Go module caches and shell history for the typosquatted path. |
| CI systems | Review build logs, dependency cache layers, runners, and stored secrets. |
| Production systems | Look for binaries built with the malicious module and review outbound DNS logs. |
| Network telemetry | Alert on TXT lookups to dnslog-cdn-images[.]freemyip[.]com and suspicious dynamic DNS domains. |
What to do if the malicious module appears
Teams should remove github.com/shopsprint/decimal and replace it with the canonical github.com/shopspring/decimal module. After that, they should run go mod tidy and rebuild from a clean environment.
If version 1.3.3 was ever built or run, the affected host should be treated as compromised. That is especially important for CI runners, deployment systems, and developer machines with access to private repositories or cloud infrastructure.
Credentials available to those hosts should be rotated. This includes GitHub tokens, cloud access keys, npm or Go registry credentials, SSH keys, Kubernetes tokens, deployment secrets, and signing keys.
Recommended response checklist
- Search all repositories for github.com/shopsprint/decimal.
- Replace the fake module with github.com/shopspring/decimal.
- Clear local and CI Go module caches where the fake module may exist.
- Rebuild affected applications from clean runners.
- Rotate credentials from any host that built or ran v1.3.3.
- Review DNS logs for dnslog-cdn-images[.]freemyip[.]com.
- Block or alert on suspicious TXT lookups from developer and CI networks.
- Add dependency review checks for typosquats and unexpected package imports.
Lessons for Go supply chain security
This case shows how a one-character mistake can become a long-running software supply chain risk. The fake module kept the expected API working, which made the package less likely to break builds or draw attention.
It also shows why dependency checks should look beyond names and version numbers. A small library that performs decimal arithmetic should not resolve DNS records or execute operating system programs.
Security teams should treat unusual imports, init-time network activity, and process execution inside simple utility libraries as high-value warning signs. These checks can catch malicious behavior even when the package name looks almost correct.
Why old builds still matter
The Go security team’s withdrawal helps stop new downloads through the public proxy, but it does not erase previous exposure. Organizations may still have the malicious module in local caches, build layers, vendored directories, internal mirrors, or compiled binaries.
That means a source cleanup alone may not be enough. Teams should identify where the package was fetched, where it was built, and whether any resulting binaries reached production.
The safest response is to combine dependency removal, credential rotation, clean rebuilds, DNS monitoring, and binary review. That approach addresses both the package and any access the attacker may have gained while it was active.
FAQ
The malicious module is github.com/shopsprint/decimal. It impersonated the legitimate github.com/shopspring/decimal library by changing one letter in the module path.
Socket identified version 1.3.3 as the malicious release. It was published on August 19, 2023 and added an init function that starts a DNS TXT-based command channel.
The backdoor queries dnslog-cdn-images.freemyip.com for TXT records every five minutes. It passes returned TXT values to os/exec.Command, allowing the attacker to execute programs available to the infected process.
Socket says the Go security team withdrew the module after coordinated disclosure. New fetches from proxy.golang.org now return a security error, but cached, vendored, or already-built copies may still exist in organizations.
Teams should replace it with github.com/shopspring/decimal, clear module caches, rebuild from clean environments, rotate credentials from affected hosts, and review DNS logs for the freemyip.com command-and-control domain.
Read our disclosure page to find out how can you help VPNCentral sustain the editorial team Read more
User forum
0 messages