Command Injection in Rails
How Command Injection Manifests in Rails
Command injection in Rails applications typically occurs when user-controlled input is passed to system commands without proper sanitization. Rails developers often use methods like system, exec, or backticks (`) to execute shell commands, creating injection points when input isn't validated.
The most common Rails-specific patterns include:
- Shell command execution with user input:
system("ls -la #{params[:path]}")allows attackers to inject additional commands via crafted path parameters - Backtick execution:
`git clone #{repo_url}`whererepo_urlcomes from user input - Kernel.exec usage:
exec("rm -rf #{params[:directory]}")can be exploited with semicolon-separated commands - Open3 with string interpolation:
Open3.capture3("grep '#{search_term}' #{file}")wheresearch_termis user-controlled
A particularly dangerous Rails pattern involves using params directly in shell commands. For example:
class ReportsController < ApplicationController
def generate
cmd = "wkhtmltopdf --page-size A4 #{params[:url]} report.pdf"
system(cmd)
send_file 'report.pdf'
end
endAn attacker could exploit this by visiting /reports/generate?url=example.com%20%26%26%20rm%20-rf%20/%26%26, causing the server to execute wkhtmltopdf --page-size A4 example.com && rm -rf / && report.pdf.
Rails applications also face command injection through:
- ActiveRecord's
connection.executewhen combined with system calls - Delayed job workers that execute shell commands from database-stored strings
- Background job processors like Sidekiq or Resque that run user-provided commands
- Asset pipeline compilation with user-controlled input in custom build scripts
The Rails ecosystem's flexibility with metaprogramming and dynamic code execution creates additional attack surfaces. Methods like send, public_send, and method_missing can be combined with command execution in unexpected ways.
Rails-Specific Detection
Detecting command injection in Rails requires both static code analysis and runtime scanning. middleBrick's Rails-specific scanning identifies these vulnerabilities by analyzing your API endpoints and their parameters.
Static detection patterns middleBrick looks for:
# Dangerous patterns detected:
system(params[:input])
exec("command #{user_input}")
`echo #{unsafe_var}`
Kernel.system("ls #{path}")
Open3.capture3("grep '#{search}' #{file}")middleBrick's Rails scanner specifically identifies:
- Parameter-based command injection: Scanning for endpoints that accept parameters used in shell commands
- Model attribute exploitation: Checking if database attributes containing user input are passed to system calls
- Background job vulnerabilities: Analyzing job classes that execute shell commands with user data
- Configuration file injection: Detecting if Rails configuration files allow command execution with user input
Runtime detection with middleBrick involves submitting your Rails API endpoints to the scanner. The tool tests for command injection by:
- Analyzing the OpenAPI/Swagger spec to understand parameter structures
- Crafting payloads that attempt to break out of the intended command context
- Monitoring system responses for signs of successful injection
- Testing across all 12 security categories, including input validation bypass attempts
For Rails applications, middleBrick provides specific findings like:
[CRITICAL] Command Injection - ReportsController#generate
Endpoint: POST /api/reports
Severity: Critical
Risk: System compromise possible
Remediation: Use Kernel.system with array syntax, validate input
Evidence: User input 'path' used in shell command without sanitizationThe scanner also checks for Rails-specific command execution patterns in background jobs, rake tasks, and initializer files that might not be exposed through standard API endpoints.
Rails-Specific Remediation
Remediating command injection in Rails applications requires a multi-layered approach. The primary defense is eliminating shell command execution entirely when possible, but Rails provides several safe alternatives when system calls are necessary.
Safe command execution patterns:
# ✅ Safe - array syntax prevents shell interpretation
system(['ls', '-la', safe_path])
# ✅ Safe - Open3 with array syntax
Open3.capture3(['grep', safe_search, file_path])
# ✅ Safe - Process.spawn with arguments
Process.spawn('convert', image_path, '-resize', '200x200', output_path)Input validation and sanitization:
class ReportsController < ApplicationController
VALID_PATHS = %w[reports uploads temp].freeze
def generate
path = params[:path].to_s
sanitized_path = sanitize_path(path)
unless VALID_PATHS.include?(sanitized_path)
render json: {error: 'Invalid path'}, status: :bad_request
return
end
cmd = ['wkhtmltopdf', '--page-size', 'A4', sanitized_path, 'report.pdf']
system(cmd)
send_file 'report.pdf'
end
private
def sanitize_path(path)
path.gsub(/[^a-zA-Z0-9_/\-]/, '') # Remove dangerous characters
end
endRails-specific security gems and practices:
- Shellshocked: Provides shell command sanitization utilities
- Brakeman: Static analysis tool that detects command injection patterns
- Danger: CI tool that can flag command injection in code reviews
Background job security:
class SecureJob < ApplicationJob
def perform(user_input)
Related CWEs: inputValidation
CWE ID Name Severity CWE-20 Improper Input Validation HIGH CWE-22 Path Traversal HIGH CWE-74 Injection CRITICAL CWE-77 Command Injection CRITICAL CWE-78 OS Command Injection CRITICAL CWE-79 Cross-site Scripting (XSS) HIGH CWE-89 SQL Injection CRITICAL CWE-90 LDAP Injection HIGH CWE-91 XML Injection HIGH CWE-94 Code Injection CRITICAL