Privilege escalation - Ruby

Privilege escalation - Ruby

Need

Prevention of unauthorized privilege escalation

Context

  • Usage of Ruby 2.5.0 as the programming language for development
  • Usage of Rails framework for building web applications

Description

Non compliant code

        class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]

  # GET /users
  # GET /users.json
  def index
    @users = User.all
  end

  # PATCH/PUT /users/1
  # PATCH/PUT /users/1.json
  def update
    respond_to do |format|
      if @user.update(user_params)
        format.html { redirect_to @user, notice: 'User was successfully updated.' }
        format.json { render :show, status: :ok, location: @user }
      else
        format.html { render :edit }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_user
      @user = User.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def user_params
      params.require(:user).permit(:email, :password, :role)
    end
end
        
        

The above code is a typical UsersController in a Ruby on Rails application. There's a major security vulnerability in the update action and user_params method.

In the update action, any user can update any other user's details because there is no check to see if the user making the request is allowed to update the details of the user specified by params[:id].

The user_params method is where the parameters for updating a user are whitelisted. The problem here is that the :role parameter is whitelisted. This means that any user can change their role to any value they want, including 'admin'. This allows any authenticated user to escalate their privileges to admin level.

This can be exploited by a malicious user to gain admin access and perform actions they are not authorized to do.

Steps

  • Implement proper authorization and authentication mechanisms to ensure that only authorized users can access privileged actions.
  • Use role-based access control (RBAC) to assign specific roles and permissions to users.
  • Ensure that the 'update' action is only accessible to users with the appropriate role or privilege.
  • Validate user input and sanitize any user-supplied data to prevent injection attacks.
  • Regularly update and patch the Ruby on Rails framework to address any known security vulnerabilities.
  • Perform regular security audits and penetration testing to identify and fix any potential vulnerabilities.

Compliant code

        class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]
  before_action :check_role, only: [:update]

  # GET /users
  # GET /users.json
  def index
    @users = User.all
  end

  # PATCH/PUT /users/1
  # PATCH/PUT /users/1.json
  def update
    respond_to do |format|
      if @user.update(user_params)
        format.html { redirect_to @user, notice: 'User was successfully updated.' }
        format.json { render :show, status: :ok, location: @user }
      else
        format.html { render :edit }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_user
      @user = User.find(params[:id])
    end

    # Check if the current user has the right role to perform the action
    def check_role
      unless current_user.admin?
        redirect_to root_path, alert: 'You do not have permission to perform this action.'
      end
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def user_params
      params.require(:user).permit(:email, :password)
    end
end
        
        

The updated code includes a new before_action filter, check_role, which is only applied to the update action. This filter checks if the current user has the admin role before allowing them to perform the update action. If the user is not an admin, they are redirected to the root path with an alert message.

The user_params method has also been updated to remove :role from the list of permitted parameters. This prevents standard users from being able to escalate their privileges by changing their role.

This code ensures that only users with the appropriate role or privilege can access the update action, effectively mitigating the privilege escalation vulnerability.

References