@sudobility cryptominer

A cryptominer hit four @sudobility packages.

What happened (facts)

  • Four packages in the @sudobility scope got a new version within 2m28s on 2026-10-21.
  • Each new version adds "postinstall": "sh scripts/miner.sh".
  • scripts/miner.sh is byte-identical in all four (3,514 bytes).
  • The script downloads and runs an XMRig Monero miner under a disguised process name.
  • All four were published by the npm account johnqh, the scope’s sole maintainer.
  • None of the four malicious versions were ever served by Greenflagged.
  • As of 2026-10-21 ~15:40 UTC, all four were still latest on npm.

Affected versions

PackageBad versionPublished (UTC)Last clean versionnpm weekly downloads (10-12 → 10-18)
@sudobility/testomniac_client0.0.13115:24:40.7150.0.129 (10-17 20:00)493
@sudobility/wildduck_client2.3.10215:26:36.9532.3.100 (10-17 19:54)393
@sudobility/indexer_client0.0.14615:26:51.1560.0.144 (10-17 19:55)373
@sudobility/shaperouter_client0.0.1415:27:08.8580.0.12 (10-17 19:54)362

The johnqh account maintains 175 npm packages (172 under @sudobility, plus @johnqh/haraka, @johnqh/wildduck, signic_sdk). Combined weekly downloads across all 175: 19,495 (npm search API, 2026-10-21). Only these four carry the miner. Every other version across all 175 packuments was checked for preinstall / install / postinstall. The pre-existing hooks are benign: echo '...installed successfully!' (@sudobility/contracts) and patch-package (@johnqh/haraka, @johnqh/wildduck).

Clean prior versions are unaffected. Greenflagged still serves them.

Hashes

ArtifactSHA-256SHA-1 (npm shasum)
scripts/miner.sh (all four)50df05975352cfa4558892d0a9ab493744c8ddd40948efa6755bb11dc5050118
testomniac_client-0.0.131.tgzeba7da4c1c6b3a22f0cb29ab569f750ce56dd8c62f528ae289df011c262443d8bc3a6d8ee27d80d8c744fd45f2eb6766cfd55182
wildduck_client-2.3.102.tgz96a607c1f052d9997cf207047fed381b8338a94867a667453bbb8c8806809f8f5439d124d9c5c1419c84e2ad7ef0ae92f6b07c7a
indexer_client-0.0.146.tgz417cd6de672151ca0a7fa1e6328c7a06d9eab9c0a42e5d121d68656043a3f4e152144949ba1b70b2391529e169ae97ea05948d60
shaperouter_client-0.0.14.tgz867649018b2ed3cf1ccfa9071d98b490693983f82f437aa8e01cd40798b7cf4c372f87819798f88dfdd8a18f513e3607315c1910

The diff (testomniac_client 0.0.129 → 0.0.131)

Tarball diff: one file added, one file changed. No source changes.

  • Added: package/scripts/miner.sh
  • Changed: package/package.json
    • files: ["dist/**/*"] → ["dist/**/*", "scripts"]

    • scripts.postinstall added: sh scripts/miner.sh

    • scripts.prepublishOnly removed. It was:

      sh -c 'test ! -f ../workflows/scripts/check-publish-safety.cjs || node ../workflows/scripts/check-publish-safety.cjs' && bun run clean && bun run build
      

      The other three packages’ prepublishOnly was bun run clean && bun run build. It was removed from all four.

Publish fingerprints: clean vs malicious

FieldClean releases (10-17)Malicious releases (10-21)
_npmUserjohnqhjohnqh
_npmVersion10.9.910.9.8
_nodeVersion22.23.322.23.1
gitHeadpresent (e.g. 7071e2e2…, c2f3f956…)absent in all four
Version step+1+2 in all four (0.0.130, 2.3.101, 0.0.145, 0.0.13 never existed in npm time)
prepublishOnlypresentremoved
Provenancenonenone

