Welcome back to “Programming Thursdays”! In a

\[previous week\]

(/posts/2023/02/ruby-programming-language-the-red-teamers-elegant-powerhouse/), we explored the world of Ruby programming and its basic concepts. This week, we’re diving deeper into the world of web development with Rails, a robust framework built on Ruby. In this article, we’ll uncover Rails’ best practices and tips for efficient web development, while focusing on the specific areas where developers make mistakes—giving us a significant edge when identifying and exploiting weaknesses.

Rails is famous for its “convention over configuration” philosophy. For a red teamer, “convention” means predictable file paths, standard behavior, and common pitfalls, which is a goldmine for reconnaissance and exploitation.


1. The MVC Pattern: The Skeleton of the App

Rails follows the Model-View-Controller (MVC) pattern. Understanding this is key to efficient auditing.

  1. Model (app/models): The data and business logic. This is where you look for weak validation or SQL injection in custom scopes (e.g., User.where("name like '#{params[:name]}'")).
  2. View (app/views): The user interface. This is where you look for XSS via raw, html_safe, or improper usage of link_to leading to javascript: execution.
  3. Controller (app/controllers): The intermediary. This is where you look for authorization bypasses (missing before_action), insecure direct object references (IDOR), and mass assignment.

2. Mass Assignment and Strong Parameters

In the old days of Rails, you could update a user object by passing a whole hash of parameters. This led to Mass Assignment vulnerabilities where an attacker could inject user[admin]=true into a POST request.

The Fix: Strong Parameters

Modern Rails uses the permit method in the controller to whitelist allowed fields.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# app/controllers/users_controller.rb
class UsersController < ApplicationController
  def update
    @user = User.find(params[:id])
    # Safe: Only permits specific fields
    if @user.update(user_params)
      redirect_to @user
    end
  end

  private

  def user_params
    params.require(:user).permit(:username, :email, :password)
  end
end

Red Team Tip: Look for controllers where permit! (with an exclamation mark) is used. This is the “lazy developer” switch that allows all parameters to be passed, effectively re-introducing the mass assignment vulnerability. If you see params.permit!, try changing sensitive attributes immediately.


3. The Keys to the Kingdom: SECRET_KEY_BASE and Deserialization

Every Rails app has a SECRET_KEY_BASE (and historically secret_token). This key is used to sign (and sometimes encrypt) session cookies.

  • The Risk: If you can find this key (often leaked in .env files, GitHub, or via local file inclusion config/secrets.yml), you can craft your own session cookies.
  • The Payoff: This often leads directly to Remote Code Execution (RCE). Rails often uses Marshal.load for session deserialization (using ActiveSupport::MessageVerifier). If you can sign a malicious serialized Ruby object (gadget chain), the server will execute it upon deserialization.

Tools: Use rails-cookie-decryptor or Metasploit’s rails_secret_deserialization exploit.


4. Dangerous Bypasses: raw and html_safe

Rails automatically escapes all output in the view to prevent XSS. However, developers sometimes use these methods to display “legitimate” HTML (like blog posts or comments):

1
2
3
<%# VULNERABLE: Bypasses escaping completely %>
<%= @user.bio.html_safe %>
<%= raw @user.bio %>

Always search the codebase (grep -r "html_safe" app/views) for these keywords during a white-box assessment. If user input flows into them, you have XSS.


5. Insecure Direct Object References (IDOR)

Rails makes it easy to find records, sometimes too easy.

Vulnerable Code:

1
2
3
4
def show
  # No check if the current user owns this document!
  @document = Document.find(params[:id])
end

Secure Code:

1
2
3
4
def show
  # Scopes search to the current user's association
  @document = current_user.documents.find(params[:id])
end

As a tester, always try changing the ID in the URL (/documents/1 -> /documents/2). If you see someone else’s data, the developer missed the scope.


6. Automated Security Tools for Rails

Before you go manual, use the tools that the pros use:

  1. Brakeman: A static analysis security scanner for Rails. It parses the source code and finds everything from SQLi to XSS and insecure redirects.
    1
    2
    
    gem install brakeman
    brakeman -A
    
  2. Bundler-audit: Checks your Gemfile.lock for dependencies with known CVEs.
    1
    2
    
    gem install bundler-audit
    bundle audit check --update
    

Conclusion

Ruby on Rails is a beautiful, powerful framework that makes web development a joy. But like any complex system, it relies on the developer to use its safety features correctly. By understanding the standard conventions of Rails, mastering the nuances of strong parameters, and knowing where to look for hidden secrets and deserialization gadgets, you become a formidable web infiltrator.

Stay curious, audit your code, and never stop breaking things.

Happy hacking!


References