X-Received: by 10.180.14.102 with SMTP id o6mr701151wic.0.1399413163896; Tue, 06 May 2014 14:52:43 -0700 (PDT) X-BeenThere: sysa...@hostpapasupport.com Received: by 10.180.92.98 with SMTP id cl2ls224505wib.53.canary; Tue, 06 May 2014 14:52:43 -0700 (PDT) X-Received: by 10.112.26.199 with SMTP id n7mr34647401lbg.27.1399413163475; Tue, 06 May 2014 14:52:43 -0700 (PDT) Return-Path: Received: from mother.openwall.net (mother.openwall.net. [195.42.179.200]) by mx.google.com with SMTP id c10si5653989lbv.231.2014.05.06.14.52.43 for ; Tue, 06 May 2014 14:52:43 -0700 (PDT) Received-SPF: pass (google.com: domain of oss-security-return-12752-sysadmins=hostpapas...@lists.openwall.com designates 195.42.179.200 as permitted sender) client-ip=195.42.179.200; Authentication-Results: mx.google.com; spf=pass (google.com: domain of oss-security-return-12752-sysadmins=hostpapas...@lists.openwall.com designates 195.42.179.200 as permitted sender) smtp.mail=oss-security-return-12752-sysadmins=hostpapas...@lists.openwall.com; dkim=fail head...@gmail.com; dmarc=fail (p=NONE dis=NONE) header.from=gmail.com Received: (qmail 3262 invoked by uid 550); 6 May 2014 21:52:03 -0000 Mailing-List: contact oss-secu...@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: Reply-To: oss-se...@lists.openwall.com Delivered-To: mailing list oss-se...@lists.openwall.com Delivered-To: moderator for oss-se...@lists.openwall.com Received: (qmail 19756 invoked from network); 6 May 2014 16:20:28 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=date:from:to:cc:message-id:subject:mime-version:content-type; bh=30w/snFq876hcJVNExewGgpzxmAA92lhM8bKLulHXmE=; b=DO/P+MvfvR6oLCZLpco2Yz6Z/8CZYWkvT0TIpXB2nl0h4qg/lv4NCvWGk9uSszSEdT iVH/w7RZXbamrtPpZ3824JwxbQj8QvPXN+3CoHcvLwCtvQs4VApitPPzXBHTkyZkZjIb REZuHZfBJ2yFFSDdMQdu6O4hqx1ZG+dizBM/6vVk+OHAEyen9LAHTnFEG3+F/b52Xp9b l1QHx11zFXPO+G9jQZzcF+wOU90sxOvq/HzYk6Iq5bbGtcACduYNTDJ4dmDaYIsNVU/4 pY4+Q8mWzdmZ1J8nhBgX2QX/RMYif4gJw7HYQ+zBF3FWJPekCu3QSt4drUBsf2+l+B/p 1UHw== X-Received: by 10.140.46.7 with SMTP id j7mr54009qga.35.1399393216441; Tue, 06 May 2014 09:20:16 -0700 (PDT) Date: Tue, 6 May 2014 09:20:15 -0700 (PDT) From: =?UTF-8?Q?Rafael_Mendon=C3=A7a_Fran=C3=A7a?= To: rubyonrails-security@googlegroups.com Cc: oss-se...@lists.openwall.com, ruby-sec...@googlegroups.com Message-Id: <4796dbe4-a8e8-44d6-9e6d-6b78570a5d18@googlegroups.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_Part_1481_1122205.1399393215472" Subject: [oss-security] [CVE-2014-0130] Directory Traversal Vulnerability With Certain Route Configurations ------=_Part_1481_1122205.1399393215472 Content-Type: multipart/alternative; boundary="----=_Part_1482_3319689.1399393215472" ------=_Part_1482_3319689.1399393215472 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit There is a vulnerability in the 'implicit render' functionality in Ruby on Rails. This vulnerability has been assigned the CVE identifier CVE-2014-0130. Versions Affected: All Supported Not affected: None Fixed Versions: 4.1.1, 4.0.5, 3.2.18 Impact ------ The implicit render functionality allows controllers to render a template, even if there is no explicit action with the corresponding name. This module does not perform adequate input sanitization which could allow an attacker to use a specially crafted request to retrieve arbitrary files from the rails application server. In order to be vulnerable an application must specifically use globbing routes[1] in combination with the :action parameter. The purpose of the route globbing feature is to allow parameters to contain characters which would otherwise be regarded as separators, for example '/' and '.'. As these characters have semantic meaning within template filenames, it is highly unlikely that applications are deliberately combining these functions. To determine if you are vulnerable, search your application's routes files for '*action' and if you find any, use one of the work arounds below. Releases -------- The 4.1.1, 4.0.5 and 3.2.18 releases are available at the normal locations. Workarounds ----------- The simplest workaround is to simply not use globbing matches for the :action parameter. As action methods cannot contain a '/' character, the simple matching should be sufficient. So replace get 'my_url/*action', controller: 'asdf' with get 'my_url/:action', controller: 'asdf' If your application depends on this functionality, you will need to rename the route parameter and add an explicit action: get 'my_url/*template_path', controller: 'asdf', action: 'display' Then add an action which renders explicitly: def display if !params[:template_path].index('.') render file: params[:template_path] end end Note: The path check in this example may not be suitable for your application, take care Patches ------- To aid users who aren't able to upgrade immediately we have provided patches for the two supported release series. They are in git-am format and consist of a single changeset. * 4-1-directory_traversal.patch - Patch for 4.1 series * 4-0-directory_traversal.patch - Patch for 4.0 series * 3-2-directory_traversal.patch - Patch for 3.2 series Please note that only the 4.1.x, 4.0.x and 3.2.x series are supported at present. Users of earlier unsupported releases are advised to upgrade as soon as possible as we cannot guarantee the continued availability of security fixes for unsupported releases. Credits ------- Thanks to Ville Lautanala of Flowdock for reporting the vulnerability to us, and working with us on a fix. [1] http://guides.rubyonrails.org/routing.html#route-globbing-and-wildcard-segments ------=_Part_1482_3319689.1399393215472 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: 7bit
There is a vulnerability in the 'implicit render' functionality in Ruby on Rails. This vulnerability has been assigned the CVE identifier CVE-2014-0130.

Versions Affected:  All Supported
Not affected:       None
Fixed Versions:     4.1.1, 4.0.5, 3.2.18

Impact
------
The implicit render functionality allows controllers to render a template, even if there is no explicit action with the corresponding name.  This module does not perform adequate input sanitization which could allow an attacker to use a specially crafted request to retrieve arbitrary files from the rails application server.

In order to be vulnerable an application must specifically use globbing routes[1] in combination with the :action parameter.  The purpose of the route globbing feature is to allow parameters to contain characters which would otherwise be regarded as separators, for example '/' and '.'.  As these characters have semantic meaning within template filenames, it is highly unlikely that applications are deliberately combining these functions.

To determine if you are vulnerable, search your application's routes files for '*action' and if you find any, use one of the work arounds below.

Releases
--------
The 4.1.1, 4.0.5 and 3.2.18 releases are available at the normal locations.

Workarounds
-----------
The simplest workaround is to simply not use globbing matches for the :action parameter.  As action methods cannot contain a '/' character, the simple matching should be sufficient. So replace

  get 'my_url/*action', controller: 'asdf'

with

  get 'my_url/:action', controller: 'asdf'

If your application depends on this functionality, you will need to rename the route parameter and add an explicit action:

  get 'my_url/*template_path', controller: 'asdf', action: 'display'

Then add an action which renders explicitly:

  def display
    if !params[:template_path].index('.')
      render file: params[:template_path]
    end
  end

Note: The path check in this example may not be suitable for your application, take care


Patches 
------- 
To aid users who aren't able to upgrade immediately we have provided patches for the two supported release series.  They are in git-am format and consist of a single changeset. 

* 4-1-directory_traversal.patch - Patch for 4.1 series
* 4-0-directory_traversal.patch - Patch for 4.0 series
* 3-2-directory_traversal.patch - Patch for 3.2 series

Please note that only the 4.1.x, 4.0.x and 3.2.x series are supported at present.  Users of earlier unsupported releases are advised to upgrade as soon as possible as we cannot guarantee the continued availability of security fixes for unsupported releases.

Credits 
------- 
Thanks to Ville Lautanala of Flowdock for reporting the vulnerability to us, and working with us on a fix.

