When an attacker takes over a maintainer's account on RubyGems.org, they don't need to find a new vulnerability to exploit — they just publish one. That's what makes a rubygems account takeover so dangerous: the malicious code ships through the same trusted channel that legitimate updates use, and every application that runs bundle install after the fact pulls it down without a second thought. The most consequential example of this remains the 2019 compromise of the popular rest-client gem, tracked as CVE-2019-15224, in which an attacker hijacked a maintainer's credentials and pushed backdoored releases to thousands of downstream Ruby applications.
This case study walks through what actually happened, which versions and components were affected, what severity data exists for the vulnerability, and what concrete steps engineering and security teams can take to reduce their exposure to the next rubygems account takeover — because there will be a next one.
The RubyGems Account Takeover Pattern Behind rest-client and Beyond
rest-client is a widely used HTTP client library for Ruby, pulled in as a dependency by a large number of gems and applications rather than installed directly by most developers — which is exactly what made it an attractive target. In August 2019, an unknown attacker gained control of the maintainer account authorized to publish the gem on RubyGems.org and used that access to push several new versions containing malicious code.
The injected code was not subtle in its ambitions: it opened a backdoor that allowed the attacker to execute arbitrary code on any machine running the compromised gem, exfiltrated environment variables and cookies (a direct path to credentials and session tokens in many Rails deployments), and included logic associated with cryptocurrency mining. Because rest-client sat deep in dependency trees rather than at the top, many teams that were affected didn't even realize they depended on it — a common and underappreciated feature of a ruby supply chain incident.
This wasn't an isolated event, either. In 2017, the strong_password gem — a small library for password strength validation — had malicious versions published after its source was tampered with, injecting code designed to swap cryptocurrency wallet addresses copied to the clipboard. The financial motive and the low-profile nature of the target gem again illustrate a defining trait of these incidents: attackers frequently favor gems with a broad but quiet install base over headline-grabbing targets, because a rubygems security breach is far more valuable when it goes unnoticed for as long as possible.
Affected Versions and Components
For CVE-2019-15224, the malicious code was distributed in rest-client versions 1.6.10, 1.6.11, 1.6.12, and 1.6.13. These versions were published directly to RubyGems.org by the compromised account and were available for installation via standard gem install rest-client or Gemfile/Gemfile.lock resolution for roughly a day before the RubyGems security team and the actual maintainers identified the tampering and yanked the releases.
Any application, gem, or CI pipeline that resolved to one of those four versions during that window was exposed, regardless of whether the team had pinned a "known good" major version — because from the perspective of Bundler and RubyGems, 1.6.13 looked like a normal, signed-off release. This is the structural weakness of a malicious gem version: the delivery mechanism is indistinguishable from a legitimate update unless you are independently verifying package provenance, diffing source against prior releases, or otherwise treating new dependency versions as untrusted until proven otherwise.
For the strong_password incident, the malicious code was shipped in versions 0.0.7 and 0.0.8 of the gem before the legitimate maintainer discovered the tampering and restored a clean release.
Severity: CVSS, EPSS, and KEV Context
CVE-2019-15224 is recorded in the National Vulnerability Database with a CVSS v3 base score of 9.8 (Critical), reflecting a network-exploitable issue requiring no privileges or user interaction and resulting in complete loss of confidentiality, integrity, and availability — consistent with the arbitrary code execution capability the backdoor provided.
EPSS scoring is less illuminating here than it is for typical network-facing CVEs: EPSS estimates the probability of exploitation for a persistent, patchable vulnerability in deployed software, whereas this was a time-boxed malicious publish that was pulled within roughly a day. The exploitation "event" already happened during that narrow window rather than representing an ongoing, scannable attack surface — which is a useful reminder that traditional vulnerability-management scoring doesn't map cleanly onto supply chain compromise events. CVE-2019-15224 does not appear in CISA's Known Exploited Vulnerabilities (KEV) catalog, which tracks vulnerabilities with confirmed in-the-wild exploitation against victim systems post-disclosure rather than malicious-package incidents of this kind. The strong_password incident was never assigned a CVE at all, which is itself a common outcome for malicious-package events and one reason they're easy for vulnerability-scanning tools to miss entirely.
Timeline
- 2017 — Malicious versions 0.0.7 and 0.0.8 of the
strong_passwordgem are published after the gem's source is tampered with, containing code to hijack cryptocurrency wallet addresses copied to the clipboard. The legitimate maintainer discovers the issue and disclosed it publicly. - August 19, 2019 — An attacker who had compromised a
rest-clientmaintainer's RubyGems.org credentials publishes versions 1.6.10 through 1.6.13, each containing backdoor code and cryptocurrency-mining logic. - August 20, 2019 — Community members and maintainers identify the malicious code by comparing the published gem contents against the project's public source repository; the discrepancy is reported to RubyGems.org.
- Shortly after discovery — RubyGems.org's security team yanks the malicious versions, and CVE-2019-15224 is assigned to document the incident. Maintainer credentials are rotated and legitimate clean releases are republished.
- May 2022 — In direct response to this pattern of account-takeover incidents across the open source ecosystem (RubyGems, npm, and PyPI all saw similar events), RubyGems.org announces a phased requirement for two-factor authentication, starting with maintainers of gems that have accumulated the highest download counts.
- 2022–2023 — RubyGems.org progressively extends the MFA requirement to a broader set of maintainer accounts, aiming to close the credential-based attack vector that enabled the
rest-clientcompromise in the first place.
Remediation Steps
- Pin and lock dependencies deliberately. Use
Gemfile.lockand commit it to version control so that a newly published, unreviewed version of a transitive dependency can't silently enter your build the next time CI resolves gems. - Enable MFA on every publishing account you control. If your organization maintains or co-maintains any public gems, mandatory hardware- or app-based two-factor authentication on RubyGems.org accounts closes the single most common entry point for this class of incident.
- Audit dependency updates before merging them, particularly for gems that sit deep in your dependency tree and rarely get manual review. Diffing a new release against its source repository tag is a cheap check that would have caught both incidents described above.
- Monitor for yanked or flagged gem versions in your existing lockfiles. Versions pulled from RubyGems.org after a compromise don't automatically disappear from environments that installed them before the yank.
- Restrict outbound network access from build and CI environments where feasible, since both incidents relied on the compromised package reaching out — for cryptocurrency mining infrastructure or credential exfiltration — immediately at install or require time.
- Maintain a software bill of materials (SBOM) for your Ruby applications so that when the next rubygems account takeover happens, you can determine your exposure in minutes rather than days of manually grepping Gemfiles across every repository.
How Safeguard Helps
Incidents like the rest-client compromise expose a hard truth: traditional vulnerability scanning, which looks for known CVEs in known versions, is structurally too slow to catch a malicious gem version the moment it's published, because there's no CVE yet — often not for hours or days after the damage is done. Safeguard is built for that gap. We continuously monitor gem publishing activity, dependency graphs, and behavioral signals in newly released package versions so that anomalies — a sudden new maintainer, unexpected network calls, obfuscated payloads, or code diverging from a project's public source — surface before they reach your build pipeline, not after a CVE is finally assigned.
Safeguard also maintains an up-to-date, verified inventory of every gem and version in use across your applications, mapped against real-time supply chain threat intelligence, so that if a rubygems security breach does occur, your team knows within minutes exactly which services, repositories, and environments are affected rather than spending days reconstructing dependency trees under pressure. Combined with policy controls that let you require review or MFA-backed provenance for newly published dependency versions before they're allowed into a build, Safeguard turns the lessons of the rest-client and strong_password incidents into standing protection — so the next ruby supply chain incident gets caught at the door instead of in production.