RubyGems Navigation menu

Blog

Back to blog posts

Password Reset Vulnerabilities, Hacker One and Humility

Have you ever thrown actual spaghetti at a wall? It’s funny, sticky and barely induces any panic. HackerOne reports, on the other hand, have the opposite effect. Unlike wet spaghetti, the clean-up job is far more work for our security team.

Running a bug bounty program means a stream of incoming reports, not all of them correct, that must be reviewed. After receiving enough dire-sounding reports that ultimately lead nowhere, it can look like thrown spaghetti (a see-what-sticks approach). Though we try to give each report a thorough, unbiased evaluation, it’s difficult to keep an open mind about any given report.

Dead-end reports cost the RubyGems security team time, and slow down our ability to address more urgent security issues. I once spent days working on a vulnerability and the result was: clicking that checkbox in BurpSuite invalidates this approach.

But sometimes a hacker finds a very real security issue. This is a story about a recent bug report that I almost closed, assuming it was another false alarm, and how I realized I was wrong.

MFA bypass detected?

On Dec 22nd, 2023, we received the report about multi-factor authentication (MFA) bypass on RubyGems.org’s password reset form. The report claimed to reset an MFA-protected user’s password using only the emailed password reset token, significantly undermining the value of MFA. Most reports tend to claim a similar severity level with varying accuracy. However, that’s not exactly what the report’s included screen capture looked like to me.

In the screen recording, the reporter showed the following:

  1. Use a tool to store the full response from a valid password reset on an attacker-controlled user without MFA.
  2. Use the same tool to man-in-the-middle (MITM) the request by the victim (an MFA enabled user).
  3. Swap the saved response in place of the MFA challenge response for the victim before it is returned.
  4. Change every user_id in the page to the victim’s user_id (mostly to places that I could tell did not matter, which added skepticism).
  5. Allow the MITM to finish, returning the full password edit page to the attacker, but with the victim’s user IDs swapped.
  6. Use the form intended for the attacker’s user, but with the victim’s user id, to submit the password for the victim.

I was confused and, if I’m honest, a little annoyed. The report made no sense and the changes he was making to the page seemed unrelated. It was not clear to me from the screen recording which account was being submitted or which user had their password updated. I didn’t see signing in nor the user profile of either user. I thought swapping the response body was just sending back the attacker’s session cookie, therefore logging in the attacker user as intended and ignoring the victim. Swapping the user IDs everywhere seemed especially silly and further invited my skepticism.

Additionally, the attacker used MITM to execute the vulnerability. Very few applications are immune to MITM where the attacker has unencrypted access to the victim’s request and response (HTTPS should make this impossible).

I can come up with a lot of excuses for why I couldn’t possibly have seen this vulnerability right away to make me feel better about my misplaced skepticism, but in short, I was dismissive. I responded to the hacker multiple times with “more information” requests. I pointed out why the approach seemed to be abusing MITM or how sending back a user’s cookies would just authorize that user.

Investigation

Luckily the reporter was very responsive to my requests and I was open to being proven wrong. The report was filed at 5:30am Dec 22nd and by 7:30pm the same day we had communicated back and forth for hours. By the 3rd screen recording and after a discussion with Samuel Giddins, our AWS sponsored Software Engineer in Residence, we finally arrived at a proof of the vulnerability. The report had found a real issue.

The vulnerability worked like this: the password reset form (the edit action in our rails app) was well protected behind MFA and an email token. You could not render the form without verifying your MFA credentials. However, the submit action on the form (the update action in our rails app) only checked the email token and did not care if you had previously submitted a valid MFA. Oops. All the seemingly pointless changes the hacker was doing were for the purpose of rendering the form so it could be submitted without having passed the MFA check.

Once verified, I opened a GitHub Security Advisory which allows the creation of a private fork. This is a critical step for open source projects to avoid leaking vulnerabilities before they are patched.

With the further help of Eric Herscovich and Josef Šimánek, we merged and deployed the fix in commit 0b3272a on Jan 7th, 2024. The vulnerability was published and assigned CVE-2024-21654. We have no reason to believe that this exploit was ever used since it would require a compromised email token.

Reflection

This report provided a valuable lesson for me. Valid security research can come from hackers that are completely unfamiliar with your app.

What I first interpreted as a naive false alarm turned out to be a real security issue. It was humbling for me to take a look back at how doubtful I had been at first when ultimately the report proved valid. I wanted, even expected, reports to be sophisticated, to put my doubts to rest with advanced techniques or clear scripted exploits. The reality is that you can’t dismiss a report based on your initial impressions.

The hacker who submitted the report relied on the tools at their disposal to explain the problem. The approach that I would expect from a maintainer of RubyGems.org, someone with a fuller understanding of our code, was not available to this reporter. What I interpreted as a convoluted approach, copypasta hand-editing via MITM, was still an effective way to exploit rubygems.org.

When vulnerability reports talk about “a sophisticated hacker”, it may just be a cover for the egos of the hacked. As I have now experienced, an apparently “non-sophisticated” attack can still be sophisticated enough to bypass security.

My hope is that this story helps you understand how the password reset process poses a unique attack surface, and that it also challenges your own sense of what makes a good vulnerability report. High-quality reports sometimes look like low-effort spaghetti throwing.

I want to thank the undisclosed reporter of this vulnerability for their patience and persistance.

What you can do right now to support RubyGems.org security

While you’re here, I encourage you to immediately enable MFA on RubyGems.org via one time passcode. Better yet, enable our recently launched Passkey support. Without a one-time passcode or a passkey your account is only as secure as your email.

We invite anyone with an interest in security to challenge RubyGems.org and the RubyGems/Bundler library. Please, please read our policy first! You can find it at the RubyGems Hacker One program. Please don’t disrupt the RubyGems.org service or interfere with users of RubyGems. Our bug bounty program is backed by the Internet Bug Bounty which makes it possible for us to award hackers that help make our service more secure for everyone.

Responding to security reports like this takes significant time and resources. Paying our maintainers for their time to validate and fix vulnerabilities like this costs Ruby Central many thousands of dollars per year. Our generous supporters and sponsors ensure that we are able to support the security of the RubyGems ecosystem and respond to reports like this. If you aren’t already chipping in to support RubyGems, please consider supporting us directly with even a small donation. If you use Ruby at work, ask your employer about sponsorship opportunities for Ruby Central. Help us ensure that RubyGems is safe and secure.

Martin Emde