Comments:" Ruby on Rails vulnerable to mass assignment and SQL injection "
URL:http://www.zweitag.de/en/blog/ruby-on-rails-vulnerable-to-mass-assignment-and-sql-injection
During the last weeks Ruby on Rails has been hit by several security vulnerabilites. As with all bigger open source projects, it is up to the community to spot and fix such issues. Last week I notified the Ruby on Rails security team about a huge vulnerability that I spotted in the latest stable release of Rails and its related gems. As a result the Rails core team published a security advisory today, urging users to upgrade the json gem to the latest stable release.
Here’s the gist: The default JSON parser can be used to inject malicious objects into the params hash of a Rails application. This allows for tampering with ActiveRecord::Base
functionality like dynamic finders and attribute assignment, eventually leading to mass assignment of blacklisted attributes or even SQL injection.
Besides deserializing simple data types, the default JSON parser used by Rails supports deserialization of more complex classes. When parsing a JSON string, the parser honors a special key called json_class
. It will try to instantiate this class by calling .json_create
on it. Consider the following example:
This mechanism can be used to deserialize JSON into instances of more complex Ruby classes. This isn’t a problem in itself, as nearly no class provided by the standard Rails stack implements a .json_create
method, and thus no Rails internal class can be instantiated this way (as opposed to the YAML parser vulnerability, which allowed to instantiate every class). But unfortunately, json >= 1.6.7
ships with a class called JSON::GenericObject
which is basically an OpenStruct
descendant class:
As you can see above, this class can be used to ‘fake’ method return values: If I’d expect to work on a String
somewhere in my code but actually work on an JSON::GenericObject
instance, methods like #strip
don’t behave as assumed. An attacker can use this behavior to trick Rails into accepting values for mass assignment protected attributes. This works by passing a nested data structure consisting of JSON::GenericObject
instances to a model’s initializer. Take a look at an example from my Blog-in-10-minutes app:
As you can see, I’ve created a Post
instance and directly assigned a protected attribute — that’s bad. Rails’ mass assignment protection errorneously thinks it always works on simple data types like strings and hashes. It tries to convert the supplied hash keys to strings by calling #stringify_keys
on the hash. Afterwards it rejects each attribute that is on the mass assignment blacklist by calling #reject
on the hash. If I pass the above data structure to Post.create
, all those method calls are acutally handled by my JSON::GenericObject
, and Rails thinks it deals with a sanitized hash even though I can freely determine the value of all attributes.
This mechanism works at least for ActiveRecord and Mongoid, other ORMs untested. ActiveRecord is even vulnerable to SQL injection: The abstract database adapter’s ID quoting can be circumvented by providing “pre-quoted” arguments:
I was able to verify this vulnerability remotely against a Rails 3.2.11 app consisting of a simple scaffold. Due to the severity of this issue, all Rails applications should be updated to use the latest stable release of json immediately. Your application is at risk!
Thanks to Michael Koziarski from Rails’ security team and Aaron Patterson for the constructive collaboration on this issue.