Mar 23, 2026·5 min read·4 visits
Rails Active Support number helpers are vulnerable to DoS attacks due to unbounded memory expansion when processing scientific notation strings into BigDecimals.
A medium-severity Denial of Service (DoS) vulnerability exists in the Active Support component of Ruby on Rails. Unsanitized string inputs containing scientific notation cause excessive memory allocation and CPU consumption during BigDecimal formatting operations, resulting in application resource exhaustion.
Rails Active Support provides various number helper functions, such as number_to_currency and number_to_percentage. These functions facilitate the formatting of numerical data for presentation in web applications. The vulnerability, tracked as CVE-2026-33176 and classified under CWE-400 and CWE-770, affects how these specific helpers process string-based inputs.
The attack vector involves a network-based denial of service. The flaw is triggered when application endpoints pass unvalidated, user-supplied strings directly into the vulnerable helpers. The underlying mechanical failure lies in the conversion of string-based scientific notation into highly expanded floating-point representations during the formatting sequence.
Applications processing malicious inputs experience immediate and severe resource exhaustion. The Ruby interpreter attempts to allocate exceptionally large string objects to satisfy the formatting requirements. This operation results in out-of-memory errors or significant thread blocking, severely degrading application availability.
The root cause is located in the ActiveSupport::NumberHelper::NumberConverter#valid_bigdecimal method. This function normalizes diverse numerical inputs into a BigDecimal object to ensure precision during the formatting process.
Ruby's native BigDecimal constructor accepts strings containing scientific notation, such as "1e1000000". The constructor parses these values successfully without allocating the fully expanded string in memory immediately. The problem surfaces during subsequent string formatting operations invoked by the Active Support helpers.
Formatting operations require the expanded, base-10 representation of the number. When the helper attempts to append decimal places or thousand separators, the Ruby interpreter computes the fixed-point string representation of the parsed BigDecimal.
Processing "1e1000000" forces the interpreter to generate a string exceeding one million characters in length. This operation blocks the executing thread synchronously and rapidly consumes available heap memory, leading directly to uncontrolled resource consumption.
Reviewing the vulnerable implementation in activesupport/lib/active_support/number_helper/number_converter.rb reveals the unconstrained instantiation of BigDecimal. The valid_bigdecimal method processes incoming parameters based on their class type.
def valid_bigdecimal
case number
when Float, Rational
number.to_d(0)
when String
BigDecimal(number, exception: false)
else
number.to_d rescue nil
end
endThe String when clause passes the raw string directly to BigDecimal(number, exception: false). The constructor processes the string, identifying scientific notation markers ('e', 'E', 'd', 'D') and establishing an internal representation with a large exponent. The framework fails to enforce bounds on the magnitude of this exponent before passing the object down the execution chain.
The patched implementation introduces a preventative regular expression check. By validating that the string does not contain 'd', 'D', 'e', or 'E', the framework structurally prevents the interpretation of scientific notation.
def valid_bigdecimal
case number
when Float, Rational
number.to_d(0)
when String
BigDecimal(number, exception: false) unless number.to_s.match?(/[de]/i)
else
number.to_d rescue nil
end
endThis fix addresses the immediate attack vector at the perimeter of the component. Inputs containing scientific notation are correctly rejected and process fallbacks are executed, preserving memory limits during formatting.
Exploitation requires an application execution flow where user-controlled string input is passed unmodified into a formatting helper. A common scenario is a product price display or a financial dashboard that reflects unvalidated URL parameters.
The attacker constructs a minimal payload consisting of a large exponent string. A payload of 1e1000000 is only nine bytes in length. This small footprint easily bypasses standard input length constraints, typical application parameter limits, and web application firewall size restrictions.
Upon submission, the web application routes the parameter to the vulnerable helper. The application thread blocks synchronously while the Ruby interpreter attempts to allocate and format the resulting string.
The resulting operation exhausts the computational resources allocated to the thread, generating a denial of service condition.
The primary impact of this vulnerability is a severe denial of service. Successful exploitation results in immediate memory exhaustion and CPU saturation for the affected Ruby process.
In concurrent environments utilizing application servers like Puma or Unicorn, a single malicious request ties up a worker thread entirely. A low-volume automated attack targeting this vector systematically saturates all available application workers. Once all workers are locked in string expansion operations, the entire service becomes unresponsive to legitimate user traffic.
The vulnerability carries a CVSS v4.0 score of 6.6. This metric reflects the low complexity of the attack, the lack of required authentication, and the high impact on system availability. Confidentiality and integrity boundaries remain unaffected by this specific flaw.
Recovery from this state typically requires manual intervention or an automated orchestration restart of the affected pods or instances. The process will not recover gracefully if the requested memory allocation exceeds the operating system limits, triggering the OOM killer.
The definitive resolution requires updating the activesupport gem to a patched release. The Rails core team provides fixes in versions 8.1.2.1, 8.0.4.1, and 7.2.3.1. Upgrading the framework ensures the regular expression validation is applied globally across all number helper invocations.
Applications running on end-of-life Rails versions must backport the regular expression validation into their local NumberConverter implementation, or sanitize inputs prior to helper invocation.
As a temporary mitigation, developers can sanitize inputs before passing them to number helpers. Implementing a wrapper method that rejects strings matching /[de]/i effectively neutralizes the attack vector without requiring a framework upgrade.
def safe_number_to_currency(val)
return val if val.is_a?(String) && val.match?(/[de]/i)
number_to_currency(val)
endSecurity teams should monitor application logs for anomalous parameter structures containing standard scientific notation markers, specifically targeting endpoints known to process formatting helpers.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:U| Product | Affected Versions | Fixed Version |
|---|---|---|
activesupport Ruby on Rails | >= 8.1.0.beta1, < 8.1.2.1 | 8.1.2.1 |
activesupport Ruby on Rails | >= 8.0.0.beta1, < 8.0.4.1 | 8.0.4.1 |
activesupport Ruby on Rails | < 7.2.3.1 | 7.2.3.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-400, CWE-770 |
| Attack Vector | Network |
| CVSS v4.0 | 6.6 (Medium) |
| Impact | Denial of Service (Availability: High) |
| Exploit Status | None |
| CISA KEV | Not Listed |
The software does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed.