bcrypt-ruby has an Integer Overflow that Causes Zero Key-Strengthening Iterations at Cost=31 on JRuby
Description
bcrypt-ruby is a Ruby binding for the OpenBSD bcrypt() password hashing algorithm. Prior to version 3.1.22, an integer overflow in the Java BCrypt implementation for JRuby can cause zero iterations in the strengthening loop. Impacted applications must be setting the cost to 31 to see this happen. The JRuby implementation of bcrypt-ruby (BCrypt.java) computes the key-strengthening round count as a signed 32-bit integer. When cost=31 (the maximum allowed by the gem), signed integer overflow causes the round count to become negative, and the strengthening loop executes zero iterations. This collapses bcrypt from 2^31 rounds of exponential key-strengthening to effectively constant-time computation — only the initial EksBlowfish key setup and final 64x encryption phase remain. The resulting hash looks valid ($2a$31$...) and verifies correctly via checkpw, making the weakness invisible to the application. This issue is triggered only when cost=31 is used or when verifying a $2a$31$ hash. This problem has been fixed in version 3.1.22. As a workaround, set the cost to something less than 31.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
An integer overflow in JRuby's bcrypt-ruby causes zero strengthening iterations when cost=31, producing a valid but trivially weak hash.
Overview
bcrypt-ruby is a Ruby binding for the OpenBSD bcrypt() password hashing algorithm. In the JRuby implementation (BCrypt.java), the key-strengthening round count is computed as a signed 32-bit integer. When the cost parameter is set to 31 (the maximum allowed by the gem), a signed integer overflow occurs, causing the round count to wrap to a negative value. As a result, the strengthening loop executes zero iterations, collapsing bcrypt from 2^31 rounds of exponential key-strengthening to effectively constant-time computation [1][3].
Exploitation
This issue is triggered only when cost=31 is used or when verifying a $2a$31$ hash. The resulting hash appears valid and verifies correctly via checkpw, making the weakness invisible to the application [1][4]. An attacker who obtains the database of password hashes can easily brute-force these trivially weak hashes, as the intended 2^31 rounds of strengthening are entirely bypassed.
Impact
Successful exploitation removes bcrypt's primary security property: exponential key-strengthening. Instead, only the initial EksBlowfish key setup and final 64x encryption phase remain. This dramatically reduces the time required to crack the password, effectively making the hash as weak as a single iteration of the underlying cipher [1].
Mitigation
The vulnerability has been fixed in bcrypt-ruby version 3.1.22 [2][3]. As a workaround, users should avoid setting the cost to 31 and instead use a cost value less than 31 [1]. Organizations using JRuby to run Ruby applications should upgrade the bcrypt gem immediately and re-hash any passwords that may have been stored with cost=31.
- NVD - CVE-2026-33306
- GitHub - bcrypt-ruby/bcrypt-ruby: bcrypt-ruby is a Ruby binding for the OpenBSD bcrypt() password hashing algorithm, allowing you to easily store a secure hash of your users' passwords.
- Release v3.1.22 · bcrypt-ruby/bcrypt-ruby
- ruby-advisory-db/gems/bcrypt/CVE-2026-33306.yml at master · rubysec/ruby-advisory-db
AI Insight generated on May 18, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
bcryptRubyGems | < 3.1.22 | 3.1.22 |
Affected products
2- Range: <3.1.22
- bcrypt-ruby/bcrypt-rubyv5Range: < 3.1.22
Patches
1831ce64cb0a9Merge commit from fork
3 files changed · +8 −4
bcrypt.gemspec+1 −1 modified@@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = 'bcrypt' - s.version = '3.1.21' + s.version = '3.1.22' s.summary = "OpenBSD's bcrypt() password hashing algorithm." s.description = <<-EOF
CHANGELOG+3 −0 modified@@ -1,3 +1,6 @@ +3.1.22 Mar 18 2026 + - [CVE-2026-33306] Fix integer overflow in Java extension + 3.1.21 Dec 31 2025 - Use constant time comparisons - Mark as Ractor safe
ext/jruby/bcrypt_jruby/BCrypt.java+4 −3 modified@@ -688,20 +688,21 @@ static long roundsForLogRounds(int log_rounds) { */ private byte[] crypt_raw(byte password[], byte salt[], int log_rounds, boolean sign_ext_bug, int safety) { - int rounds, i, j; + long rounds; + int i, j; int cdata[] = bf_crypt_ciphertext.clone(); int clen = cdata.length; byte ret[]; if (log_rounds < 4 || log_rounds > 31) throw new IllegalArgumentException ("Bad number of rounds"); - rounds = 1 << log_rounds; + rounds = roundsForLogRounds(log_rounds); if (salt.length != BCRYPT_SALT_LEN) throw new IllegalArgumentException ("Bad salt length"); init_key(); ekskey(salt, password, sign_ext_bug, safety); - for (i = 0; i < rounds; i++) { + for (long r = 0; r < rounds; r++) { key(password, sign_ext_bug, safety); key(salt, false, safety); }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-f27w-vcwj-c954ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-33306ghsaADVISORY
- github.com/bcrypt-ruby/bcrypt-ruby/commit/831ce64cb0a9502130fa93a28bfd9527a5fa45c4ghsax_refsource_MISCWEB
- github.com/bcrypt-ruby/bcrypt-ruby/releases/tag/v3.1.22ghsax_refsource_MISCWEB
- github.com/bcrypt-ruby/bcrypt-ruby/security/advisories/GHSA-f27w-vcwj-c954ghsax_refsource_CONFIRMWEB
- github.com/rubysec/ruby-advisory-db/blob/master/gems/bcrypt/CVE-2026-33306.ymlghsaWEB
News mentions
0No linked articles in our index yet.