PolinRider Supply-Chain Campaign Spreads Through Go and Packagist Repositories

Summary
Researchers say the DPRK-linked PolinRider campaign used compromised GitHub accounts to distribute malicious code through Go modules and Packagist, where repository access can enable releases. They describe its techniques and recommend auditing repositories and securing
Key points
- OpenSourceMalware and Socket researchers report nearly 200 malicious release artifacts tied to 111 packages and extensions across Go, Packagist, npm, and Chrome; they found more than 80 affected Go modules.
- Compromised GitHub accounts let attackers push malicious commits and tags. Go modules and Packagist packages can resolve directly from repositories, so those changes can flow to users without separate registry publishing credentials.
- The campaign hides obfuscated JavaScript in configuration files and fake font files, and uses VS Code tasks triggered when a folder is opened.
- Researchers describe loader code retrieving a second stage through blockchain dead-drops and delivering Lazarus-linked tools associated with credential, browser-data, and wallet theft.
- The researchers found compromised npm and PyPI maintainer repositories but no corresponding malicious releases in those registries in the cases discussed; they say separate publishing credentials can block this route.
- History rewriting can obscure tampering in visible commit logs. The researchers recommend checking repository activity and release metadata, enabling hardware-backed MFA, revoking stale tokens, and investigating suspicious files and tags.
- For suspected compromise, the article recommends rotating secrets from a clean host and rebuilding from a known-good lockfile.
Article Details
- Attack Vectors
- The campaign takes over legitimate GitHub maintainer accounts and injects obfuscated JavaScript into repository configuration files, fake .woff2 font files, and .vscode/tasks.json.
- For repo-sourced Go modules and Packagist packages, malicious commits or tags are distributed through normal package installation flows without a separate registry publishing credential.
- A folder-open VS Code task runs a shell command using curl, while malicious npm dependencies can use postinstall hooks to inject the configuration-file payload.
- A Windows batch script rewrites Git history and force-pushes modified commits to conceal repository tampering.
- Defensive Notes
- Audit Go tags and Composer releases against unexpected repository modifications; inspect repository activity logs, release metadata, and configuration files rather than relying on rendered Git history.
- Require hardware-backed MFA for GitHub accounts, revoke stale tokens, and review whether abandoned domains could be used for account recovery.
- Check configuration files for JavaScript appended after export default or module.exports, inspect .woff2 files for JavaScript, and look for runOn: folderOpen in .vscode/tasks.json and temp_auto_push.bat.
- If indicators are found, treat the machine as compromised: rotate secrets from a clean host, rebuild from a known-good lockfile, and restore verified-clean history with commit signing enabled.
Indicators of compromise
| Type | Indicator | Context |
|---|---|---|
| DOMAIN | proxy[.]golang[.]org | Go module proxy involved in serving malicious versions from compromised repositories; the article does not identify it as attacker-controlled. |
| URL | hxxps[:]//opensourcemalware[.]com/?search=%23polinrider | Researcher-provided link to search for campaign-related accounts and packages; not malicious infrastructure. |
MITRE ATT&CK
T1027 · Obfuscated Files or InformationThe JavaScript payload is obfuscated and padded with whitespace; the loader decrypts its second stage using embedded XOR keys.T1041 · Exfiltration Over C2 ChannelThe reported implant performs wallet exfiltration.T1057 · Process DiscoveryThe Beavertail loader fingerprints the host operating system before selecting an implant.T1059.003 · Windows Command Shelltemp_auto_push.bat is used to rewrite Git history and force-push modified commits.T1059.004 · Unix ShellThe .vscode/tasks.json folder-open trigger runs a shell command through curl-piped bash.T1059.007 · JavaScriptObfuscated JavaScript loaders are embedded in repositories and executed through Node.js.T1078 · Valid AccountsThe campaign takes over legitimate GitHub maintainer accounts and uses their repository access to alter code.T1102 · Web ServiceThe loader retrieves second-stage payloads from blockchain dead-drops on TRON, Aptos, and BNB Smart Chain.T1140 · Deobfuscate/Decode Files or InformationThe loader decrypts its second stage using embedded XOR keys.T1195.002 · Compromise Software Supply ChainPolinRider compromises legitimate maintainer repositories and injects payloads into packages distributed through Go and Packagist.T1546.015 · Component Object Model HijackingA VS Code task configured with runOn: folderOpen triggers execution when a victim opens the project.T1555 · Credentials from Password StoresThe reported InvisibleFerret implant steals credentials and browser data.T1620 · Reflective Code LoadingThe loader executes the decrypted second-stage code through eval().
Threat Actors
Malware
BeaverTailIn that woff2 file is the JavaScript loader that bootstraps the first stage of a DPRK Beavertail or Ottercookie attack chain.DEV#POPPER RATloader that fingerprints the host OS and selects an InvisibleFerret implant (also reported as DEV#POPPER RAT and OmniStealer) for credential theft, browser-data theft, wallet exfiltration, and socket.io-basedInvisibleFerretis the familiar Lazarus toolkit: a Beavertail loader that fingerprints the host OS and selects an InvisibleFerret implant (also reported as DEV#POPPER RAT and OmniStealer) for credential theft, browser-data theft,OmniStealerfingerprints the host OS and selects an InvisibleFerret implant (also reported as DEV#POPPER RAT and OmniStealer) for credential theft, browser-data theft, wallet exfiltration, and socket.io-based C2. And on the repo
Products
AptosThe loader still resolves its second stage from blockchain dead-drops across TRON, Aptos, and BNB Smart Chain, decrypts it with embedded XOR keys, and runs it through eval(). The unpacked chain is the familiar LazarusBNB Smart ChainThe loader still resolves its second stage from blockchain dead-drops across TRON, Aptos, and BNB Smart Chain, decrypts it with embedded XOR keys, and runs it through eval(). The unpacked chain is the familiar LazarusComposerA GitHub webhook tells Packagist to re-read the repo whenever you push, and composer require downloads the code as a zipball of the tagged commit straight from GitHub.GitHubaccompanied by typosquatting or publishing throwaway malicious packages, PolinRider takes over legitimate GitHub accounts and quietly injects an obfuscated JavaScript loader into repositories the maintainer alreadyGoSocket teams observed PolinRider publishing payloads to multiple package registries including Packagist and Go. Almost 200 malicious release artifacts tied to 111 unique packages and extensions: more than 80 Gonpmtied to 111 unique packages and extensions: more than 80 Go modules, 10 Packagist packages, a cluster of NPM libraries, and a couple of Chrome extensions thrown in for good measure. The campaign didn’t have to getPackagistand Socket teams observed PolinRider publishing payloads to multiple package registries including Packagist and Go. Almost 200 malicious release artifacts tied to 111 unique packages and extensions: more than 80PyPINPM and PyPI host their own artifacts. To publish, a maintainer builds a specific artifact file, typically a tarball or a wheel, and uploads it to the registry’s storage using a publishing token. The registry keeps itsTRONThe loader still resolves its second stage from blockchain dead-drops across TRON, Aptos, and BNB Smart Chain, decrypts it with embedded XOR keys, and runs it through eval(). The unpacked chain is the familiar LazarusVS CodeThis is a classic DPRK tactic where the threat actors will leverage VS Code to run JavaScript payloads regardless of what language the host repository uses.