Learn Pentesting: Manual Testing for Ruby Code Injection Vulnerabilities
In this installment of our Learn Pentesting series, we’ll explore Ruby code injection vulnerabilities—a critical issue that can lead to remote code execution (RCE) and complete system compromise if left unaddressed. This technical deep-dive is designed for penetration testers and security researchers who need a robust, hands-on guide to manually testing web applications for Ruby code injection flaws.
Introduction
Ruby is a dynamic language often used for web applications, making it an attractive target for attackers when it comes to code injection vulnerabilities. Unlike SQL injection or XSS, Ruby code injection takes advantage of the language’s dynamic evaluation features, such as eval
, to execute arbitrary code. In this post, we’ll cover how to manually test for these vulnerabilities, complete with real code examples and testing strategies that you can integrate into your penetration testing workflow.
Understanding Ruby Code Injection
Ruby code injection vulnerabilities occur when user input is unsafely passed into methods that evaluate Ruby code dynamically. Common methods include:
eval
: Executes a string as Ruby code.instance_eval
/class_eval
: Evaluates a string or block within the context of an object or class.send
/public_send
: Dynamically invokes methods based on user input.
When input is not properly sanitized, an attacker can inject and execute arbitrary Ruby code, potentially allowing full control over the application environment.
Manual Testing Techniques
Identifying Potential Injection Points
The first step is to locate where user input might be passed to dangerous evaluation functions. During your penetration tests, consider:
- Reviewing Source Code: Look for direct calls to
eval
,instance_eval
, or other dynamic evaluation functions. - Parameter Fuzzing: Experiment with various inputs to see how the application responds. Inputs that trigger unexpected behavior, such as delays or unusual error messages, can indicate a vulnerable evaluation point.
Crafting Test Payloads
A successful payload for testing Ruby code injection should be designed to verify code execution without causing harm. Here are some strategies:
Simple Expression Testing: Start with basic arithmetic or string expressions, e.g.,
"1+1"
or"Hello World"
.Echo Payloads: Use a payload that prints a unique string. For example:
"puts 'injection_test_success'"
If the output contains your unique string, it’s a strong indicator that code injection is possible.
Time-based Payloads: If output isn’t directly visible (blind injection), use Ruby’s
sleep
function to introduce a delay:"sleep(5)"
An observable delay in response time can confirm code execution.
Code Examples and Walkthrough
A Vulnerable Ruby Snippet
Consider the following simplified example that simulates a vulnerable Ruby web application endpoint:
# vulnerable_app.rb
require 'sinatra'
get '/vulnerable' do
user_input = params[:input]
begin
# Directly evaluating user input – a dangerous practice!
result = eval(user_input)
"Evaluation result: #{result}"
rescue Exception => e
"Error: #{e.message}"
end
end
In this snippet, any input provided to the /vulnerable
endpoint is passed to eval
without sanitization, opening the door for code injection.
Exploiting the Vulnerability
As a penetration tester, you can start by sending benign payloads:
Arithmetic Test:
Request:
/vulnerable?input=1+1
Expected Output:
Evaluation result: 2
Echo Test:
Request:
/vulnerable?input=puts 'injection_test_success'
Expected Behavior:
The server’s output or logs should showinjection_test_success
. If output is not directly visible, you might rely on side effects (e.g., file creation, logs).Time Delay Test:
Request:
/vulnerable?input=sleep(5)
Expected Behavior:
The response should be delayed by 5 seconds, indicating that Ruby’ssleep
function was executed.
Combining Payloads
In more advanced testing scenarios, you might chain multiple commands together:
# Combining arithmetic and a sleep delay
"/vulnerable?input=(puts 'test'; sleep(5); 1+1)"
This payload prints a test string, introduces a delay, and finally returns a result from an arithmetic expression.
Important Consideration:
Always ensure that your payloads are safe and do not cause unintended damage or disrupt the target system’s operation. Avoid destructive commands unless you have explicit permission to conduct such tests.
Mitigation and Defensive Measures
While our focus here is on testing, it’s important to note how to defend against Ruby code injection vulnerabilities:
- Avoid Dynamic Evaluation: Eliminate the use of methods like
eval
in favor of safer alternatives. - Input Sanitization: Rigorously sanitize and validate user inputs. Consider using whitelisting approaches.
- Use Safe Evaluation Methods: When dynamic evaluation is unavoidable, limit the accessible context and use safer methods like
Safe
or custom sandbox environments. - Code Reviews and Static Analysis: Regularly review your codebase for dangerous practices and use static analysis tools to catch potential vulnerabilities.
Conclusion
Ruby code injection remains a dangerous vulnerability if user inputs are handled carelessly. In this post, we covered the critical steps in manually testing for Ruby code injection during web application penetration tests. By identifying vulnerable evaluation points, crafting intelligent payloads, and verifying the outcomes, penetration testers can effectively uncover and report these security issues.
This guide is part of our Learn Pentesting series, aimed at equipping technical professionals with the hands-on skills needed for effective vulnerability assessment. Continue to explore, experiment, and stay informed on best practices in web application security.
Happy testing, and always remember: the key to secure code is never to trust user input.