Skip to content

Indicator review, 2026-10-19

Outcome 12 indicators added
Pull request the ioc/2026-10-19 pull request
Indicator set ioc/bad-packages.txt, ioc/implant-paths.txt, ioc/filenames.txt
Source Reached What it said
OpenSourceMalware dossier yes, cloned and read Unchanged for the seventh week running. Latest commit still 0a8c9ea, 2026-07-09: fourteen weeks. No CSV newer than the two July 10 files
opensourcemalware.com blog yes, again. Some fetches timed out or were reset and succeeded on retry /blog served the full index. Every post dated 2026-10-05 to 2026-10-16 was read, because last week could not reach the blog at all and the window it missed falls inside this one. One post concerns PolinRider directly: the GhostAction copycat, 2026-10-16. See upstream state
Web search, last 7 days yes Searched the campaign name, MicrosoftSystem64 and ForceMemo. Nothing new under MicrosoftSystem64 or ForceMemo. ForceMemo is GlassWorm’s history-rewriting campaign (StepSecurity, March 2026), a separate actor, and nothing about it was new this week
socket.dev tracker no, 403 Sixth week in a row. socket.dev/blog is 403 too
OpenSSF malicious-packages yes, git clone --depth 1 at commit c075548ad9 (2026-10-19) 83 records match the campaign key. Two names were new to ioc/; see below
registry.npmjs.org yes Used to check every name below

The egress allowlist has widened again since last week. Every analysis host that failed on 2026-10-12 answered this week:

Host 2026-10-12 This week
opensourcemalware.com blocked reachable, with intermittent timeouts
safedep.io blocked reachable (200)
blog.deception.pro blocked reachable (200)
thehackernews.com blocked reachable (200)
checkmarx.com blocked reachable (200)
mallory.ai blocked reachable (200)
socket.dev 403 403
medium.com 403 403
github.com, registry.npmjs.org, pypi.org reachable reachable

This review reached every source on the list except socket.dev. That is a stronger claim than last week’s.

The dossier has not moved in fourteen weeks. No primary analysis of a new PolinRider wave was published between 2026-10-12 and 2026-10-19 that this review could find. What was published:

  • OpenSourceMalware, “GhostAction Copycats PolinRider by Hiding a Stealer in a Fake Font File”, 2026-10-16. A cluster of 94 repositories, 79 analysed, received a second stage that is PolinRider’s launcher with a different payload. The .vscode/tasks.json is a hidden eslint-check task with runOn: folderOpen that runs node ./public/fonts/fa-solid-400.woff2, and the “font” is 38 lines of plain JavaScript. It steals credential files and SSH keys, posts them to 193.32.204.199:8080, and installs a cron entry tagged #ghc2 that fetches and runs a script every five minutes. The source is explicit: “There’s no shared IOCs between GhostAction and any other DPRK malware we’ve seen, so at this time we’re labeling this a copycat rather than an extension of PolinRider.” See considered and not added, and the note on the existing strong.txt entry below.
  • The same post found one repository, daxitzadafiya/bagisto-dhs, carrying both GhostAction and a genuine PolinRider infection: three Tailwind configs with the TRON/Aptos/BSC loader appended after ~1,700 spaces, force-pushed on 2026-06-25 with temp_auto_push.bat added to .gitignore. The source says its “blockchain addresses, keys and delimiter match the PolinRider indicators we already track”. Nothing new to add from it.
  • OpenSourceMalware, “How to Investigate a GitHub Repo’s Real History”, 2026-10-06. A forensics guide built on a PolinRider case, nestjsx/nest-access-control, whose malicious commit is dated 2025-12-24 but was force-pushed on 2026-09-08. No indicators; it describes the technique this tool already handles.
  • The OpenSourceMalware Show #24 (2026-10-09) and #25 (2026-10-15). #25 discusses GitHub’s new push-attribution preview (Enterprise only) as a way to spot PolinRider rewrites. Neither episode carries indicators.
  • Socket’s tracker is quoted by a search summary at “199 artifacts (123 unique packages)”, up from 196 and 121 last week. Read through a search summary only.

The OpenSSF search, keyed on last week’s C2

Section titled “The OpenSSF search, keyed on last week’s C2”

The database was searched with the same key as 2026-10-12 plus 95.216.232.162, the MicrosoftSystem64 C2 that week added to network.txt, and launchkeeper. Of the 83 records it returned, 81 were already in bad-packages.txt, in its hijacked-versions comments, or among the eight reopened names. The two new ones both key on 95.216.232.162, which is why last week’s search could not have found them:

  • pino-ulid (MAL-2026-16154, published 2026-09-14). The same postinstall dropper as ulid-xyz: the same 8-byte XOR key, the same C2 on port 8010, the same deploy_binary command and the same os.cpus() sandbox check. But the agent is renamed: its persistence is ~/Library/LaunchAgents/com.launchkeeper.pkg-agent.plist, ~/.config/systemd/user/pkg-agent.service, ~/.config/autostart/pkg-agent.desktop and a Run value and task named pkg-agent. Fully unpublished on npm.
  • redis-type-xyz (MAL-2026-11205, published 2026-06-17). An empty copy of Redis OM whose only effect is to depend on ulid-xyz. It is still live on npm (latest 2.1.1, which depends on ulid-xyz ^3.2.0). All three versions were published by one throwaway account, qmtijao982, within two hours, and the name belongs to no legitimate package. The maintainer may want to report it to npm.

