Openwall GNU/*/Linux - a small security-enhanced Linux distro for servers
[<prev] [next>] [day] [month] [year] [list]
Date: Mon, 11 Feb 2013 10:25:23 -0800
From: Aaron Patterson <tenderlove@...y-lang.org>
To: rubyonrails-security@...glegroups.com, oss-security@...ts.openwall.com
Subject: Serialized Attributes YAML Vulnerability with Rails 2.3 and 3.0
 [CVE-2013-0277]

Serialized Attributes YAML Vulnerability with Rails 2.3 and 3.0

There is a vulnerability in the serialized attribute handling code in Ruby on Rails 2.3 and 3.0, applications which allow users to directly assign to the serialized fields in their models are at risk of Denial of Service or Remote Code Execution vulnerabilities. This vulnerability has been assigned the CVE identifier CVE-2013-0277.

Versions Affected:  2.3.x, 3.0.x and all earlier versions
Not affected:       3.1.0 and Above
Fixed Versions:     2.3.17

Impact 
------ 
The +serialize+ helper in Active Record allows developers to store various objects serialized to a BLOB column in the database.  The objects are serialized and deserialized using YAML.  If developers allow their users to directly provide values for this attribute, an attacker could use a specially crafted request to cause the application to deserialize arbitrary YAML. 

Vulnerable applications will have models similar to this:

  class Post < ActiveRecord::Base
    serialize :tags
  end

and will allow foreign input to be directly assigned to the serialized column like this:

  post = Post.new
  post.tags = params[:tags]

All users running an affected release should either apply one of the patches or use one of the work arounds immediately. 

Releases 
-------- 
The 2.3.17 release is available in the normal locations.

In accordance with our maintenance policy, there will be no new release of Ruby on Rails 3.0 to address this vulnerability.  The patches included below have been pushed to the relevant branches in git.

Workarounds 
----------- 
To work around this issue, you must ensure that users cannot assign directly to the serialized column.  For example if you have a model Post which serializes an array of tags you should use attr_accessible to prevent attackers from changing these values directly:

  class Post < ActiveRecord::Base
    serialize :tags
    # because :tags isn't included in the accessible list, it will be protected from assignment by attackers.
    attr_accessible :title, :content
  end

Note: There are additional security concerns caused by allowing your users to directly provide values for a serialized attribute like this. You should consider making this change even if you apply the patches. 

Patches 
------- 
To aid users who are still running 2.3 or 3.0, we have included patches against this vulnerability.  They are in git-am format and consist of a single changeset. 

* 2-3-serialize.patch - Patch for 2.3 series 
* 3-0-serialize.patch - Patch for 3.0 series 

Please note that only the 3.1.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 Tobias Kraze for reporting this issue to us and working with us on the fix.

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

From d4a53b2e02106c6734bbfea2a0e209febd5f36bd Mon Sep 17 00:00:00 2001
From: Tobias Kraze <tobias@...ze.eu>
Date: Fri, 8 Feb 2013 12:52:10 +0100
Subject: [PATCH] fix serialization vulnerability

---
 .../lib/active_record/attribute_methods.rb         |   17 ++++++++++++++++-
 activerecord/test/cases/base_test.rb               |    6 ++++++
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 22630b3..ff71b42 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -80,7 +80,9 @@ module ActiveRecord
           end
 
           unless instance_method_already_implemented?("#{name}=")
-            if create_time_zone_conversion_attribute?(name, column)
+            if self.serialized_attributes[name]
+              define_write_method_for_serialized_attribute(name)
+            elsif create_time_zone_conversion_attribute?(name, column)
               define_write_method_for_time_zone_conversion(name)
             else  
               define_write_method(name.to_sym)
@@ -184,6 +186,19 @@ module ActiveRecord
         def define_write_method(attr_name)
           evaluate_attribute_method attr_name, "def #{attr_name}=(new_value);write_attribute('#{attr_name}', new_value);end", "#{attr_name}="
         end
+
+        # Defined for all serialized attributes. Disallows assigning already serialized YAML.
+        def define_write_method_for_serialized_attribute(attr_name)
+          method_body = <<-EOV
+            def #{attr_name}=(value)
+              if value.is_a?(String) and value =~ /^---/
+                raise ActiveRecordError, "You tried to assign already serialized content to #{attr_name}. This is disabled due to security issues."
+              end
+              write_attribute(:#{attr_name}, value)
+            end
+          EOV
+          evaluate_attribute_method attr_name, method_body, "#{attr_name}="
+        end
         
         # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
         # This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 242be2a..f23894e 100755
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1499,6 +1499,12 @@ class BasicsTest < ActiveRecord::TestCase
     assert_nil topic.content
   end
 
+  def test_should_raise_exception_on_assigning_already_serialized_content
+    topic = Topic.new
+    serialized_content = %w[foo bar].to_yaml
+    assert_raise(ActiveRecord::ActiveRecordError) { topic.content = serialized_content }
+  end
+
   def test_should_raise_exception_on_serialized_attribute_with_type_mismatch
     myobj = MyObject.new('value1', 'value2')
     topic = Topic.new(:content => myobj)
-- 
1.7.9.5


From 13b1b381de048e00f06fbab410d9d3ecc26460b9 Mon Sep 17 00:00:00 2001
From: Tobias Kraze <tobias@...ze.eu>
Date: Fri, 8 Feb 2013 12:52:10 +0100
Subject: [PATCH] fix serialization vulnerability

---
 .../lib/active_record/attribute_methods/write.rb   |    9 ++++++++-
 activerecord/test/cases/base_test.rb               |    6 ++++++
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index 3c4dab3..4684c4b 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -10,7 +10,14 @@ module ActiveRecord
       module ClassMethods
         protected
           def define_method_attribute=(attr_name)
-            if attr_name =~ /^[a-zA-Z_]\w*[!?=]?$/
+            if self.serialized_attributes[attr_name]
+              generated_attribute_methods.send(:define_method, "#{attr_name}=") do |new_value|
+                if new_value.is_a?(String) and new_value =~ /^---/
+                  raise ActiveRecordError, "You tried to assign already serialized content to #{attr_name}. This is disabled due to security issues."
+                end
+                write_attribute(attr_name, new_value)
+              end
+            elsif attr_name =~ /^[a-zA-Z_]\w*[!?=]?$/
               generated_attribute_methods.module_eval("def #{attr_name}=(new_value); write_attribute('#{attr_name}', new_value); end", __FILE__, __LINE__)
             else
               generated_attribute_methods.send(:define_method, "#{attr_name}=") do |new_value|
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 0894c7d..eb39c10 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1040,6 +1040,12 @@ class BasicsTest < ActiveRecord::TestCase
     assert_nil topic.content
   end
 
+  def test_should_raise_exception_on_assigning_already_serialized_content
+    topic = Topic.new
+    serialized_content = %w[foo bar].to_yaml
+    assert_raise(ActiveRecord::ActiveRecordError) { topic.content = serialized_content }
+  end
+
   def test_should_raise_exception_on_serialized_attribute_with_type_mismatch
     myobj = MyObject.new('value1', 'value2')
     topic = Topic.new(:content => myobj)
-- 
1.7.9.5



[ CONTENT OF TYPE application/pgp-signature SKIPPED ]

Powered by blists - more mailing lists

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Powered by Openwall GNU/*/Linux - Powered by OpenVZ