Command Injection in Grape
How Command Injection Manifests in Grape
Command injection vulnerabilities in Grape APIs occur when user input is passed directly to system commands without proper sanitization. In Grape applications, this typically happens through endpoints that interact with the operating system, execute shell commands, or interface with external processes.
A common pattern in Grape APIs is accepting file paths or identifiers from request parameters and using them in system calls. For example:
post '/execute' do
command = params[:command]
result = `#{command}`
{ output: result }
end
This endpoint is vulnerable because it directly interpolates user input into a shell command. An attacker could send command=id; cat /etc/passwd to execute arbitrary commands on the server.
Another Grape-specific scenario involves file operations. Consider this endpoint that processes uploaded files:
post '/process' do
filename = params[:filename]
`convert #{filename} -resize 200x200 output.png`
{ status: 'processed' }
end
If an attacker uploads a file named test.png; rm -rf /, the shell will execute both the convert command and the malicious rm command.
Environment variable manipulation is another vector. Grape APIs often use environment variables for configuration, but if these are exposed or manipulated through endpoints:
get '/env' do
var = params[:var]
`echo $#{var}`
end
An attacker could use var=PATH; ls / to list directory contents or access sensitive information.
Process spawning functions in Ruby are particularly dangerous when used in Grape APIs. The system, exec, and backtick operators all invoke the shell, creating injection opportunities:
post '/run' do
program = params[:program]
system(program)
end
Even seemingly safe operations can be vulnerable. File path traversal combined with command execution:
get '/file' do
path = params[:path]
`cat #{path}`
end
An attacker could use path=/etc/passwd; ls -la to read files and list directories.
Grape-Specific Detection
Detecting command injection in Grape APIs requires both static code analysis and dynamic runtime testing. Static analysis tools can identify dangerous patterns like string interpolation in system calls, but they may miss context-specific vulnerabilities.
middleBrick's black-box scanning approach is particularly effective for Grape APIs because it tests the actual runtime behavior without requiring source code access. The scanner identifies command injection by:
- Testing parameter injection points with known shell metacharacters (
;,&,&&,||,|) - Analyzing HTTP response codes and timing to detect successful command execution
- Checking for command output in API responses
- Testing common injection payloads like
id,whoami, andls
For Grape APIs specifically, middleBrick scans for patterns common in Ruby web applications:
# Dangerous patterns middleBrick detects
`#{user_input}` # Backtick execution
exec(user_input) # Direct exec
Kernel.system(user_input) # System call
IO.popen(user_input) # Pipe execution
Open3.capture3(user_input) # Capture execution
The scanner also tests for indirect command injection through file operations, environment variable usage, and process spawning functions that Grape APIs commonly employ.
middleBrick's LLM/AI security module adds another layer of detection for Grape APIs that use AI/ML features. It tests for prompt injection that could lead to command execution through AI model interfaces, a growing concern as APIs integrate with language models.
Runtime detection in development environments can include monitoring system calls and logging suspicious patterns. Tools like strace or Ruby's Process::Status can help identify when user input reaches system commands.
Grape-Specific Remediation
Remediating command injection in Grape APIs requires eliminating shell command execution entirely or implementing strict input validation and parameterization. The safest approach is to avoid shell commands whenever possible.
Instead of using shell commands for file operations, use Ruby's native file handling:
# Vulnerable
`cat #{path}`
# Secure
File.read(path)
For image processing that might have used ImageMagick commands, use Ruby libraries:
# Vulnerable
`convert #{filename} -resize 200x200 output.png`
# Secure
require 'mini_magick'
image = MiniMagick::Image.open(filename)
image.resize('200x200')
image.write('output.png')
When shell commands are unavoidable, use the multi-argument form that bypasses the shell:
# Vulnerable - shell interpolation
`ls -la #{dir}`
# Secure - array form, no shell
system('ls', '-la', dir)
For complex commands, use Ruby's Open3 with explicit argument arrays:
require 'open3'
# Secure - explicit arguments
command = ['find', dir, '-name', '*.rb']
stdout, stderr, status = Open3.capture3(*command)
Input validation is critical. Implement strict whitelisting for acceptable inputs:
# Allow only alphanumeric filenames
def sanitize_filename(name)
return nil unless name =~ /\/(^[a-zA-Z0-9_\-\.]+$)/
name
end
# Validate paths against allowed directories
def validate_path(path, base_dir)
full_path = File.expand_path(path, base_dir)
return nil unless full_path.start_with?(base_dir)
full_path
end
For Grape APIs, use parameter validation with Grape's built-in features:
module API
class SecureAPI < Grape::API
params do
requires :command, type: String, regexp: /\/(^[a-zA-Z0-9_\-\.]+$)/
end
post '/execute' do
# Safe because regex validation occurred
command = params[:command]
# Use safe execution method
result = safe_execute(command)
{ output: result }
end
end
end
Implement a security middleware in Grape to scan for dangerous patterns before execution:
class CommandInjectionProtection
DANGEROUS_PATTERNS = [
/;\|s\|/,
/\&\/u,
/\|\/u,
/\$\{(.*?)\}\/u
]
def initialize(app)
@app = app
end
def call(env)
# Check for dangerous patterns in parameters
params = Rack::Request.new(env).params
if contains_dangerous_patterns?(params)
return [400, {}, ['Forbidden: potential command injection']]
end
@app.call(env)
end
private
def contains_dangerous_patterns?(params)
params.values.any? do |value|
next false unless value.is_a?(String)
DANGEROUS_PATTERNS.any? { |pattern| pattern.match?(value) }
end
end
end
Finally, implement comprehensive logging and monitoring for any remaining system interactions to detect attempted exploitation:
# Log all system command executions
def safe_execute(command, *args)
logger.warn("Executing system command: #{command} #{args.join(' ')}")
system(command, *args)
rescue StandardError => e
logger.error("System command failed: #{e.message}")
raise
end
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 |
Frequently Asked Questions
How can I test my Grape API for command injection vulnerabilities?
test; whoami or test && ls -la in parameters that might reach system commands. Look for command output in responses or changes in HTTP status codes.