[prev in list] [next in list] [prev in thread] [next in thread] 

List:       oss-security
Subject:    [oss-security] [CVE-2019-5420] Possible Remote Code Execution Exploit in Rails Development Mode
From:       Aaron Patterson <tenderlove () ruby-lang ! org>
Date:       2019-03-13 17:14:54
Message-ID: 20190313171454.GA90773 () TC-275 ! local
[Download RAW message or body]

[Attachment #2 (multipart/mixed)]


There is a possible a possible remote code executing exploit in Rails when in
development mode. This vulnerability has been assigned the CVE identifier
CVE-2019-5420.

Versions Affected:  6.0.0.X, 5.2.X.
Not affected:       None.
Fixed Versions:     6.0.0.beta3, 5.2.2.1

Impact
------
With some knowledge of a target application it is possible for an attacker to
guess the automatically generated development mode secret token.  This secret
token can be used in combination with other Rails internals to escalate to a
remote code execution exploit.

All users running an affected release should either upgrade or use one of the
workarounds immediately.

Releases
--------
The 6.0.0.beta3 and 5.2.2.1 releases are available at the normal locations.

Workarounds
-----------
This issue can be mitigated by specifying a secret key in development mode.
In "config/environments/development.rb" add this:

  config.secret_key_base = SecureRandom.hex(64)


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.

* 6-0-railties-dev-mode-token.patch - Patch for 6.0 series
* 5-2-railties-dev-mode-token.patch - Patch for 5.2 series

Please note that only the 5.2.x, 5.1.x, 5.0.x, and 4.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 ooooooo_q

-- 
Aaron Patterson
http://tenderlovemaking.com/

["5-2-railties-dev-mode-token.patch" (text/plain)]

From 7f5ccda38bfecbe0bf00f15e5b8f5e40d52ab3f1 Mon Sep 17 00:00:00 2001
From: Aaron Patterson <aaron.patterson@gmail.com>
Date: Sun, 10 Mar 2019 16:37:46 -0700
Subject: [PATCH] Fix possible dev mode RCE

If the secret_key_base is nil in dev or test generate a key from random
bytes and store it in a tmp file. This prevents the app developers from
having to share / checkin the secret key for dev / test but also
maintains a key between app restarts in dev/test.

[CVE-2019-5420]

Co-Authored-By: eileencodes <eileencodes@gmail.com>
Co-Authored-By: John Hawthorn <john@hawthorn.email>
---
 .../middleware/session/cookie_store.rb        |  7 +++---
 railties/lib/rails/application.rb             | 19 ++++++++++++++--
 .../test/application/configuration_test.rb    | 22 ++++++++++++++++++-
 railties/test/isolation/abstract_unit.rb      |  1 +
 4 files changed, 43 insertions(+), 6 deletions(-)

diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb \
b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index \
                4ea96196d3..b7475d3682 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -29,9 +29,10 @@
     #
     #   Rails.application.config.session_store :cookie_store, key: '_your_app_session'
     #
-    # By default, your secret key base is derived from your application name in
-    # the test and development environments. In all other environments, it is stored
-    # encrypted in the <tt>config/credentials.yml.enc</tt> file.
+    # In the development and test environments your application's secret key base is
+    # generated by Rails and stored in a temporary file in \
<tt>tmp/development_secret.txt</tt>. +    # In all other environments, it is stored encrypted \
in the +    # <tt>config/credentials.yml.enc</tt> file.
     #
     # If your application was not updated to Rails 5.2 defaults, the secret_key_base
     # will be found in the old <tt>config/secrets.yml</tt> file.
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index e346d5cc3a..6a30e8cfa0 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -426,8 +426,8 @@ def secrets=(secrets) #:nodoc:
     # then credentials.secret_key_base, and finally secrets.secret_key_base. For most \
applications,  # the correct place to store it is in the encrypted credentials file.
     def secret_key_base
-      if Rails.env.test? || Rails.env.development?
-        secrets.secret_key_base || Digest::MD5.hexdigest(self.class.name)
+      if Rails.env.development? || Rails.env.test?
+        secrets.secret_key_base ||= generate_development_secret
       else
         validate_secret_key_base(
           ENV["SECRET_KEY_BASE"] || credentials.secret_key_base || secrets.secret_key_base
@@ -588,6 +588,21 @@ def validate_secret_key_base(secret_key_base)
 
     private
 
+      def generate_development_secret
+        if secrets.secret_key_base.nil?
+          key_file = Rails.root.join("tmp/development_secret.txt")
+
+          if !File.exist?(key_file)
+            random_key = SecureRandom.hex(64)
+            File.binwrite(key_file, random_key)
+          end
+
+          secrets.secret_key_base = File.binread(key_file)
+        end
+
+        secrets.secret_key_base
+      end
+
       def build_request(env)
         req = super
         env["ORIGINAL_FULLPATH"] = req.fullpath
diff --git a/railties/test/application/configuration_test.rb \
b/railties/test/application/configuration_test.rb index 293a1a7dbd..68c2199aba 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -513,6 +513,27 @@ def index
     end
 
 
+    test "application will generate secret_key_base in tmp file if blank in development" do
+      app_file "config/initializers/secret_token.rb", <<-RUBY
+        Rails.application.credentials.secret_key_base = nil
+      RUBY
+
+      app "development"
+
+      assert_not_nil app.secrets.secret_key_base
+      assert File.exist?(app_path("tmp/development_secret.txt"))
+    end
+
+    test "application will not generate secret_key_base in tmp file if blank in production" do
+      app_file "config/initializers/secret_token.rb", <<-RUBY
+        Rails.application.credentials.secret_key_base = nil
+      RUBY
+
+      assert_raises ArgumentError do
+        app "production"
+      end
+    end
+
     test "raises when secret_key_base is blank" do
       app_file "config/initializers/secret_token.rb", <<-RUBY
         Rails.application.credentials.secret_key_base = nil
@@ -550,7 +571,6 @@ def index
 
     test "application verifier can build different verifiers" do
       make_basic_app do |application|
-        application.credentials.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33"
         application.config.session_store :disabled
       end
 
diff --git a/railties/test/isolation/abstract_unit.rb \
b/railties/test/isolation/abstract_unit.rb index 6568a356d6..fe850d45ec 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -155,6 +155,7 @@ def self.name; "RailtiesTestApp"; end
       @app.config.active_support.deprecation = :log
       @app.config.active_support.test_order = :random
       @app.config.log_level = :info
+      @app.secrets.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33"
 
       yield @app if block_given?
       @app.initialize!
-- 
2.21.0


["6-0-railties-dev-mode-token.patch" (text/plain)]

From 4c743587ad6a31908503ab317e37d70361d49e66 Mon Sep 17 00:00:00 2001
From: Aaron Patterson <aaron.patterson@gmail.com>
Date: Sun, 10 Mar 2019 16:37:46 -0700
Subject: [PATCH] Fix possible dev mode RCE

If the secret_key_base is nil in dev or test generate a key from random
bytes and store it in a tmp file. This prevents the app developers from
having to share / checkin the secret key for dev / test but also
maintains a key between app restarts in dev/test.

[CVE-2019-5420]

Co-Authored-By: eileencodes <eileencodes@gmail.com>
Co-Authored-By: John Hawthorn <john@hawthorn.email>
---
 .../middleware/session/cookie_store.rb        |  7 +++---
 railties/lib/rails/application.rb             | 19 ++++++++++++++--
 .../test/application/configuration_test.rb    | 22 ++++++++++++++++++-
 railties/test/isolation/abstract_unit.rb      |  1 +
 4 files changed, 43 insertions(+), 6 deletions(-)

diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb \
b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index \
                02ccfbc81a..7c43c781c7 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -24,9 +24,10 @@
     #
     #   Rails.application.config.session_store :cookie_store, key: '_your_app_session'
     #
-    # By default, your secret key base is derived from your application name in
-    # the test and development environments. In all other environments, it is stored
-    # encrypted in the <tt>config/credentials.yml.enc</tt> file.
+    # In the development and test environments your application's secret key base is
+    # generated by Rails and stored in a temporary file in \
<tt>tmp/development_secret.txt</tt>. +    # In all other environments, it is stored encrypted \
in the +    # <tt>config/credentials.yml.enc</tt> file.
     #
     # If your application was not updated to Rails 5.2 defaults, the secret_key_base
     # will be found in the old <tt>config/secrets.yml</tt> file.
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index fbad3e5db3..558a4d1f57 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -415,8 +415,8 @@ def secrets
     # then credentials.secret_key_base, and finally secrets.secret_key_base. For most \
applications,  # the correct place to store it is in the encrypted credentials file.
     def secret_key_base
-      if Rails.env.test? || Rails.env.development?
-        secrets.secret_key_base || Digest::MD5.hexdigest(self.class.name)
+      if Rails.env.development? || Rails.env.test?
+        secrets.secret_key_base ||= generate_development_secret
       else
         validate_secret_key_base(
           ENV["SECRET_KEY_BASE"] || credentials.secret_key_base || secrets.secret_key_base
@@ -581,6 +581,21 @@ def validate_secret_key_base(secret_key_base)
 
     private
 
+      def generate_development_secret
+        if secrets.secret_key_base.nil?
+          key_file = Rails.root.join("tmp/development_secret.txt")
+
+          if !File.exist?(key_file)
+            random_key = SecureRandom.hex(64)
+            File.binwrite(key_file, random_key)
+          end
+
+          secrets.secret_key_base = File.binread(key_file)
+        end
+
+        secrets.secret_key_base
+      end
+
       def build_request(env)
         req = super
         env["ORIGINAL_FULLPATH"] = req.fullpath
diff --git a/railties/test/application/configuration_test.rb \
b/railties/test/application/configuration_test.rb index 73773602a3..377dab1a13 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -596,6 +596,27 @@ def index
       assert_equal "some_value", verifier.verify(message)
     end
 
+    test "application will generate secret_key_base in tmp file if blank in development" do
+      app_file "config/initializers/secret_token.rb", <<-RUBY
+        Rails.application.credentials.secret_key_base = nil
+      RUBY
+
+      app "development"
+
+      assert_not_nil app.secrets.secret_key_base
+      assert File.exist?(app_path("tmp/development_secret.txt"))
+    end
+
+    test "application will not generate secret_key_base in tmp file if blank in production" do
+      app_file "config/initializers/secret_token.rb", <<-RUBY
+        Rails.application.credentials.secret_key_base = nil
+      RUBY
+
+      assert_raises ArgumentError do
+        app "production"
+      end
+    end
+
     test "raises when secret_key_base is blank" do
       app_file "config/initializers/secret_token.rb", <<-RUBY
         Rails.application.credentials.secret_key_base = nil
@@ -619,7 +640,6 @@ def index
 
     test "application verifier can build different verifiers" do
       make_basic_app do |application|
-        application.credentials.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33"
         application.config.session_store :disabled
       end
 
diff --git a/railties/test/isolation/abstract_unit.rb \
b/railties/test/isolation/abstract_unit.rb index 3f1638a516..b10701aa55 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -226,6 +226,7 @@ def self.name; "RailtiesTestApp"; end
       @app.config.session_store :cookie_store, key: "_myapp_session"
       @app.config.active_support.deprecation = :log
       @app.config.log_level = :info
+      @app.secrets.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33"
 
       yield @app if block_given?
       @app.initialize!
-- 
2.21.0


["signature.asc" (application/pgp-signature)]

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic