Table of contents

RubyGems supply chain attack: malware used as a credential exfiltration dead drop

RubyGems supply chain attack: malware used as a credential exfiltration dead drop - Featured image Rubygems

Package registries have a well-known abuse pattern: attackers upload malicious packages, and unsuspecting developers install them. Our researchers just found the pattern working in reverse, in a RubyGems supply chain attack that turns the registry into a place to stash stolen data rather than deliver it.

We analyzed 14 Ruby gems uploaded to RubyGems.org over a 6.5-hour window in June 2026. None of them delivered malware. Instead, they contained stolen data: the complete contents of a 1Password vault, exfiltrated by a malicious browser extension and packaged into .gem files using nothing but JavaScript running inside the victim’s browser. The haul included plaintext passwords, SSH private keys, AWS credentials, crypto wallet seed phrases, Social Security numbers, credit card numbers, and bank account details across 63 vault items.

RubyGems wasn’t the delivery mechanism here. It was the dead drop: a trusted, high-traffic domain where stolen data sat until the attacker came back for it, invisible among normal developer uploads.

How the RubyGems supply chain attack works

The vault wasn’t the real 1Password app, which talks to its browser extension over native messaging and IPC, not HTTP. The target was a mock password manager, a Next.js app running at a local .local hostname and seeded with synthetic data: GitHub and Gmail logins, AWS IAM keys, SSH keys, credit cards, bank details, and crypto seed phrases. Whether this was research or a dry run for a live target, we can’t say from the artifacts alone. What we can say is the tooling is fully operational.

The extraction required no special access. A browser extension with broad <all_urls> permissions can quietly query localhost and .local addresses, and this one did, in three steps. First, it enumerated the vault via the item index, learning what existed. Second, it fetched each of the 87 items individually, pulling full plaintext records the application couldn’t distinguish from its own UI. Third, it hit a database endpoint that dumped the underlying SQLite state directly, capturing 19 items the user had already archived or deleted.

All of that data was assembled in memory, compressed, and handed to the real weapon: a single file called helper.js, about 10 lines minified. Using only standard browser APIs (CompressionStream, TextEncoder, fetch), it builds a valid tar-based .gem file and POSTs it straight to the RubyGems publish API with a pre-registered attacker key. No server-side component, no native tooling. The resulting gem has no Ruby source and can’t be require‘d; it exists purely as a container for stolen data.

Anatomy of the attack: a step-by-step timeline

Sorting the 14 gems by their embedded timestamps tells a clear story. It opens with infrastructure testing: three throwaway gems confirming the upload pipeline works, four minutes of trial and error before a clean push. Then reconnaissance: five gems capturing the vault index, app chunks, route map, and rendered HTML, mapping the target before touching any credentials. Next, a gem capturing the security dashboard, letting the attacker prioritize weak or reused credentials. After a multi-hour gap, the real harvest arrives: a full database dump and a complete API sweep, two independent methods pulling the same data as a redundancy check. It closes with 13 rendered admin pages, confirming visually that the exfiltration worked.

Why a package registry makes an ideal dead drop

RubyGems offers three things a command-and-control server can’t. A developer machine POSTing to rubygems.org looks identical to a routine gem push, so DLP rules and SIEM detections tuned to unfamiliar hosts never fire. There’s no attacker infrastructure to take down, since the infrastructure is RubyGems itself. And once published, the gem sits there indefinitely, retrievable with a single gem fetch weeks later from any machine.

The stolen data isn’t a side payload smuggled inside a package. The stolen data is the package.

Why this supply chain attack is hard to detect

The properties that make registries indispensable to developers are exactly what make them attractive exfiltration channels. Blocking POSTs to rubygems.org would break every organization shipping Ruby, so no security team will do it. Payload inspection doesn’t help either: these gems share the same domain, endpoint, content type, format, and size range as legitimate uploads, and since they contain no Ruby source at all, they’d sail past malicious-gem scanners built to catch backdoored code.

Worse, the technique isn’t Ruby-specific. npm’s .tgz, PyPI’s .whl, NuGet’s .nupkg, and Docker Hub’s tar layers can all be assembled in-browser the same way. Any registry accepting HTTPS uploads is a candidate dead drop for this kind of supply chain attack.

The most viable defenses sit at the endpoint and the registry: EDR flagging a browser process POSTing to a registry API with auth headers, a pattern no legitimate workflow produces, plus registry-side anomaly detection on publisher behavior, package structure, and key age. Neither exists at scale today.

Key takeaways for security and AppSec teams

Building .gem files in-browser with the Web Compression API and uploading them to RubyGems as an anonymous dead drop is a real evolution in credential theft. It needs no attacker-controlled server, generates no suspicious outbound traffic, and leaves no forensic trace beyond a gem that looks like ordinary developer output. Since the pattern transfers cleanly to npm, PyPI, NuGet, and any other registry accepting HTTPS uploads, defenders who focus only on attacker-owned infrastructure will miss this category of attack entirely.

Gem names, hashes, and the full attacker API key are withheld and available to verified security researchers and affected vendors on request.

Manage open source risk

Read the report

Recent resources

RubyGems supply chain attack: malware used as a credential exfiltration dead drop - @Mastra

Mastra npm Scope Takeover: 140+ Packages Compromised via easy-day-js Dropper

@Mastra npm: 140+ Packages Compromised

Read more
RubyGems supply chain attack: malware used as a credential exfiltration dead drop - Shai Hulud Miasma

Miasma: Red Hat Cloud Services npm Packages Hit by a Mini Shai-Hulud-Style Campaign

npm packages in @redhat-cloud-services drop a multi-stage cloud credential stealer.

Read more
RubyGems supply chain attack: malware used as a credential exfiltration dead drop - Blog Cover Threat news

Laravel-Lang Composer tag-rewrite Supply Chain Attack

Four Laravel-Lang Composer packages were poisoned via tag rewrite.

Read more