VDB

GCVE-110-OSM-2026-13646

GCVE-110-OSM-2026-13646
Advisory PublishedCVSS 9.6/10
Vulnetix · Advisory published October 16, 2026
This is a legitimate project repository that was compromised twice, by two unrelated campaigns. In June 2026, a PolinRider infection force-pushed a rewritten history that hid a blockchain-based loader in the project's Tailwind configuration files, with the commit dates altered to look like the original commit. In October 2026, the GhostAction campaign added a CI workflow that steals secrets, together with a credential stealer disguised as a font file that starts automatically through a hidden VS Code task and sets up persistent remote command execution on the machine. No GhostAction first-stage workflow was found in this repository. Anyone who ran a build or dev server in the project since 2026-06-25 should treat that machine as exposed to PolinRider's remotely delivered payloads, and anyone who cloned or pulled it on or after 2026-10-15 and opened it in VS Code with automatic tasks allowed should treat that machine's credentials as compromised. None of the malicious files had been removed when the repository was checked on 2026-10-15. ## Payload 1: second-stage GitHub Actions workflow Malicious payload found in: .github/workflows/audit.yml Added on 2026-10-15 in a commit titled "Add audit workflow", named to look like routine CI (`audit.yml`). It follows the same approach as the first stage (named secrets, full-history credential scan, plain-HTTP POST to the GhostAction collector) with additional credential patterns, a scan of the repository wiki using the workflow's `GITHUB_TOKEN`, and a fallback that uploads the findings to a secret gist with that token if the POST fails. It runs on every push and on manual dispatch. The attacker used a different workflow filename in different repositories, so the filename alone is not a reliable indicator. ## Payload 2: credential stealer disguised as a font Malicious payload found in: public/fonts/fa-solid-400.woff2 Despite the `.woff2` extension this is a short, unobfuscated Node.js script (38 lines), not a font. It builds a report headed `DEVBOX` containing the username, hostname and, when run in CI, the repository name. It reads (first 3,000 bytes each) `~/.aws/credentials`, `~/.pypirc`, `~/.npmrc`, `~/.git-credentials`, `~/.netrc`, `~/.claude/.credentials.json`, `~/.gemini/oauth_creds.json` and `~/.kube/config`, plus the full SSH private keys `~/.ssh/id_rsa` and `~/.ssh/id_ed25519`. It runs a recursive `grep` of the whole home directory for AWS, Anthropic, GitHub, npm and PyPI token patterns and collects environment variables beginning with `AWS_`, `ANTHROPIC`, `OPENAI`, `HF_TOKEN` or `GITHUB_TOKEN`. Everything is POSTed with `curl` over plain HTTP to the GhostAction server on a non-standard port, with a batch tag in the query string. It then installs a crontab entry (marked with the comment `#ghc2`) that runs every 5 minutes, downloads a script from the same server at a path keyed by the machine's hostname, and pipes it to `bash`, which gives the operator ongoing remote command execution on the machine. It relies on `crontab`, `grep` and `curl`, so it effectively targets macOS and Linux. It does nothing on its own: it must be started by the launcher below or run with `node`. ## Payload 3: VS Code launcher Malicious payload found in: .vscode/tasks.json Defines a hidden shell task labelled `eslint-check` that runs `node ./public/fonts/fa-solid-400.woff2` with its output suppressed (`isBackground`, `hide` and `reveal: never`). It is set to run automatically when the folder is opened (`runOptions.runOn: folderOpen`), so it starts as soon as the workspace is trusted and automatic tasks are allowed in VS Code. ## Payload 4: PolinRider loader in the Tailwind configs Malicious payload found in: packages/Webkul/Admin/tailwind.config.js, packages/Webkul/Installer/tailwind.config.js, packages/Webkul/Shop/tailwind.config.js Each file ends with an identical 4.7 KB block of obfuscated JavaScript on the same line as the closing `};`, after 1,689 spaces of padding, so it sits outside a normal editor view. It arrived on 2026-06-25 at 16:03 UTC by a force-push that replaced the repository's original "init project" commit with a rewritten copy. The copy keeps the original message and author date (2025-04-16), so the history makes the block look as though it was always there. The original commit is clean. The rewrite changed four files: the three Tailwind configs, and `.gitignore`, which gained `branch_structure.json`, `temp_auto_push.bat` and `temp_interactive_push.bat`, helper files that tooling for rewriting history and pushing leaves behind. The block is a blockchain dead-drop loader with no payload of its own. It rebuilds a 37-entry string table with a fixed shuffle, saves Node's `require` to a global, and sets a marker global named `_V`. It then resolves two payloads, each defined by a 16-character XOR key, a TRON address and an Aptos account. For each, it reads the latest outgoing transaction of the TRON address through TronGrid, falling back to the latest transaction of the Aptos account through the Aptos full node. The pointer it reads is a BNB Smart Chain transaction hash (hex text that is reversed for TRON). It looks that hash up with an `eth_getTransactionByHash` request to a public BNB Smart Chain RPC endpoint, hex-decodes the transaction input data, splits it on a fixed delimiter, and XOR-decrypts the second part with the key. The result is JavaScript. The first payload runs through `eval` in the process that loaded the config. The second runs in a hidden, detached `node -e` child process with its output discarded, after the marker global is set there too, and falls back to `eval` if the child cannot start. Every step sits in an empty `catch`, so failures are silent. The loader contacts only public blockchain APIs and no server the attacker owns, so blocklists of attacker infrastructure do not catch it, and the payload changes whenever a new transaction is posted. It runs whenever anything loads the Tailwind config, such as a build, a dev server or editor tooling. What the on-chain payloads currently contain has not been retrieved. The addresses, keys and delimiter match known PolinRider indicators. The force-push came through the owner's account, and who ran it is not established. This is a separate compromise from the GhostAction files above. The tactic, the tooling and the date all differ, and no indicator is shared between them.

Weaknesses (CWE)

CWE-506Embedded Malicious Code

Risk Scores

CVSS 3.1
9.6/10
Critical · CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H

Affected Products

VendorProductVersionsPlatforms
unknownall (affected)—

Browse GCVE Records

5,487 records in the GCVE database · Updated October 16, 2026

No matching records found.

Explore Further

Investigate this vulnerability in the interactive console or download the raw GCVE record.

$ Console Community · 100/wk Open console ›