CVE-2024-26146 - Slow HTTP DoS in Ruby’s Rack via Malicious Accept and Forwarded Headers
The world of web security is always evolving, and recently a critical vulnerability surfaced in the popular Ruby library, Rack. If your Ruby apps use Rack as a web server interface, you should know about CVE-2024-26146, a bug allowing specially-crafted HTTP headers to slow down (and potentially take down) affected servers.
In this post, we’ll break down how this vulnerability works, who it affects, how exploitation could happen, and how to safeguard your systems.
What is Rack and Why This Vulnerability Matters
Rack acts as the interface between Ruby-based web applications (like Rails or Sinatra) and web servers like Puma, WEBrick, or Unicorn. Nearly every Ruby web application relies on it.
CVE-2024-26146 allows attackers to craft HTTP Accept or Forwarded headers that rack up CPU time during parsing. This “slow header” pattern can choke server resources, bringing your web app to its knees. This is what’s known as a Denial of Service (DoS).
The Technical Details
Rack parses incoming HTTP headers for use in your Ruby app. Certain headers, especially Accept and Forwarded, use regular expressions (regexps) under the hood for parsing. Crafty attackers can create headers that make these regexps take an extremely long time to process—a type of Regular Expression Denial of Service (ReDoS).
Here’s a simplified, conceptual snippet of how Rack’s header parsing might look
# Simulated problematic regex pattern - actual code differs!
if env['HTTP_ACCEPT']
# This regex can consume a ton of CPU given the right string
mime_types = env['HTTP_ACCEPT'].scan(/[^,]+/)
end
If an attacker sends a huge, complex string as the Accept header like below
Accept: x,y,z,a,a,a,a,.... (with thousands of repeated patterns)
… then Rack might chew up a lot of CPU cycles trying to parse it. Multiply this by several concurrent requests and your server could be overwhelmed.
Forwarded
Any endpoint that accepts these headers can be a target.
1. Write a test client
require 'net/http'
uri = URI('http://your-ruby-app.local/';)
long_header = ('a;' * 10_000) # craft a huge Accept header
req = Net::HTTP::Get.new(uri)
req['Accept'] = long_header
begin
Net::HTTP.start(uri.hostname, uri.port) do |http|
http.request(req)
end
rescue => e
puts "Error: #{e.message}"
end
2. Watch your server’s CPU and response time.
If the CPU spikes or the response stalls, you’re likely vulnerable.
Ruby versions before 3.2 (since Ruby 3.2 has mitigations)
Good News:
If you’re using Ruby 3.2+ or have updated Rack per the official advisories, you’re not affected.
Update your Gemfile to the protected version
gem "rack", ">= 2.2.8.1" # Or 3..9.1, depending on your major version
Then run
bundle update rack
2. Use Ruby 3.2 or higher
Ruby 3.2 incorporates fixes to regex performance that blunt this sort of attack.
Example NGINX block
location / {
if ($http_accept ~ "^.{4096,}$") {
return 400;
}
}
References and More Reading
- GitHub Security Advisory for rack GHSA-rx3w-8mxc-gfjv
- CVE-2024-26146 at NVD
- Official Patch Pull Request
- Understanding ReDoS Attacks (OWASP)
Conclusion
CVE-2024-26146 affects a core piece of the Ruby web ecosystem and can allow remote attackers to degrade or deny service on unpatched servers. If you use Ruby and Rack, check your versions, update today, and consider adding header-size limits as a belt-and-suspenders defense.
Timeline
Published on: 02/29/2024 00:15:51 UTC
Last modified on: 02/29/2024 13:49:47 UTC