Ruby ERB Template Injection

Written by Scott White & Geoff Walton

Templates are commonly used both client and server-side for many of today’s web applications.  Many template engines are available in several different programming languages.  Some examples are Smarty, Mako, Jinja2, Jade, Velocity, Freemaker, and Twig.  Template injection is a type of injection attack that can have some particularly interesting impacts.  With the case of AngularJS, this could mean XSS, and in the case of server-side injection could mean remote code execution.

Portswigger, the folks that produce BurpSuite, have written a great blog about server-side template injection at   As an attacker, the basic methodology is to first identify the template engine, enumerate accessible classes/methods, and finally utilize those to obtain the desired action.  That action could be something like reading or writing a file, command execution, or other action.  The actions that can be performed are determined by what the available class methods/functions can do.

For this blog, we will do a walkthrough of Ruby/ERB template injection, using some of the training software in TrustedSec’s application security courses.  Let’s take a sample application that simulates an IT Helpdesk reporting tool with template editing functionality.  The application allows for HTML and template editing with a preview function as seen below:

When submitting the form with the preview button, the page displays a long list of users with their creation dates:

We see the syntax used for the username and tombstone.  Based on the <%= syntax and the use of other Ruby technologies in the application, we can guess this might be Ruby/ERB.  From here, we edit the input to test to see if we have template injection.  A quick look at ERB documentation tells us that the <%= syntax executes a Ruby statement and attempts to convert the result into a string that becomes part of the resulting text.  We simply try to perform a mathematical operation with the payload:

<%= 7 * 7 %>

The result is 49 printed for each user:

We have confirmed the template injection . . .  Awesome!  What’s next?  Let’s see if we can execute functions.  A good start is to test to see if built-in global functions work.  Our next payload we try is:

<%=‘/etc/passwd’).read %>

The system has blocked access to as an insecure operation.  Ruby’s ERB template engine has a safe level parameter; when the safe level is set beyond zero, such as three (3), certain functions like file operations cannot be executed from within the template binding.  If the application has a safe level of four (4), maximum isolation is provided and only code marked trusted can be executed.  It looks like the administrator has set the safe level on the template engine.  Although our attacks won’t be as simple as just writing or retrieving some disk files; there is likely still considerable attack surface here.  What can we do with the gadgets that are available to us?  If we investigate the self-object we may be able to enumerate its available properties and methods.  The next payload we use is:

<%= self %>

The result:

This definitely looks like Ruby. Let’s see if we can obtain the class name from the self object:

<%= %>

The result:

The class name is “TemplateInjection”.  Now that we have “access” to the controller, what can it do?  Let’s enumerate the available TemplateInjection class methods:

<%= self.methods %>

The result:

Next, look at the functions and think about what may be passed to them to obtain some unauthorized data access.  Without knowing more about the web application framework in use, we can guess that we are likely inside the handle_POST or do_POST functions as we are doing an HTTP POST request to the system.  Maybe we can access some of the local variables.

For those less familiar with Ruby, it offers strong metaprogramming and introspection (One can learn more at; as attackers we can use some of these features such as the .methods, and .name class methods we have already seen to discover information about program internals.  We can see what parameters are required using:

<%= self.method(:handle_POST).parameters %>

The result:

Next, we see that handle_POST takes three required parameters req, probably an object representing the request, rsp, possibly a reference to what will be the response, and finally a session, probably an id or possibly an object.  We can continue with some discovery and determine what the session object is:

<%= %>

The result:

One thing we can see immediately is “WEBrick”; this is Ruby’s native web server in the standard library.  We could certainly keep digging around this session object but there may be more interesting targets than data mostly associated with our own session.  A quick review of some WEBrick documentation informs us that Servlets invoked to handle requests are passed a number of interesting variables.  We can use some more introspection to see if we have access to these as well as what else might be available.

<%= self.instance_variables %>

The result:

WEBrick passes an instance of the http server to the servlet when it is instantiated to handle a request.  This is probably the instance variable @server.  Let’s check this out and see what member variables that object might have.  We can again leverage introspection by calling the .instance_variables method.
<%=@server.instance_variables %>

The result:

@ssl_context appears the most interesting.  That might contain keys or other useful information.  Note that we will change our syntax up a little here and use just ‘<%’ which executes a Ruby statement.  This will create our own local variable to hold a reference to @ssl_context to keep things more readable, then we can reference that subsequently in the template.

<% ssl=@server.instance_variable_get(:@ssl_context) %><%= ssl.instance_variables %>

The result:

Well, @key sure looks interesting. Let’s recover that value:

<% ssl = @server.instance_variable_get(:@ssl_context) %><%= ssl.instance_variable_get(:@key) %>

The result:

Certainly, discovering the server’s private key would represent a serious compromise.  As application security testers, we’d probably stop somewhere around here.  There are a number of routes our developers could take to limit the scope of data and better sandbox any code execution accessible from the template.  Suggestions on how to do that would be made based on what else we learned about the application during testing.  As application testers, it’s important to thoroughly explore any case where a template that gets processed server side can be manipulated by a client.  Risky as it may be, user provided templates are common, especially in applications that generate reports and send e-mail.  Hopefully this post provides readers with technique to test one more common template engine in use today.