Internet Bug Bounty · Report #1374512
The Host Authorization middleware in Action Pack is vulnerable to crafted X-Forwarded-Host values
How it was found
Title: The Host Authorization middleware in Action Pack is vulnerable to crafted X-Forwarded-Host values Scope: https://github.com/rails/rails Weakness: Open Redirect Severity: Medium Link: https://hackerone.com/reports/1189310 Date: 2021-05-09 06:29:19 +0000 By: @mshtawy CVE IDs: CVE-2021-22942, CVE-2021-22881
Details:
Steps to reproduce
This is a follow up to CVE-2021-22881 and https://github.com/rails/rails/commit/83a6ac3fee8fd538ce7e0088913ff54f0f9bcb6f
with a controller like the following
class TestsController < ApplicationController
extend ActiveSupport::Concern
def index
redirect_to('/')
end
end
when sending a request like the following where the URL has a mixed case characters
curl 'http://localhost:3000/tests' -H 'X-Forwarded-Host: Evil.com'
Or all capital case
curl 'http://localhost:3000/tests' -H 'X-Forwarded-Host: EVIL.COM'
Expected behavior
<div id="container">
<h2>To allow requests to evil.com, add the following to your environment configuration:</h2>
<pre>config.hosts << "Evil.com"</pre>
</div>
Actual behavior
<html><body>You are being <a href="http://Evil.com/">redirected</a>.</body></html>%
System configuration
Rails version: Tested on Rails 6.1.3.1 and Rails 6.1.3.2 Ruby version: N/A
Notes
This was fixed in main in this PR https://github.com/rails/rails/pull/41435 but still affects <= 6.1.3.1
The problem is in this code https://github.com/rails/rails/blob/6-1-stable/actionpack/lib/action_dispatch/middleware/host_authorization.rb#L115
origin_host = valid_host.match(
request.get_header("HTTP_HOST").to_s.downcase)
forwarded_host = valid_host.match(
request.x_forwarded_host.to_s.split(/,\s?/).last)
forwarded_host is missing a downcase after the .to_s, which results in nil assigned to forwarded_host, which then results in true in the following code
origin_host && @permissions.allows?(origin_host[:host]) && (forwarded_host.nil? || @permissions.allows?(forwarded_host[:host]))
because of the nil? check on the forwarded_host
forwarded_host.nil? || @permissions.allows?(forwarded_host[:host])
The examples I gave are using localhost, but I also confirmed this using a production environment with a configuration like the following
Rails.application.config.hosts = %w(.EXAMPLE.com)
Impact
Hackers can redirect victims to a malicious website.
Timeline: 2021-08-03 20:27:55 +0000: @tenderlove (bug triaged)
2021-09-07 20:30:35 +0000: @tenderlove (cve id added)
2021-10-05 20:37:19 +0000: @tenderlove (bug resolved)
Impact
Hackers can redirect victims to a malicious website.