CVE-2026-33306
High · 7.5CVSS
7.5
EPSS
0.3%
Published
Mar 24, 2026
CISA due date
—
Cloudanix analysis
Some Ruby applications may have stored passwords in a way that looked very strong but was actually much easier to guess if the password database was stolen.
- Exploitation
- Local Privilege
- Attacker needs
- None
- Complexity
- High
- Impact
- Other
- Component
- Library
- Class
- Cryptographic Weakness
- Remediation
- Upgrade
- Effort
- Medium
- Patch risk
- Low
What it actually does
In bcrypt-ruby before 3.1.22, the JRuby Java implementation calculates the bcrypt strengthening round count with a signed 32-bit integer. When the bcrypt cost parameter is 31, the expected 2^31 strengthening iterations overflow into a negative value, causing the strengthening loop to run zero times. The generated password hash still appears syntactically valid, for example as a $2a$31$ bcrypt hash, and password verification can still succeed, but the password-derived hash is far weaker than intended and can be computed much faster by an attacker. The issue is limited to JRuby usage of bcrypt-ruby with cost=31, or verification of existing $2a$31$ hashes produced or accepted by the affected implementation.
Why it matters to the business
Applications relying on affected JRuby bcrypt-ruby configurations may have stored password hashes that are much easier to crack than expected if password data is stolen. This can increase the likelihood of account takeover, credential stuffing against other services, regulatory exposure related to password protection, and forced password reset campaigns. The risk is highest for applications that deliberately configured the maximum bcrypt cost of 31, because the setting intended to provide the strongest protection instead produces a weakened hash under the affected implementation. The issue is not listed in CISA KEV, has low EPSS, and has no public exploit maturity indicated, but it should still be treated seriously wherever JRuby and cost=31 are in use because exploitation after password-hash disclosure is practical and may not be visible in application logs.
How an attack unfolds
An attacker generally cannot exploit this weakness remotely unless they can influence bcrypt cost selection or obtain password hashes. The more common attack scenario begins with a separate compromise or data leak that exposes stored password hashes. The attacker reviews the hashes and identifies bcrypt entries using the $2a$31$ prefix. If those hashes were generated or verified by a vulnerable JRuby bcrypt-ruby version, the attacker can perform offline guessing against the weakened hashes at far lower computational cost than expected for bcrypt cost 31. Successfully cracked passwords can then be used to access user accounts directly or reused against other services. Because the affected hashes look valid and application password checks still work, the weakness may remain unnoticed until hash formats, gem versions, and runtime configuration are reviewed.
How to fix it
Upgrade bcrypt-ruby to version 3.1.22 or later wherever JRuby is used. If an immediate upgrade is not possible, configure bcrypt cost to a value lower than 31; use a well-tested operational value such as 12 to 16 depending on authentication latency requirements, rather than the maximum value. Search for and remove configuration that sets BCrypt::Engine.cost, BCrypt::Password.create cost, or equivalent application settings to 31. Inventory stored password hashes and identify any $2a$31$ entries. For affected accounts, force password resets or rehash passwords after successful login using a safe cost on a fixed library version. Treat any previously exposed $2a$31$ hashes from vulnerable JRuby deployments as materially weakened. Ensure dependency lockfiles, container images, and deployed artifacts all use the corrected gem version, not only source manifests.
How to confirm the fix worked
Confirm the runtime is not using a vulnerable bcrypt-ruby version by checking dependency manifests and lockfiles, such as Gemfile.lock, for bcrypt or bcrypt-ruby at version 3.1.22 or later. Confirm whether the application runs on JRuby; non-JRuby implementations are not the described vulnerable path. Review authentication configuration and code for any cost value of 31. Query or scan password-hash storage for bcrypt hashes beginning with $2a$31$, $2b$31$, or other bcrypt prefixes using cost 31, with special attention to $2a$31$. After remediation, generate a test bcrypt hash under JRuby and verify that cost 31 is either safely handled by the fixed version or avoided by configuration. Validate that new password hashes are generated with an approved cost lower than 31 unless the fixed library and performance impact have been explicitly tested.
Typical attack path
- Gain access to password hashes through a separate vulnerability, backup exposure, insider access, logging mistake, or database compromise.
- Identify bcrypt hashes using cost 31, commonly visible as strings beginning with $2a$31$.
- Determine or assume the application used JRuby with bcrypt-ruby before version 3.1.22 to generate or verify those hashes.
- Run offline password guessing against the affected hashes, benefiting from the integer-overflow behavior that eliminates the intended key-strengthening loop.
- Recover weak or reused user passwords.
- Use recovered credentials for account takeover, privilege escalation, lateral movement, or credential stuffing against other systems.
How to detect it
This weakness does not usually produce a direct exploitation event in application logs because the main abuse path is offline password cracking after hash exposure. Detection should focus on three areas: exposure, vulnerable hash presence, and downstream account abuse. Review dependency inventories, build artifacts, Gemfile.lock files, software composition analysis results, and container images for bcrypt-ruby versions before 3.1.22 running on JRuby. Search application configuration and code for bcrypt cost set to 31. Search password stores, database exports, backups, logs, support bundles, and data-loss-prevention events for bcrypt hashes beginning with $2a$31$. Investigate any historical incidents that may have exposed password hashes from affected applications. For signs of successful cracking, review identity and application authentication logs for spikes in login failures followed by successful logins, successful logins from new locations or autonomous systems, password-spraying patterns, impossible travel, newly enrolled MFA devices, account recovery changes, and privilege changes after login. Because no known public exploitation or named threat actor activity is indicated, detections should be behavior-based and focused on credential misuse rather than payload signatures.
- $2a$31$
- BCrypt::Engine.cost = 31
- BCrypt::Password.create
- cost: 31
- cost => 31
- bcrypt-ruby
- bcrypt (3.1.21)
- bcrypt (3.1.20)
- bcrypt (3.1.19)
- bcrypt (3.1.18)
- bcrypt (3.1.17)
- bcrypt (3.1.16)
- bcrypt (3.1.15)
- bcrypt (3.1.14)
- bcrypt (3.1.13)
- bcrypt (3.1.12)
- BCrypt.java
What your security team should do
- Prioritize inventory of JRuby applications using bcrypt-ruby, especially authentication services and identity-adjacent applications.
- Upgrade bcrypt-ruby to 3.1.22 or later in source manifests, lockfiles, build pipelines, containers, and deployed runtime artifacts.
- Immediately change bcrypt cost settings from 31 to a lower approved value if upgrade rollout will take time.
- Search password-hash stores and backups for $2a$31$ hashes and plan forced resets or opportunistic rehashing for affected accounts.
- Review whether password hashes have ever been exposed through incidents, backups, logs, analytics exports, support bundles, or development copies; treat exposed cost-31 hashes from vulnerable JRuby systems as high-risk.
- Add dependency scanning rules to flag bcrypt-ruby versions earlier than 3.1.22 in JRuby projects.
- Add secure configuration checks preventing use of bcrypt cost 31 unless explicitly tested on a fixed implementation.
- Monitor authentication logs for credential-stuffing patterns, unusual login success after many failures, and access from unfamiliar geographies or infrastructure, since exploitation is most likely to appear as use of cracked credentials rather than as a direct exploit event.
The record
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.
Affected packages
| Ecosystem | Package | Introduced | Fixed |
|---|
Related vulnerabilities
- CVE-2026-110264 An issue has been discovered in GitLab CE/EE affecting all versions from 7.0 before… CWE-190
- CVE-2026-53615 util-linux is a random collection of Linux utilities. CWE-190
- CVE-2026-106574 ImageMagick is free and open-source software used for editing and manipulating digital… CWE-190
- CVE-2026-106571 ImageMagick is free and open-source software used for editing and manipulating digital… CWE-190
- CVE-2026-75820 GNU Aspell contains an integer truncation vulnerability in the WritableDict::add()… CWE-190
- CVE-2026-102621 A vulnerability was identified in Freedesktop Poppler up to 26.08.0. CWE-190
- CVE-2026-102620 A vulnerability was determined in Freedesktop Poppler 26.06.0/26.07.0/26.08.0. CWE-190
- CVE-2026-102633 libexpat versions 2.7.2 through 2.8.5 contain an integer overflow vulnerability in… CWE-190