{"id":"CMP-202611-4oqn2e","kind":"campaign","aliases":[],"related":["MAL-2026-18432"],"title":"Malicious Ruby gem strutil-core 1.0.1 runs a Shai-Hulud-style worm (testmetest/Miasma) on gem install: steals cloud, CI and AI tool credentials, poisons AI coding tools, and wipes the home folder if you revoke the stolen GitHub token (Socket)","description":null,"summary":"Socket found RubyGems package strutil-core 1.0.1, published 1 Nov 2026 04:05 UTC by account jamnajiea and removed by RubyGems about five hours later. During gem install or bundle install its native-extension script (ext/strutil-core/extconf.rb) downloads Bun 1.2.18 and runs a 5.48 MB worm, a newer build of the testmetest worm (Miasma family, built from the leaked Shai-Hulud code). The worm steals cloud (AWS, Azure, GCP), Kubernetes, Vault, registry (RubyGems, npm, PyPI, JFrog), GitHub/GitLab, SSH, Docker, wallet and AI coding tool credentials (.claude/.credentials.json, .codex/auth.json). On GitHub Actions runners it scrapes secrets from runner memory and turns off StepSecurity Harden-Runner. It installs a backdoor that runs attacker scripts, plants MCP servers, SessionStart hooks, CLAUDE.md/AGENTS.md and VS Code tasks in home folders and repositories so AI coding tools re-run it, and contains code to republish to RubyGems, npm and PyPI and spread over SSH. If a stolen GitHub token starts returning 401/403, a monitor deletes the user's home directory. No victims or downstream infected packages confirmed so far; 0 recorded downloads of 1.0.1 when first tracked, and real install count unknown. The PyPI package pystr-codec 0.1.0-0.1.2 (MAL-2026-18432) uses the same collection server.","severity":"high","cvss":null,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"strutil-core","fixed":[],"affected":"= 1.0.1","ecosystem":"rubygems"},{"name":"RubyGems","fixed":[],"ecosystem":"rubygems"},{"name":"GitHub Actions","fixed":[]}],"ecosystems":[],"tags":["campaign","early-warning"],"not_affected_if":[],"how_to_check":"Gemfile.lock, CI caches or container images containing strutil-core (esp. 1.0.1); gems you maintain that gained an unexpected ext/*/extconf.rb or extensions: entry (installer SHA-256 c5264f3b9cf2f95c83a70b7dd7430aab8abc262b853dbbcd5386c3a8a9c74769). Egress to paleasbest[.]top (hxxps://paleasbest[.]top/v1/api). Host artifacts: systemd user service pkg-runtime or LaunchAgent com.user.pkg-runtime, ~/.local/share/runtimes/sync.py, net-health-checker service with ~/.local/bin/net-health-checker.sh and ~/.config/net-health-checker/token, ~/.config/index.js, /var/tmp/.fontconfig_state, bun run from /tmp/.b_<pid> started by Ruby, sudo python3 reading /proc/<pid>/mem on CI runners. Unknown MCP server named workspace-tools or SessionStart hooks in AI tool configs. In GitHub orgs: new public repos described project-utils with adjective-noun-number names, commits with AutoSyncComplete or skip-checks:true, chore/ci-* branches, a 'CodeQL' workflow uploading a scan-output artifact, new .github/init.js, AGENTS.md, CLAUDE.md, .claude/settings.json, .cursor/mcp.json, .vscode/tasks.json; GitHub commits mentioning msync-3f7a; GitLab CI job cache_sync. Hashes: gem 3b3f6e1bde0b28b015030a81a6d805b5b5306dc3f021e1102123971cfd83acc0, index.js 306c1deec402e8b4a6478e7f0fe02194bcd224d37d8fc6ba61ad1951e8abd7a9, sync.py c7ef186aa59b67d9b198a14f6dc8aaee6dd86510ac7c37d28f65d310cd846411, net-health-checker installer e29a5319888319b51345ab8e3e009938ee5c2b9ae5d6bdd053f4f44c98f309d3.","fix":"If strutil-core 1.0.1 was installed anywhere, isolate that host and treat every credential reachable from it as stolen. BEFORE revoking any GitHub token, stop and remove the net-health-checker and pkg-runtime services (systemd units or LaunchAgents), or revoking the token wipes the home folder. Then rotate RubyGems, npm, PyPI, GitHub, GitLab, cloud, SSH, Vault, Kubernetes and AI provider keys from a clean device, and rebuild the host. Remove the planted workspace-tools MCP entries, SessionStart hooks and repo files (.github/init.js, AGENTS.md, CLAUDE.md, .vscode/tasks.json). Block paleasbest[.]top. Removing the gem alone does not clean an infection.","status":"open","published_at":null,"first_seen_at":"2026-11-06T18:44:15+00:00","triaged_at":"2026-11-06T18:44:15+00:00","updated_at":"2026-11-06T18:45:51+00:00","category":"package","confidence":null,"triage":null,"analyst_status":"done","analyst_note":null,"search":"'-18432':258B '-2026':257B '-202611':44A '-4':45A '/agents.md':178B '0':235B '0.1.0':254B '0.1.2':255B '04':69B '05':70B '1':66B '1.0.1':7A,64B,239B '1.2.18':97B '2026':68B '401/403':217B '5.48':101B 'account':73B 'action':148B 'actions':52A 'ai':23A,27A,140B,189B 'attack':169B 'aw':125B 'azur':126B 'backdoor':166B 'build':106B 'built':113B 'bun':96B 'bundl':87B 'campaign':53B 'ci':21A 'claude.md':177B 'claude.md/agents.md':176B 'claude/.credentials.json':144B 'cloud':20A,124B 'cmp':43A 'code':28A,120B,141B,181B,190B,198B 'codec':253B 'codex/auth.json':145B 'collect':262B 'confirm':232B 'contain':197B 'core':6A,49A,63B 'count':246B 'credenti':25A,143B 'delet':220B 'directori':225B 'docker':137B 'download':95B,237B 'downstream':229B 'earli':55B 'early-warn':54B 'ext/strutil-core/extconf.rb':94B 'extens':92B 'famili':112B 'far':234B 'first':241B 'five':80B 'folder':34A,185B 'found':58B 'gcp':127B 'gem':3A,17A,84B 'github':40A,51A,147B,213B 'github/gitlab':135B 'harden':161B 'harden-runn':160B 'home':33A,184B,224B 'hook':175B 'hour':81B 'hulud':12A,119B 'infect':230B 'instal':18A,85B,88B,164B,245B 'jamnajiea':74B 'jfrog':134B 'kubernet':128B 'later':82B 'leak':116B 'mal':256B 'malici':1A 'mb':102B 'mcp':172B 'memori':155B 'miasma':111B 'monitor':219B 'nativ':91B 'native-extens':90B 'newer':105B 'nov':67B 'npm':132B,203B 'oqn2e':46A 'packag':60B,231B,250B 'plant':171B 'poison':26A 'publish':65B 'pypi':133B,205B,249B 'pystr':252B 'pystr-codec':251B 're':193B 're-run':192B 'real':244B 'record':236B 'registri':130B 'remov':76B 'repositori':187B 'republish':200B 'return':216B 'revok':37A 'rubi':2A 'rubygem':59B,78B,131B,202B 'rubygems':50A 'run':8A,99B,168B,194B 'runner':149B,154B,162B 'scrape':151B 'script':93B,170B 'secret':152B 'server':173B,263B 'sessionstart':174B 'shai':11A,118B 'shai-hulud':117B 'shai-hulud-styl':10A 'socket':42A,57B 'spread':207B 'ssh':136B,209B 'start':215B 'steal':19A,123B 'stepsecur':159B 'stolen':39A,212B 'strutil':5A,48A,62B 'strutil-cor':4A,61B 'strutil-core':47A 'style':13A 'task':182B 'testmetest':109B 'testmetest/miasma':15A 'token':41A,214B 'tool':24A,29A,142B,191B 'track':242B 'turn':157B 'unknown':247B 'use':259B 'user':222B 'utc':71B 'vault':129B 'victim':227B 'vs':180B 'wallet':138B 'warn':56B 'wipe':31A 'worm':14A,103B,110B,122B","nicknames":["testmetest","Miasma","Shai-Hulud descendant"],"mitigations":[],"who_affected":null,"tier":"A","verified":{},"analyst_revisit_at":null,"fix_status":null,"audience":null,"plain_action":null,"pkeys":["actions","githubactions","miasma","rubygems","shaihuluddescendant","strutilcore","testmetest"],"ptokens":["actions","core","github","rubygems","strutil"],"labels":["campaign","earlywarning","high","package"],"campaign_type":"supply-chain","search_a":"'-202611':44A '-4':45A '1.0.1':7A 'actions':52A 'ai':23A,27A 'ci':21A 'cloud':20A 'cmp':43A 'code':28A 'core':6A,49A 'credenti':25A 'folder':34A 'gem':3A,17A 'github':40A,51A 'home':33A 'hulud':12A 'instal':18A 'malici':1A 'oqn2e':46A 'poison':26A 'revok':37A 'rubi':2A 'rubygems':50A 'run':8A 'shai':11A 'shai-hulud-styl':10A 'socket':42A 'steal':19A 'stolen':39A 'strutil':5A,48A 'strutil-cor':4A 'strutil-core':47A 'style':13A 'testmetest/miasma':15A 'token':41A 'tool':24A,29A 'wipe':31A 'worm':14A","search_b":"'-18432':258B '-2026':257B '/agents.md':178B '0':235B '0.1.0':254B '0.1.2':255B '04':69B '05':70B '1':66B '1.0.1':64B,239B '1.2.18':97B '2026':68B '401/403':217B '5.48':101B 'account':73B 'action':148B 'ai':140B,189B 'attack':169B 'aw':125B 'azur':126B 'backdoor':166B 'build':106B 'built':113B 'bun':96B 'bundl':87B 'campaign':53B 'claude.md':177B 'claude.md/agents.md':176B 'claude/.credentials.json':144B 'cloud':124B 'code':120B,141B,181B,190B,198B 'codec':253B 'codex/auth.json':145B 'collect':262B 'confirm':232B 'contain':197B 'core':63B 'count':246B 'credenti':143B 'delet':220B 'directori':225B 'docker':137B 'download':95B,237B 'downstream':229B 'earli':55B 'early-warn':54B 'ext/strutil-core/extconf.rb':94B 'extens':92B 'famili':112B 'far':234B 'first':241B 'five':80B 'folder':185B 'found':58B 'gcp':127B 'gem':84B 'github':147B,213B 'github/gitlab':135B 'harden':161B 'harden-runn':160B 'home':184B,224B 'hook':175B 'hour':81B 'hulud':119B 'infect':230B 'instal':85B,88B,164B,245B 'jamnajiea':74B 'jfrog':134B 'kubernet':128B 'later':82B 'leak':116B 'mal':256B 'mb':102B 'mcp':172B 'memori':155B 'miasma':111B 'monitor':219B 'nativ':91B 'native-extens':90B 'newer':105B 'nov':67B 'npm':132B,203B 'packag':60B,231B,250B 'plant':171B 'publish':65B 'pypi':133B,205B,249B 'pystr':252B 'pystr-codec':251B 're':193B 're-run':192B 'real':244B 'record':236B 'registri':130B 'remov':76B 'repositori':187B 'republish':200B 'return':216B 'rubygem':59B,78B,131B,202B 'run':99B,168B,194B 'runner':149B,154B,162B 'scrape':151B 'script':93B,170B 'secret':152B 'server':173B,263B 'sessionstart':174B 'shai':118B 'shai-hulud':117B 'socket':57B 'spread':207B 'ssh':136B,209B 'start':215B 'steal':123B 'stepsecur':159B 'stolen':212B 'strutil':62B 'strutil-cor':61B 'task':182B 'testmetest':109B 'token':214B 'tool':142B,191B 'track':242B 'turn':157B 'unknown':247B 'use':259B 'user':222B 'utc':71B 'vault':129B 'victim':227B 'vs':180B 'wallet':138B 'warn':56B 'worm':103B,110B,122B","search_c":"","first_public_at":"2026-11-03T12:37:09+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"analyst","external_id":"CMP-202611-4oqn2e","url":"https://socket.dev/blog/shai-hulud-descendant-malicious-ruby-gem","severity":null,"published_at":null,"modified_at":null,"first_seen_at":"2026-11-06T18:44:15+00:00","fetched_at":"2026-11-06T18:44:15+00:00","note":null,"cites":["https://socket.dev/blog/shai-hulud-descendant-malicious-ruby-gem"]}],"documents":[{"id":88606,"url":"https://x.com/SocketSecurity/status/2118768788757754350","title":"@SocketSecurity: 💎 Socket researchers uncovered a Shai-Hulud descendant in a malicious Ruby gem, ","kind":"post","source":"x","publisher":null,"text":"💎 Socket researchers uncovered a Shai-Hulud descendant in a malicious Ruby gem, flagged 70 seconds after publication.\n\nThe worm steals credentials, poisons AI coding tools, and carries a wiper triggered by GitHub token revocation.\n\nFull analysis: https://t.co/375oaRryZE\n\nlikes 5 reposts 5","published_at":"2026-11-06T18:13:38+00:00","relation":"describes","confidence":null,"note":null},{"id":88824,"url":"https://socket.dev/blog/shai-hulud-descendant-malicious-ruby-gem","title":"Shai-Hulud Descendant Surfaces in Malicious Ruby Gem That Steals Credentials and Poisons AI Tools (Socket)","kind":"blog","source":"analyst","publisher":null,"text":"Socket Threat Research (Kush Pandya): RubyGems strutil-core 1.0.1 (account jamnajiea, published 2026-11-01 04:05 UTC, removed ~08:52-09:13 UTC) runs a 5.48 MB testmetest/Miasma (Shai-Hulud-derived) worm at gem install via ext/strutil-core/extconf.rb, which downloads Bun 1.2.18 to /tmp/.b_<pid> and runs index.js. Steals cloud, CI, registry, SSH, AI tool (.claude/.credentials.json, .codex/auth.json) and wallet creds; exfil to hxxps://paleasbest[.]top/v1/api, fallback public GitHub repos described project-utils (adjective-noun-number names), commits prefixed AutoSyncComplete, or GitLab. Scrapes GitHub Actions Runner.Worker memory via sudo python3, disables StepSecurity Harden-Runner. Persistence: sync.py at ~/.local/share/runtimes/sync.py as systemd user service pkg-runtime / LaunchAgent com.user.pkg-runtime, polls GitHub commits for msync-3f7a; state /var/tmp/.fontconfig_state; ~/.config/index.js. AI tool poisoning: MCP server workspace-tools, SessionStart hooks, commits .github/init.js, .claude/settings.json, .cursor/mcp.json, .gemini, CLAUDE.md, AGENTS.md, .vscode/tasks.json. Propagation code for RubyGems, npm, PyPI, JFrog Artifactory, GitHub (fake CodeQL workflow, scan-output artifact, chore/ci-* branches, skip-checks:true), GitLab CI job cache_sync, SSH known_hosts. Wiper: net-health-checker (~/.local/bin/net-health-checker.sh, token in ~/.config/net-health-checker/token) runs rm -rf ~/ if stolen GitHub token returns 401/403, 48h lifetime. No confirmed victims or downstream infections. Related PyPI pystr-codec 0.1.0-0.1.2 (MAL-2026-18432) same endpoint. Hashes: gem 3b3f6e1bde0b28b015030a81a6d805b5b5306dc3f021e1102123971cfd83acc0; index.js 306c1deec402e8b4a6478e7f0fe02194bcd224d37d8fc6ba61ad1951e8abd7a9; extconf.rb c5264f3b9cf2f95c83a70b7dd7430aab8abc262b853dbbcd5386c3a8a9c74769; sync.py c7ef186aa59b67d9b198a14f6dc8aaee6dd86510ac7c37d28f65d310cd846411; net-health-checker installer e29a5319888319b51345ab8e3e009938ee5c2b9ae5d6bdd053f4f44c98f309d3; node_win.napi.node c78daf27c42c3929758b24c2ef0baf65eea81b26b0fddfe8a5b748672d40d5f5.","published_at":"2026-11-06T17:11:11+00:00","relation":"describes","confidence":null,"note":null},{"id":57511,"url":"https://www.stepsecurity.io/blog/malicious-files-find-what-supply-chain-attacks-leave-behind-in-your-github-repositories-and-on-developer-machines","title":"StepSecurity: Malicious Files: Find What Supply Chain Attacks Leave Behind in Your GitHub Repositories and on Developer Machines","kind":"blog","source":"blog","publisher":null,"text":"Malicious Files: Find What Supply Chain Attacks Leave Behind in Your GitHub Repositories and on Developer Machines\n\nStepSecurity Malicious Files finds files planted by Miasma, Shai-Hulud and other supply chain attacks in GitHub repositories and on developer machines.","published_at":"2026-11-03T12:37:09+00:00","relation":"mentions","confidence":null,"note":null},{"id":88678,"url":"https://socket.dev/blog/shai-hulud-descendant-malicious-ruby-gem?utm_medium=feed","title":"Socket: Shai-Hulud Descendant Surfaces in Malicious Ruby Gem That Steals Credentials and Poisons AI Tools","kind":"blog","source":"blog","publisher":null,"text":"Shai-Hulud Descendant Surfaces in Malicious Ruby Gem That Steals Credentials and Poisons AI Tools\n\nA malicious Ruby gem runs a credential-stealing worm during installation, poisons AI coding tools, and can wipe files when a stolen GitHub token is revoked.","published_at":"2026-11-06T17:11:11+00:00","relation":"mentions","confidence":null,"note":null}],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}