Ruby on Rails Dynamic Render File Upload Remote Code Execution

Tl;dr: If your application uses dynamic render paths (eg: render params[:id]) then you are vulnerable to remote code execution via local file inclusion. Update to the latest version of Rails, or refactor your controllers.


Rails controllers are designed to implicitly render view files based on the method being invoked. For example, when evaluating a controller’s show method the framework will fall back to implicitly rendering the show.html.erbfile if there are no other explicit render statements.

In many cases, however, developers will decide to explicitly render different content based on the formats such as text, JSON, XML or an entirely different view file. A view file, in this context, would be a templating language file such as ERB, HAML, and so on. There are several methods which can be used to influence the view content. For our purposes, we will focus on the render method. The Rails documentation highlights several ways to invoke the render method, including the ability to explicitly specify a path to render using the file: option.

If you have read the documentation around this method and find yourself a bit unsure about the desired functionality, you’re not alone. In fact, let’s consider a snippet of code:

def show
  render params[:template]

At first glance, this code seems pretty simple, and one would guess that the desired purpose of the specified controller action was to render the view template, which is specified via the template parameter. It is not clear, though, where Rails will look to find the specified template. Does it look in the views directory, does it look in the application root, or does it look elsewhere? Is it expecting a template name, a filename with a specific extension, or a full file path? There are many unanswered questions that can only be resolved by looking at the implementation details.


The rendering mechanism seems to be a prime example of a single function trying to accomplish too much, and this is where the problem lies.

Let us assume that the expected behavior for the rendering mechanism is to attempt to render the file in app/views/user/#{params[:template]} – this seems like a plausible thought. Providing the template parameter with a value of dashboard will attempt to load the template in app/views/user/dashboard.{ext}, where .ext is any allowable extension (such as .html, .haml, .html.erb, etc.)


Consider now a user that enters the following value for a template parameter:../admin/dashboard. What is the expected result? It may be hard to know for sure, but when we try this we can see that the application throws a missing template error.


Upon analyzing the error, it appears that the application is attempting to render the view by searching for it in several paths, includingRAILS_ROOT/app/views, RAILS_ROOT and the file system root. This is quite worrisome.

So when /etc/passwd  is pass in as the template parameter, then we are able to read our passwd file. This is a huge concern.


If we can read the passwd file we can probably read the application’s source code and configuration values, such as the config/initializers/secrettoken.rb_ file.


In case we have forgotten why this is occurring, it is because we chose to use dynamic render paths within our application:

def show
  render params[:template]

Such a simple code snippet has provided the ability for an attacker to read our source code and application configuration values. Unfortunately, that is not the worst part.

As Jeff Jarmoc describes in his paper “The Anatomy of a Rails Vulnerability – CVE-2014-0130: From Directory Traversal to Shell,” we can leverage this vulnerability to gain a shell on our Rails application. Jeff’s paper describes a similar flaw in Rail’s implicit render mechanism that allows for directory traversal, or more accurately, local file inclusion, in certain versions of Rails. In this blog we are addressing the concern related to explicit rendering, which is a developer-introduced vulnerability.

Before we dive into the details we want to point out that the category of vulnerability we are addressing is file inclusion and not directory traversal. The differentiating factor is that we are loading, interpreting, and executing the file as code (ERB). Traditionally, directory traversal vulnerabilities return non-executable content, such as a CSV file. So, in essence, not only can we read the application’s source code, and other system-readable files, but we can also execute Ruby code. Because we can execute Ruby code, we can also execute system-level commands on behalf of the web server.

A key factor in transitioning this attack from file inclusion to shell is a technique known as log file tainting. Rails is designed to log request information, including parameters, for each request within the environment’s log file (such as development.log). The log file, which is otherwise plaintext, can be tainted with Ruby code. This can be accomplished by making a request to the web application using valid Ruby code, contained within a parameter.

In the following example we make a legitimate request to the web application but pass along the fake parameter with the following URL-encoded value <%= `ls` %>.


Upon review of the log file, we can see the associated entry, with parameters represented as a hash with keys URL-decoded. This is valid Ruby code and would execute if this file were rendered within our application.


We can then leverage the file inclusion vulnerability to attempt to load the log file, executing the Ruby code.


When the log file is returned we see the parameters hash. Notice that the fake parameter’s value which, previously contained our payload, has been replaced with the output of the ls command. At this point, we can execute any system-level command with which the web-server has privileges to run.

Public Exploit

Recently, public exploit for this vlnerability has been added to metasploit. The module name for this exploit is exploit/multi/http/rails_dynamic_render_code_exec. This Metasploit module has been tested across multiple versions of Ruby on Rails. The technique used by this module requires the specified endpoint to be using dynamic render paths. Also, the vulnerable target will need a POST endpoint for the TempFile upload, this can literally be any endpoint. This Metasploit module does not use the log inclusion method of exploitation due to it not being universal enough. Instead, a new code injection technique was found and used whereby an attacker can upload temporary image files against any POST endpoint and use them for the inclusion attack.

Ruby on Rails 4.0.8 July 2, 2014


At this point, you’re probably wondering how we can mitigate the vulnerability. Most importantly, apply the patch for your specific version of Rails.

If you haven’t installed the patch, consider not attempting to render non-acceptable files. This can be accomplished by creating a hash of valid filenames that can be rendered in the given context, and ensure that the user parameter is included in the hash. This will need to be accomplished for each instance of dynamic render paths within the application.

def show
  template = params[:id]

  valid_templates = {
    "dashboard" => "dashboard",
    "profile"   => "profile",
    "deals"   => "deals"

  if valid_templates.include?(template)
    render " #{valid_templates[template]}"
    # throw exception or 404

Another similar solution would be to verify that the given file exists within the context of a specified directory.

def show
  template = params[:id]
  d = Dir["myfolder/*.erb"]

  if d.include?("myfolder/#{template}.erb")
    render "myfolder/#{template}"
    # throw exception or 404

Leverage Brakeman, a Rails static-analysis tool to scan the application. Brakeman will report instances of dynamic render paths, amongst other things. This will allow you to determine which controllers within your application are affected.


Leave a Reply

Your email address will not be published. Required fields are marked *