CVE-2014-1985: Improper Input Validation (CWE-20)
value: Open redirect vulnerability in the redirect_back_or_default function in app/controllers/application_controller.rb in Redmine before 2.4.5 and 2.5.x before 2.5.1 allows remote attackers to redirect users to arbitrary web sites and conduct phishing attacks via a URL in the back url (back_url parameter).
CWE description: The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
Published: 2014-04-11T14:55Z
Repository: https://github.com/redmine/redmine
Language: Ruby
Files changed: app/controllers/application_controller.rb, test/functional/account_controller_test.rb
Commit date: 2014-03-29 14:32:47 +0000
Commit message: Fixed back url verification (#16466).

git-svn-id: http://svn.redmine.org/redmine/trunk@13018 e93f8b46-1217-0410-a6f0-8f06a7374b81

Diff: diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 43257b2bf..1e251c2a4 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -377,11 +377,11 @@ class ApplicationController < ActionController::Base
     back_url = params[:back_url].to_s
     if back_url.present?
       begin
         uri = URI.parse(back_url)
         # do not redirect user to another host or to the login or register page
-        if (uri.relative? || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
+        if ((uri.relative? && back_url.match(%r{\A/\w})) || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
           redirect_to(back_url)
           return
         end
       rescue URI::InvalidURIError
         logger.warn("Could not redirect to invalid URL #{back_url}")
diff --git a/test/functional/account_controller_test.rb b/test/functional/account_controller_test.rb
index 35074bd4e..e9156957c 100644
--- a/test/functional/account_controller_test.rb
+++ b/test/functional/account_controller_test.rb
@@ -64,12 +64,18 @@ class AccountControllerTest < ActionController::TestCase
     post :login, :username => 'jsmith', :password => 'jsmith', :back_url => 'http://test.host/issues/show/1'
     assert_redirected_to '/issues/show/1'
   end
 
   def test_login_should_not_redirect_to_another_host
-    post :login, :username => 'jsmith', :password => 'jsmith', :back_url => 'http://test.foo/fake'
-    assert_redirected_to '/my/page'
+    back_urls = [
+      'http://test.foo/fake',
+      '//test.foo/fake'
+    ]
+    back_urls.each do |back_url|
+      post :login, :username => 'jsmith', :password => 'jsmith', :back_url => back_url
+      assert_redirected_to '/my/page'
+    end
   end
 
   def test_login_with_wrong_password
     post :login, :username => 'admin', :password => 'bad'
     assert_response :success

Vulnerable code:         if (uri.relative? || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
    post :login, :username => 'jsmith', :password => 'jsmith', :back_url => 'http://test.foo/fake'
    assert_redirected_to '/my/page'
Fixed code:         if ((uri.relative? && back_url.match(%r{\A/\w})) || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
    back_urls = [
      'http://test.foo/fake',
      '//test.foo/fake'
    ]
    back_urls.each do |back_url|
      post :login, :username => 'jsmith', :password => 'jsmith', :back_url => back_url
      assert_redirected_to '/my/page'
    end