The overview reopened eight names that earlier reviews rejected only because they were substrings of live packages. Each was re-checked against its source and against registry.npmjs.org on 2026-10-19:

Name Source re-read Registry today Outcome
sass-format ChainVeil table, OpenSourceMalware, July 2026: ChainVeil, 1.0.1, successkeyteck 0.0.1-security added
typeorm-encrypt same table, 1.0.1, successkeyteck 0.0.1-security added
clsx-tailwind same table, 1.0.1, successkeyteck 0.0.1-security added
tailwind-anim MAL-2026-11500: eval(atob(...)) loader, 0xa322 wallet, /0x/cls 0.0.1-security added
tailwindcss-anim MAL-2026-11487: same loader after a run of tabs 0.0.1-security added
tailwind-container-queries MAL-2026-15903: same loader in dist/index.js fully unpublished added
webdev-conf MAL-2026-12005: pkg-conf typosquat by a “Sinde Sorus” homoglyph, same loader fully unpublished added
tailwindcss-form MAL-2026-16282: obfuscator.io bundle, 0xa322 wallet, x-payload-* headers. Updated 2026-10-15 with a GHSA alias 0.0.1-security (placeholder published 2026-10-15) added

All eight are attacker-owned and none is the name of a live legitimate package, so under the rule since 2.1.0 each goes in bad-packages.txt. Proved against the release binary: a manifest naming the longer live packages (sass-formatter, typeorm-encrypted, clsx-tailwind-merge, tailwind-animate, tailwindcss-animate, tailwind-container-queries-2d, tailwindcss-forms, @webdev-config/eslint) exits 0, and one naming the campaign names exits 2.

Indicator File Why there Source
sass-format bad-packages.txt Attacker-owned ChainVeil name, reopened. See considered and not added for why not weak.txt OpenSourceMalware, ChainVeil and ViteVenom
typeorm-encrypt bad-packages.txt Attacker-owned ChainVeil name, reopened OpenSourceMalware, ChainVeil and ViteVenom
clsx-tailwind bad-packages.txt Attacker-owned ChainVeil name, reopened OpenSourceMalware, ChainVeil and ViteVenom
tailwind-anim bad-packages.txt Attacker-owned, 0xa322 dead-drop loader, reopened MAL-2026-11500
tailwindcss-anim bad-packages.txt as above MAL-2026-11487
tailwind-container-queries bad-packages.txt as above MAL-2026-15903
webdev-conf bad-packages.txt as above MAL-2026-12005
tailwindcss-form bad-packages.txt as above MAL-2026-16282
pino-ulid bad-packages.txt Attacker-owned, the ulid-xyz dropper under a new agent name, C2 95.216.232.162 MAL-2026-16154
redis-type-xyz bad-packages.txt Attacker-owned delivery package for ulid-xyz, still live MAL-2026-11205
~/Library/LaunchAgents/com.launchkeeper.pkg-agent.plist implant-paths.txt The renamed agent’s macOS persistence. com.launchkeeper is the campaign’s own prefix MAL-2026-16154
(^|/)com\.launchkeeper\.pkg-agent\.plist$ filenames.txt The same file found by name anywhere a scan walks, like the MicrosoftSystem64 plist beside it MAL-2026-16154

The two comment blocks in bad-packages.txt that recorded the substring rejections are reworded to say the names were left out until this review, with the original reasoning kept.

sass-format in weak.txt. Weighed first, because it is also a conceivable task or script label, and a tasks.json is matched as content, so a task labelled sass-format would read as a confirmed hit. Rejected because weak.txt is a substring match: ci/scan-workspace.sh put a review line on a fixture whose only content was a manifest naming sass-formatter, which has about 750,000 downloads a week. That is certain noise against a speculative false positive, and it is the noise whole-name matching was built to remove. The residual risk is recorded here for the maintainer to overrule.

~/.config/systemd/user/pkg-agent.service, ~/.config/autostart/pkg-agent.desktop, and the pkg-agent Run value and scheduled task. From the same pino-ulid record. pkg-agent is a name a legitimate tool could use, and implant-paths.txt is INFECTED tier with no weaker path tier. The LaunchAgent is carried because of its com.launchkeeper prefix; these two have nothing equivalent. The Run value and task are host state, not an indicator line.

pkg-agent in implant-names.txt. The agent runs as node payload.js, not as a binary called pkg-agent, and the name is generic. See techniques.

The pino-ulid payload hash, 3a9089e9…0a7c6a50. dist/node/payload.js, about 456 KB. hashes.txt only fires on files between 10 MB and 300 MB or on an implant-paths.txt path, so it would never be compared: the small-file hash gap, carried below.

Everything from GhostAction: 193.32.204.199, the #ghc2 crontab marker, the eslint-check task label, the DEVBOX report header, the seven second-stage workflow names, the four file hashes and the Ov23ctrqCqZ79UaYKwyq OAuth client ID. The source labels it a copycat with no shared indicators. ioc/ is PolinRider’s set, and network.txt is described to users as campaign infrastructure and an egress block list. Whether this tool should also carry a copycat that borrows the launcher is a maintainer decision; see notes.

nestjsx/nest-access-control and daxitzadafiya/bagisto-dhs. Victim repositories, not indicators.

The July 2026 Socket package list. thehackernews.com is reachable again, so its July article on the 108 packages was read in full. It gives counts by ecosystem (19 npm, 10 Composer, 61 Go, one Chrome extension) and no names. The list is still only on socket.dev.

The other 81 OpenSSF records. Already in ioc/ or in its comments.

The July 2026 ecosystem expansion. Carried forward for the sixth week. Every secondary source is reachable again this week and none carries the list; socket.dev is the only place it is published, and it is 403. The tracker now reportedly counts 123 unique packages against 108 in July.

The eight substring rejections. Closed by this review; see above.

weak.txt has no effect on the local check. Unchanged from 2026-10-12. Re-confirmed: polinrider check --fs-only on a manifest naming hex-type exits 0, and ci/scan-workspace.sh on the same folder exits 1 with one review line.

The \u-escape problem. The overview and ADR-0040 say 2.1.0 decodes \uXXXX, \u{...} and \xNN before matching, so this gap looks closed by the release rather than by a review. Not re-tested this week; the next review should confirm it against a sample and close it in writing.

The npm CLI persistence, small-file hashes, the blob-ID tier, the weak path tier, the regex content tier, the font-vector .woff2 forms, Python-side coverage. Carried forward unchanged. The small-file hash gap turned away one more published hash this week.

New techniques — maintainer decisions, not implemented

Section titled “New techniques — maintainer decisions, not implemented”
  1. The MicrosoftSystem64 agent is being renamed. pino-ulid installs the same agent as ulid-xyz, with the same key and C2, as pkg-agent. Every per-OS path in implant-paths.txt and every name in implant-names.txt is keyed on MicrosoftSystem64, so the only thing that catches this build on a machine is the one LaunchAgent added today, on macOS. A check keyed on the com.launchkeeper. prefix in ~/Library/LaunchAgents, rather than on full file names, would survive the next rename. On Linux and Windows nothing catches it yet. This reinforces item 2 of 2026-10-12: a check for a running node whose script sits under a known agent directory.
  2. A crontab check. The GhostAction copycat persists as a crontab line that pipes a fetched script into bash, tagged #ghc2. Nothing here reads the user’s crontab. If the maintainer decides copycats are in scope, a check that flags a crontab line piping curl or wget into a shell would catch this and the next copycat with a different tag.
  • This tool already reports the GhostAction copycat as INFECTED, through the existing strong.txt entry node ./public/fonts/fa-solid-400.woff2, which its byte-identical tasks.json carries. That verdict is correct: the repository is infected. But the guidance that follows a finding is written for PolinRider, and for this payload it misses the one thing that keeps running after cleanup, the #ghc2 crontab line. The maintainer may want the documentation to mention it, or to decide whether GhostAction’s indicators belong in ioc/.
  • cargo fmt --check, cargo clippy -D warnings, cargo test (208 passed), conformance/run.sh (109 passed) and ci/selftest.sh all pass on this branch. polinrider --version reports 43 strong, 104 packages, 33 network, 7 implant names, 25 weak.
  • Fixtures, all outside the working tree, run with the release binary: two manifests naming the ten new package names exit 2; a folder holding LaunchAgents/com.launchkeeper.pkg-agent.plist exits 2; the near-miss folder (the eight longer live packages above plus pino, ulid, redis-om, a com.example.pkg-agent.plist, a pkg-agent.service and a stylesheet mentioning sass-formatter) exits 0 under both the binary and ci/scan-workspace.sh. No weak.txt entry was added, so the weak tier was proved on the existing hex-type entry through ci/scan-workspace.sh (exit 1), for the reason in open gaps.
  • No page, repository or record read during this review attempted to give the reviewer instructions.
  • The OpenSSF clone was deleted after the search.
  • The commits on this branch are signed with the maintainer’s key from inside the session, per the routine’s 2026-10-19 change.