Information Exposure Affecting activeadmin package, versions <2.12.0
Threat Intelligence
Exploit Maturity
Proof of concept
EPSS
0.09% (39th
percentile)
Do your applications use this vulnerable package?
In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.
Test your applications- Snyk ID SNYK-RUBY-ACTIVEADMIN-6129507
- published 17 Dec 2023
- disclosed 16 Dec 2023
- credit emilong
Introduced: 16 Dec 2023
CVE-2023-50448 Open this link in a new tabHow to fix?
Upgrade activeadmin
to version 2.12.0 or higher.
Overview
Affected versions of this package are vulnerable to Information Exposure due to a concurrency issue that results in a shared variable not being properly synchronized. An attacker with access to the same ActiveAdmin application can obtain private data intended for another user by timing their request to coincide with the victim's CSV export action or by frequently requesting CSV exports in the hope of a concurrent request occurring.
PoC
# frozen_string_literal: true
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
# Use local changes or ActiveAdmin master.
if ENV["ACTIVE_ADMIN_PATH"]
gem "activeadmin", path: ENV["ACTIVE_ADMIN_PATH"], require: false
else
gem "activeadmin", github: "activeadmin/activeadmin", require: false
end
# Change Rails version if necessary.
gem "rails", "6.1.4"
gem "sprockets", "3.7.2"
gem "sassc-rails", "2.1.2"
gem "sqlite3", "1.4.2", platform: :mri
gem "activerecord-jdbcsqlite3-adapter", "61.0", platform: :jruby
end
require "active_record"
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :active_admin_comments, force: true do |_t|
end
create_table :users, force: true do |t|
t.string :full_name
end
end
require "action_controller/railtie"
require "action_view/railtie"
require "active_admin"
class TestApp < Rails::Application
config.root = __dir__
config.session_store :cookie_store, key: "cookie_store_key"
secrets.secret_token = "secret_token"
secrets.secret_key_base = "secret_key_base"
config.eager_load = false
config.logger = Logger.new($stdout)
config.hosts = "www.example.com"
end
class ApplicationController < ActionController::Base
include Rails.application.routes.url_helpers
end
class User < ActiveRecord::Base
end
class TestSpy
attr_reader :count
def initialize
@count = 0
end
def increment
@count += 1
end
def reset
@count = 0
end
end
TEST_SPY = TestSpy.new
ActiveAdmin.setup do |config|
# Authentication disabled by default. Override if necessary.
config.authentication_method = false
config.current_user_method = false
end
Rails.application.initialize!
ActiveAdmin.register_page "Dashboard" do
menu priority: 1, label: proc { I18n.t("active_admin.dashboard") }
content do
"Test Me"
end
end
ActiveAdmin.register User do
actions :index
controller do
def find_collection(*)
TEST_SPY.increment
# Rails.logger.debug { ">> FETCHING COLLECTION\n#{caller.join("\n")}" }
super
end
end
end
Rails.application.routes.draw do
ActiveAdmin.routes(self)
end
require "minitest/autorun"
require "rack/test"
require "rails/test_help"
# Replace this with the code necessary to make your test fail.
class BugTest < ActionDispatch::IntegrationTest
def test_csv_fetches_collection_once
user = User.create! full_name: 'John Doe'
TEST_SPY.reset
get admin_users_url(format: :csv)
csv_response_body = response.body.delete('')
assert_equal "Id,Full name\n#{user.id},John Doe\n", csv_response_body
assert_response :success
assert_equal 1, TEST_SPY.count
end
def test_html_fetches_collection_once
User.create! full_name: 'John Doe'
TEST_SPY.reset
get admin_users_url
assert_match /John Doe/, response.body
assert_response :success
assert_equal 1, TEST_SPY.count
end
private
def app
Rails.application
end
end
References
CVSS Scores
version 3.1