top of page

69 Malicious RubyGems Were Listed Free. Our Deny-List Held Zero of Them. Capability Is Not Coverage.

Writer: Patrick Duggan
Patrick Duggan
2 hours ago
5 min read

RubyGems is the supply-chain story of the week. On October 5, OpenSourceMalware and SafeDep published research on 42 malicious crypto gems pushed by one publisher, the account reqthrottle_3474, a campaign researchers are calling "Ghost Dev." The Hacker News picked it up on October 8. On October 11 another 24 crypto-themed gems landed in OSV. Add three singles from late September and the count is 69 malicious RubyGems published to OSV since September 22.


Our deny-list held zero of them. Not a few. Zero. Free tools had the first 42 within hours on October 5, and we were absent for nine days. This post is about why, what we fixed, and what a Ruby shop with no security budget should do today.





What Ghost Dev does


The gems install like utilities a crypto developer would want: req-throttle-mini, eth-keystore-utils, wallet-hd-derive, bip39-wordlist-utils, plus typosquats of real names like bitcion-ruby, cryoto-toolbox and lighthing-invoice. The payload runs at install time. Per OpenSourceMalware and SafeDep, it opens a reverse shell to 45.138.12.177. You don't have to require the gem or call a single method. Running gem install is enough.


That's the part that matters for defenders. A developer machine that holds wallet keys, cloud credentials and a signed-in GitHub session hands an interactive shell to a stranger the moment the install finishes.



Free tools had it within hours


OSV, the free open-source vulnerability database at osv.dev, published the first record (MAL-2026-17568, req-throttle-mini) at 06:47 UTC on October 5. GitHub's advisory database had the same gem at 09:31 UTC, and by the end of that day GitHub listed all 42 as malware. On October 11 GitHub listed 24 more. We counted all 69 in GitHub's advisory database ourselves this morning (ecosystem rubygems, type malware, published September 22 through October 15).


None of that costs anything. A cash-poor shop that queried OSV or GitHub before installing was covered on October 5. Anyone relying on our check-package tool was not.



Why we had zero


Three failures stacked, and each one looked green.


The collector existed and never ran. In August we wrote a sync for RubyGems, crates.io and Go malicious-package advisories. It was never put on the scheduler. It last ran by hand around August 22. On October 14 the newest RubyGems advisory we held was MAL-2026-10914, while our npm list had reached MAL-2026-17876. Same source, same numbering, nearly 7,000 numbers apart. npm and PyPI synced every hour, so the dashboards looked healthy. Ruby, Rust and Go sat frozen for seven weeks while the feed for the two big ecosystems kept ticking.


Our check tool answered the wrong question. check-package is the tool AI agents call before installing a dependency. It treated every ecosystem that wasn't npm as PyPI. Ask it about a Ruby gem and it searched the Python deny-list, checked the Python registry, and came back with a confident answer about the wrong ecosystem. No error and no warning. Just a verdict about a package nobody asked about.


Our own pre-install hook had the same blind spot. The hook that guards our own agents' installs didn't recognize gem install at all. It also exited silently on Homebrew casks, cargo, go and apt. Silent exit reads as "allowed."


Our own earlier Ruby coverage was no better on speed. On SleeperGem in July and StubMaker in August, we were days behind the vendors who found them, and we added those indicators by hand. OSV and GitHub never listed those gems, so there is no race to claim either way. We were late, and we said so then.



What we fixed, and what we checked live


On October 14 and 15 we shipped four changes. The ones you can reach, we checked against the live service, not just the test suite.


  • check-package routes RubyGems properly. Ask about a gem and it searches the RubyGems deny-list. Ask about an ecosystem it doesn't cover and it now answers "unknown," with the words "NOT a safety judgement." It doesn't guess.

  • RubyGems, crates.io and Go joined the hourly verified OSV sync, the same path npm and PyPI use: every write waited on and read back before the watermark moves.

  • Open-ended malicious ranges block every version. An OSV malware record lists the versions it saw and a range like "introduced 0, never fixed." We were keeping only the list, so a version published after the advisory read "not affected." 49 RubyGems, all 20 Go and 9 crates.io entries had that hole. They now block on any version.

  • The hook checks gems and warns by name on casks, cargo, go and apt instead of exiting silently.

This morning, check-package returned BLOCK for eth-keystore-utils 1.0.0 (MAL-2026-17598), req-throttle-mini 1.0.1 (MAL-2026-17568), wallet-hd-derive 0.9.1 (MAL-2026-17800) and the September single kerokwis (MAL-2026-16420). Then we ran every one of the 69 names. 35 came back BLOCK from check-package, and 10 more we read back directly from the deny-list. Not one we checked was missing. The last 24 we could not check today, because our own public rate limit cut us off, so we are claiming 45 confirmed, not 69.


The Ghost Dev network indicators are in our feed too: the C2 45.138.12.177 at confidence 90, three payload URLs, six payload SHA-256 hashes and five attacker wallets. They're labeled as OpenSourceMalware and SafeDep research with links to both, because it's their work. The IP is in the delivered ips.csv. That's redistribution of their finding, not a detection of ours.



What a Ruby shop should do today


You don't need us for most of this.


  • Check before you install, for free. Look the gem up on osv.dev, or send a POST to api.osv.dev/v1/query with the gem's name and the ecosystem RubyGems. GitHub's advisory database answers the same question at github.com/advisories with the filters ecosystem:rubygems and type:malware. Either one had Ghost Dev on October 5.

  • Pin and freeze. Commit Gemfile.lock and install with bundle config set frozen true, so a build can't pick up a new gem or version nobody reviewed.

  • Block [45.138.12.177](https://analytics.dugganusa.com/stix/register?ref=ioc-click&q=45.138.12.177) outbound at your firewall or DNS layer, and search egress logs for it back to late September. A hit means a developer machine ran an install that phoned home. Treat that machine's keys and tokens as stolen.

  • Treat crypto-named utility gems from new publishers as suspect until someone has read the code. Every one of the 69 was crypto-themed or a typo of a crypto library.

If your AI coding agent already calls our check-package tool, it now answers for RubyGems. It didn't on October 5.



Capability is not coverage


The tool could answer. The list behind it was frozen. Every check said green, because a check against an empty list always says green. That's the failure we keep finding in ourselves: a capability that exists, passes its tests and returns a confident answer, with nothing behind it.


We measured it by comparing our deny-list to a free public source, gem by gem. The sync that should have caught it now runs every hour instead of whenever someone remembers, and every write is read back before we count it. We'd rather tell you we were nine days behind a free database than let you assume we weren't.


Was this useful? Rate this post with the widget at the bottom of the page. We read every response.




Every indicator in this post is in the feed. Free.

1.58M+ IOCs, STIX 2.1 / TAXII, 88% novel vs ThreatFox, exploited-CVE leads ahead of CISA. No credit card — a free API key in 30 seconds, and you can audit every claim above against the live endpoints.



Was this useful? Thirty seconds, no cookies, no tracking, no third parties, your address hashed and never stored. If the box below does not load, the same question lives at https://analytics.dugganusa.com/nps.html?post=69-malicious-rubygems-were-listed-free-our-deny-list-held-zero-of-them-capability-is-not-coverage



Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating
bottom of page