GitHub (github.com/johnqh/*, public API, 2026-10-21): the latest commit in mail_box_indexer_client, wildduck_client and shaperouter_client is the 10-17 version bump to the last clean version. No commit contains miner.sh. entitytestomniac_client returns 404 (private or renamed).

What miner.sh does

Header comment, verbatim: # rig2 stealth miner — background, idempotent, quiet. Runs on npm postinstall.

  1. Exits if anything already has a TCP connection on port 8443 (grep 20FB /proc/net/tcp), or if /tmp/.sysmond.lock names a live PID.
  2. Downloads a gzip from a pre-signed S3 URL to /tmp/.smh and gunzips it to /tmp/.smi.
  3. If root: nohup sh /tmp/.smi agent. The script comments call this “full persistent install … cron + relay/tor”.
  4. If passwordless sudo works (sudo -n true): nohup sudo -n sh /tmp/.smi agent.
  5. Otherwise: extracts a base64 tarball after a __PAYLOAD__ marker in /tmp/.smi, takes bin/miner-linux-amd64, and copies it to ~/.local/lib/<name>/<name>. <name> is picked at random from 25 fake system-daemon names.
  6. Writes the XMRig config to ~/.sysmond.json: rx/0 (RandomX), TLS, donate-level: 0, log-file: /dev/null, threads = min(nproc, 4).
  7. The worker name is derived from the hostname, so the pool can tell victims apart.

Fake process names (SNAMES): edid-mon smmon irqstatsd kmod-cache dmi-cache acpi-polld sysfs-mon bus-quotad mem-syncd pci-cache cpu-slot-mon hwmon-sync udev-cache netlink-mon uevent-polld kset-mon slab-mon vmstatd pagemapd smbios-mon edid-cache hpet-mon rtc-syncd dmi-polld

Linux only (/proc/net/tcp, miner-linux-amd64). Every failure path exits 0, so npm install never fails visibly.

IOCs

TypeValue
Mining poolnetworkservicesupp.flashhub.net:8443 (TLS)
Monero wallet49u1mKb1dNoMixgRHt11Ra32D1w4hviUuRDbaJhXFwXUjQP4FiHsSjnDa7XZepDUW1Tx6HhR6gFwWgbuwNxby4UAMiqxieX
Payload hostamplify-gursha-dev-121835-deployment.s3.amazonaws.com
Payload object/amplify-cfn-templates/cloud-backend-cache-20261019.gz
AWS access key in pre-signed URLAKIAUHL4Y2TJZ7L7AE3F
Pre-signed URL Expires2107891463 (2036-10-17 21:24 UTC)
Files on host/tmp/.smh, /tmp/.smi, /tmp/.sysmond.lock, /tmp/.sysmond.log, /tmp/.sm.XXXXXX/, ~/.sysmond.json, ~/.local/lib/<fake-name>/<fake-name>

Not checked: the stage-2 payload itself. We did not download it. The bucket name and object path look like an AWS Amplify deployment bucket (amplify-<app>-<env>-<id>-deployment), likely someone else’s account being abused. That is an inference from the name only.

How Greenflagged caught it

Time (UTC)PTEvent
15:24:40.71508:24testomniac_client@0.0.131 published
15:24:55Greenflagged imports it (npm change feed), +14s
15:25:16Static analysis done, +36s. Routed to AI review
15:26:36.953wildduck_client@2.3.102 published
15:26:45Change feed fetches wildduck_client, but npm returns the old listing. 2.3.102 missed
15:26:51.156indexer_client@0.0.146 published
15:27:00 / 15:27:04Imported (+9s) / analyzed (+13s)
15:27:08.858shaperouter_client@0.0.14 published. We did not mirror this package
15:32:0608:32AI reviewer rejects testomniac_client@0.0.131, confidence 99. Novel-malware alert sent. 7m26s after publish
15:34:3808:34AI reviewer rejects indexer_client@0.0.146, confidence 99. Alert sent. 7m47s after publish
15:39:07shaperouter_client imported on request during triage
15:42:5608:42AI reviewer rejects shaperouter_client@0.0.14, confidence 99
15:45:46wildduck_client@2.3.102 imported on request during triage. Risk 100, routed to AI review
15:53:2908:53AI reviewer rejects wildduck_client@2.3.102, confidence 99

Greenflagged

Greenflagged never served any of these versions. Get started with Greenflagged today at https://greenflagged.dev