[1] http://guides.rubyonrails.org/routing.html#route-globbing-and-wildcard-segments
------=_Part_1482_3319689.1399393215472-- ------=_Part_1481_1122205.1399393215472 Content-Type: text/x-diff; charset=US-ASCII; name=3-2-directory_traversal.patch Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename=3-2-directory_traversal.patch X-Attachment-Id: 56645a7e-8cee-4eea-b65f-05fb076ff78a Content-ID: <56645a7e-8cee-4eea-b65f-05fb076ff78a> From 0f3b7d1a319383f743f9938e1eed00f0fba7a367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Thu, 17 Apr 2014 16:50:39 -0300 Subject: [PATCH] Only accept actions without File::SEPARATOR in the name. This will avoid directory traversal in implicit render. Fixes: CVE-2014-0130 --- actionpack/lib/abstract_controller/base.rb | 28 +++++++++++++++++++--- .../new_base/render_implicit_action_test.rb | 17 ++++++++++++- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index fd6a46f..2541125 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -112,7 +112,7 @@ module AbstractController def process(action, *args) @_action_name = action_name = action.to_s - unless action_name = method_for_action(action_name) + unless action_name = _find_action_name(action_name) raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}" end @@ -138,7 +138,7 @@ module AbstractController # available action consider actions that are also available # through other means, for example, implicit render ones. def available_action?(action_name) - method_for_action(action_name).present? + _find_action_name(action_name).present? end private @@ -182,6 +182,23 @@ module AbstractController end # Takes an action name and returns the name of the method that will + # handle the action. + # + # It checks if the action name is valid and returns false otherwise. + # + # See method_for_action for more information. + # + # ==== Parameters + # * action_name - An action name to find a method name for + # + # ==== Returns + # * string - The name of the method that handles the action + # * false - No valid method name could be found. Raise ActionNotFound. + def _find_action_name(action_name) + _valid_action_name?(action_name) && method_for_action(action_name) + end + + # Takes an action name and returns the name of the method that will # handle the action. In normal cases, this method returns the same # name as it receives. By default, if #method_for_action receives # a name that is not an action, it will look for an #action_missing @@ -203,11 +220,16 @@ module AbstractController # # ==== Returns # * string - The name of the method that handles the action - # * nil - No method name could be found. Raise ActionNotFound. + # * nil - No method name could be found. def method_for_action(action_name) if action_method?(action_name) then action_name elsif respond_to?(:action_missing, true) then "_handle_action_missing" end end + + # Checks if the action name is valid and returns false otherwise. + def _valid_action_name?(action_name) + action_name.to_s !~ Regexp.new(File::SEPARATOR) + end end end diff --git a/actionpack/test/controller/new_base/render_implicit_action_test.rb b/actionpack/test/controller/new_base/render_implicit_action_test.rb index 1e2191d..5b4885f 100644 --- a/actionpack/test/controller/new_base/render_implicit_action_test.rb +++ b/actionpack/test/controller/new_base/render_implicit_action_test.rb @@ -6,7 +6,7 @@ module RenderImplicitAction "render_implicit_action/simple/hello_world.html.erb" => "Hello world!", "render_implicit_action/simple/hyphen-ated.html.erb" => "Hello hyphen-ated!", "render_implicit_action/simple/not_implemented.html.erb" => "Not Implemented" - )] + ), ActionView::FileSystemResolver.new(File.expand_path('../../../controller', __FILE__))] def hello_world() end end @@ -33,10 +33,25 @@ module RenderImplicitAction assert_status 200 end + test "render does not traverse the file system" do + assert_raises(AbstractController::ActionNotFound) do + action_name = %w(.. .. fixtures shared).join(File::SEPARATOR) + SimpleController.action(action_name).call(Rack::MockRequest.env_for("/")) + end + end + test "available_action? returns true for implicit actions" do assert SimpleController.new.available_action?(:hello_world) assert SimpleController.new.available_action?(:"hyphen-ated") assert SimpleController.new.available_action?(:not_implemented) end + + test "available_action? does not allow File::SEPARATOR on the name" do + action_name = %w(evil .. .. path).join(File::SEPARATOR) + assert_equal false, SimpleController.new.available_action?(action_name.to_sym) + + action_name = %w(evil path).join(File::SEPARATOR) + assert_equal false, SimpleController.new.available_action?(action_name.to_sym) + end end end -- 1.9.1 ------=_Part_1481_1122205.1399393215472 Content-Type: text/x-diff; charset=US-ASCII; name=4-0-directory_traversal.patch Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename=4-0-directory_traversal.patch X-Attachment-Id: 6b03f014-1685-487e-9db3-079c0a3d7b6f Content-ID: <6b03f014-1685-487e-9db3-079c0a3d7b6f> From a8ecbfde294a2ec53f034e729d663cb266dc9e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Thu, 17 Apr 2014 16:50:39 -0300 Subject: [PATCH] Only accept actions without File::SEPARATOR in the name. This will avoid directory traversal in implicit render. Fixes: CVE-2014-0130 --- actionpack/lib/abstract_controller/base.rb | 28 +++++++++++++++++++--- .../new_base/render_implicit_action_test.rb | 17 ++++++++++++- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index af5de81..26f8160 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -127,7 +127,7 @@ module AbstractController def process(action, *args) @_action_name = action_name = action.to_s - unless action_name = method_for_action(action_name) + unless action_name = _find_action_name(action_name) raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}" end @@ -160,7 +160,7 @@ module AbstractController # ==== Returns # * TrueClass, FalseClass def available_action?(action_name) - method_for_action(action_name).present? + _find_action_name(action_name).present? end private @@ -204,6 +204,23 @@ module AbstractController end # Takes an action name and returns the name of the method that will + # handle the action. + # + # It checks if the action name is valid and returns false otherwise. + # + # See method_for_action for more information. + # + # ==== Parameters + # * action_name - An action name to find a method name for + # + # ==== Returns + # * string - The name of the method that handles the action + # * false - No valid method name could be found. Raise ActionNotFound. + def _find_action_name(action_name) + _valid_action_name?(action_name) && method_for_action(action_name) + end + + # Takes an action name and returns the name of the method that will # handle the action. In normal cases, this method returns the same # name as it receives. By default, if #method_for_action receives # a name that is not an action, it will look for an #action_missing @@ -225,7 +242,7 @@ module AbstractController # # ==== Returns # * string - The name of the method that handles the action - # * nil - No method name could be found. Raise ActionNotFound. + # * nil - No method name could be found. def method_for_action(action_name) if action_method?(action_name) action_name @@ -233,5 +250,10 @@ module AbstractController "_handle_action_missing" end end + + # Checks if the action name is valid and returns false otherwise. + def _valid_action_name?(action_name) + action_name.to_s !~ Regexp.new(File::SEPARATOR) + end end end diff --git a/actionpack/test/controller/new_base/render_implicit_action_test.rb b/actionpack/test/controller/new_base/render_implicit_action_test.rb index 1e2191d..5b4885f 100644 --- a/actionpack/test/controller/new_base/render_implicit_action_test.rb +++ b/actionpack/test/controller/new_base/render_implicit_action_test.rb @@ -6,7 +6,7 @@ module RenderImplicitAction "render_implicit_action/simple/hello_world.html.erb" => "Hello world!", "render_implicit_action/simple/hyphen-ated.html.erb" => "Hello hyphen-ated!", "render_implicit_action/simple/not_implemented.html.erb" => "Not Implemented" - )] + ), ActionView::FileSystemResolver.new(File.expand_path('../../../controller', __FILE__))] def hello_world() end end @@ -33,10 +33,25 @@ module RenderImplicitAction assert_status 200 end + test "render does not traverse the file system" do + assert_raises(AbstractController::ActionNotFound) do + action_name = %w(.. .. fixtures shared).join(File::SEPARATOR) + SimpleController.action(action_name).call(Rack::MockRequest.env_for("/")) + end + end + test "available_action? returns true for implicit actions" do assert SimpleController.new.available_action?(:hello_world) assert SimpleController.new.available_action?(:"hyphen-ated") assert SimpleController.new.available_action?(:not_implemented) end + + test "available_action? does not allow File::SEPARATOR on the name" do + action_name = %w(evil .. .. path).join(File::SEPARATOR) + assert_equal false, SimpleController.new.available_action?(action_name.to_sym) + + action_name = %w(evil path).join(File::SEPARATOR) + assert_equal false, SimpleController.new.available_action?(action_name.to_sym) + end end end -- 1.9.1 ------=_Part_1481_1122205.1399393215472 Content-Type: text/x-diff; charset=US-ASCII; name=4-1-directory_traversal.patch Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename=4-1-directory_traversal.patch X-Attachment-Id: 89c9d0a9-4927-4eef-af65-df739b756809 Content-ID: <89c9d0a9-4927-4eef-af65-df739b756809> From 529720df0193991c66350154e8bed5a59dbaacfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Thu, 17 Apr 2014 16:50:39 -0300 Subject: [PATCH] Only accept actions without File::SEPARATOR in the name. This will avoid directory traversal in implicit render. Fixes: CVE-2014-0130 --- actionpack/lib/abstract_controller/base.rb | 28 +++++++++++++++++++--- .../new_base/render_implicit_action_test.rb | 17 ++++++++++++- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index af5de81..26f8160 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -127,7 +127,7 @@ module AbstractController def process(action, *args) @_action_name = action_name = action.to_s - unless action_name = method_for_action(action_name) + unless action_name = _find_action_name(action_name) raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}" end @@ -160,7 +160,7 @@ module AbstractController # ==== Returns # * TrueClass, FalseClass def available_action?(action_name) - method_for_action(action_name).present? + _find_action_name(action_name).present? end private @@ -204,6 +204,23 @@ module AbstractController end # Takes an action name and returns the name of the method that will + # handle the action. + # + # It checks if the action name is valid and returns false otherwise. + # + # See method_for_action for more information. + # + # ==== Parameters + # * action_name - An action name to find a method name for + # + # ==== Returns + # * string - The name of the method that handles the action + # * false - No valid method name could be found. Raise ActionNotFound. + def _find_action_name(action_name) + _valid_action_name?(action_name) && method_for_action(action_name) + end + + # Takes an action name and returns the name of the method that will # handle the action. In normal cases, this method returns the same # name as it receives. By default, if #method_for_action receives # a name that is not an action, it will look for an #action_missing @@ -225,7 +242,7 @@ module AbstractController # # ==== Returns # * string - The name of the method that handles the action - # * nil - No method name could be found. Raise ActionNotFound. + # * nil - No method name could be found. def method_for_action(action_name) if action_method?(action_name) action_name @@ -233,5 +250,10 @@ module AbstractController "_handle_action_missing" end end + + # Checks if the action name is valid and returns false otherwise. + def _valid_action_name?(action_name) + action_name.to_s !~ Regexp.new(File::SEPARATOR) + end end end diff --git a/actionpack/test/controller/new_base/render_implicit_action_test.rb b/actionpack/test/controller/new_base/render_implicit_action_test.rb index 1e2191d..5b4885f 100644 --- a/actionpack/test/controller/new_base/render_implicit_action_test.rb +++ b/actionpack/test/controller/new_base/render_implicit_action_test.rb @@ -6,7 +6,7 @@ module RenderImplicitAction "render_implicit_action/simple/hello_world.html.erb" => "Hello world!", "render_implicit_action/simple/hyphen-ated.html.erb" => "Hello hyphen-ated!", "render_implicit_action/simple/not_implemented.html.erb" => "Not Implemented" - )] + ), ActionView::FileSystemResolver.new(File.expand_path('../../../controller', __FILE__))] def hello_world() end end @@ -33,10 +33,25 @@ module RenderImplicitAction assert_status 200 end + test "render does not traverse the file system" do + assert_raises(AbstractController::ActionNotFound) do + action_name = %w(.. .. fixtures shared).join(File::SEPARATOR) + SimpleController.action(action_name).call(Rack::MockRequest.env_for("/")) + end + end + test "available_action? returns true for implicit actions" do assert SimpleController.new.available_action?(:hello_world) assert SimpleController.new.available_action?(:"hyphen-ated") assert SimpleController.new.available_action?(:not_implemented) end + + test "available_action? does not allow File::SEPARATOR on the name" do + action_name = %w(evil .. .. path).join(File::SEPARATOR) + assert_equal false, SimpleController.new.available_action?(action_name.to_sym) + + action_name = %w(evil path).join(File::SEPARATOR) + assert_equal false, SimpleController.new.available_action?(action_name.to_sym) + end end end -- 1.9.1 ------=_Part_1481_1122205.1399393215472--