The Case for 2FA, Post Rest-client Gem CVE
Most CVEs occur as a result of a oversight in the architecture or mishandling of how libraries may interact with your application. In some cases like what had occurred with the Rest-client gem version 1.6.13, a package maintainer account on https://rubygems.org was hijacked and used to push malicious code that would compromise sensitive credentials for payment manager accounts, database access, repository access, and others that can cause irreparable damages. The hijacker conducted a series of releases - 1.6.10, 1.6.11, 1.6.12, and 1.6.13 - all of which contained malicious code. This attack was also more elusive in that it was affecting a point release from a older version. This strategy could have been for a target using a version within
What Possibly led to the CVE
We had a chance to speak to Matt Manning who provided some clues to what may have led to his account being compromised.
I probably hadn’t logged into the rubygems web UI since 2011⁄2012. I don’t know if they had 2fa back then, and I wasn’t disciplined about using a password manager then. I use 1password now, but that login was so old that I didn’t even have it in 1pass, so I didn’t catch it when I audited dupes, etc there. I probably haven’t pushed a public gem since 2014. I guess my api key was cached for that.
Matt raises a point of interest in which we’ll dive into further later, but, its worth noting that he hadn’t pushed to a public gem since 2014. This long predates when 2FA was introduced to RubyGems, which was announced on this blog post November 2018 RubyGems Updates (rubygems.org) in 12/09/2018.
It is possible that the hijacker looked for a package that would compromise the most codebases. The rest-client was certainly a prime target, with nearly 114 million downloads. This approach would ensure that the malicious code would spread fast, far, and wide due the ubiquitous nature of the gem.
However, that the attacker chose to release an update to a very old version of rest-client suggests that it is possible that the attack may have been more targeted to particular company’s codebase that was known to the attacker to use the
~> operator in its Gemfile.
The targeted company may presumably automatically upgrade to newer 1.6 releases unattended and deploy, but would not upgrade to a 1.7 or 2.x release on its own. An alternative theory would be that the attacker is going after old codebases expected to go unnoticed for a longer time than if the latest releases of the Gem were updated.
The hijacker then obtained the account name for a maintainer (mwmanning), Matt Manning. From there, going dumpster diving in old password dumps may have provided him with the credentials necessary to access to the maintainer’s account.
What did the CVE do?
On the Github thread, a user named @JanDintel summed up how the exploit would work if deployed:
- It sent the URL of the infected host to the attacker.
- It sent the environment variables of the infected host to the attacker. Depending on your set-up this can include credentials of services that you use e.g. database, payment service provider.
- It allowed to eval Ruby code on the infected host. Attacker needed to send a signed (using the attacker’s own key) cookie with the Ruby code to run.
- It overloaded the #authenticate method on the Identity class. Every time the method gets called it will send the email/password to the attacker. However I’m unsure which libraries use the Identity class though, maybe some
What can you do to prevent this type of attack in the future?
Here at Rietta, we have a company wide policy to have 2FA on all of our accounts as the base minimum. We have password rotation reminders and some of our employees use something called a YubiKey for that extra layer of security. This step is not to only protect our in-house credentials, but most importantly to protect our clients as it is our responsibility to keep that information secure.
What can the Community do to prevent future occurrences?
Due to the nature of Open Source Software, where libraries of code are open for anyone to contribute to, maintainers are everyday developers contributing to the ecosystem. Security is extremely important when millions of other codebases are integrating these libraries into their systems. Maintainers should always take the extra step of using 2FA at a minimum, to ensure that the good code they are providing to the community isn’t poisoned by bad actors.
Most popular package manager platforms do not enforce a 2FA policy for maintainer accounts for even libraries being used by millions of people. These maintainer accounts are treated with the same level of scrutiny as libraries with even 1 download. The package manager platforms should start enforcing the use of 2FA for all maintainer accounts.
Additionally, for accounts like Matt Manning’s, which haven’t been used in a long time, simply adding an additional security protocol for stale accounts would go a long way. For example, when an account hasn’t been used in X months (>=12 would be a good place to start), users without 2FA activated are required to go through an account recovery email verification or similar process, to regain access to their accounts.
This story is continuing to evolve, so look for further updates soon.