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 |
Sources
Section titled “Sources”| 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.
Upstream state
Section titled “Upstream state”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.jsonis a hiddeneslint-checktask withrunOn: folderOpenthat runsnode ./public/fonts/fa-solid-400.woff2, and the “font” is 38 lines of plain JavaScript. It steals credential files and SSH keys, posts them to193.32.204.199:8080, and installs a cron entry tagged#ghc2that 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 existingstrong.txtentry 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 withtemp_auto_push.batadded 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 asulid-xyz: the same 8-byte XOR key, the same C2 on port 8010, the samedeploy_binarycommand and the sameos.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.desktopand a Run value and task namedpkg-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 onulid-xyz. It is still live on npm (latest2.1.1, which depends onulid-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 eight reopened names
Section titled “The eight reopened names”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.
Considered and not added
Section titled “Considered and not added”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.
Open gaps
Section titled “Open gaps”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”- The
MicrosoftSystem64agent is being renamed.pino-ulidinstalls the same agent asulid-xyz, with the same key and C2, aspkg-agent. Every per-OS path inimplant-paths.txtand every name inimplant-names.txtis keyed onMicrosoftSystem64, so the only thing that catches this build on a machine is the one LaunchAgent added today, on macOS. A check keyed on thecom.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 runningnodewhose script sits under a known agent directory. - 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 pipingcurlorwgetinto 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.txtentrynode ./public/fonts/fa-solid-400.woff2, which its byte-identicaltasks.jsoncarries. 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#ghc2crontab line. The maintainer may want the documentation to mention it, or to decide whether GhostAction’s indicators belong inioc/. cargo fmt --check,cargo clippy -D warnings,cargo test(208 passed),conformance/run.sh(109 passed) andci/selftest.shall pass on this branch.polinrider --versionreports 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.plistexits 2; the near-miss folder (the eight longer live packages above pluspino,ulid,redis-om, acom.example.pkg-agent.plist, apkg-agent.serviceand a stylesheet mentioningsass-formatter) exits 0 under both the binary andci/scan-workspace.sh. Noweak.txtentry was added, so the weak tier was proved on the existinghex-typeentry throughci/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.