I would consider myself as a mediocre CTF play, because most of the time I only try to crack easy and medium machines. Sorry, life is too busy, if I am 10 years younger and didn’t stray in my early life, then I would probably challenge the hard ones. Anyway, I was cracking an easy box with SSTI vulnerability, but that was more challenging than I thought. Because I was lack of the SSTI knowledge, so even that’s an easy machine, it’s still tough for me. So I thought it would be a good idea to write some notes.

TL;DR If the Rube server only uses regex to sanitize the input and ignored the appearance of the newline character, then command injection would be possible.

Introduction

Suppose the target server is running Ruby’s ERB engine (default one), and at main page, we have could enter Name and Age information, then click the submit button, the information you entered will get displayed

Before submit

After submit

Let’s take a look behind the scene (I mean code)

At the info.erb file, we see following

<h1>Enter your basic information</h1>
<form method="POST" action="/submit-info">
    <table>
      <tr>
        <th>Name</th>
        <th>Age</th>
      </tr>
      <tr>
        <td>
          <input type="text" id="name" name="name" required>
        </td>
        <td>
          <input type="number" id="age" name="age" min="0" max="100" required>
        </td>
      </tr>
    </table>
    <button type="submit">Submit</button>
  </form>
  <%= @result %>

At the main.rb file, we’ll see the user submitted name value is

  1. get inspected by the regex pattern /^[a-zA-Z0-9\/ ]+$/
    • It will only take the input starts and ends with alphabets, numbers, and forward slash (/), plus space.
    • Ex: Michael Scott (this is allowed), Miachae; Scott (this is not allowed)
  2. If passed, then insert into the ERB.new() function
post '/submit-info' do
if params[:name] =~ /^[a-zA-Z0-9\/ ]+$/ && params[:age] =~ /^[0-9\/ ]+$/
@result = ERB.new("Your Information: " + params[:name] + "; " + params[:age]).result(binding)
erb :'info.erb'

Exploit

In main.rb, it did not sanitize the newline character (\n). In this case, when the regex sees a newline character \n in the input, it will automatically stop checking what’s after it. Because it didn’t have the /m option to match multiple line, it also didn’t specify what happens when the input contains a newline character \n.

So now, we could test it out with newline character \n with Burp Suite. It has to be Burp Suite for easier manipulation purpose. Say we’ll enter our name as Michael\n;Scott, its URL encoded version is (Michael%0A%3BScott) such input will get blocked before, and it will pass now.

See, now we could use the semi-colon; between Michael and Scott.

With that in mind, we could start to injection command line in the template. We’ll use an easy example

<%= 7 * 7 %>

from https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#ruby

Let’s URL encode it, I would use CyberChef, remember to encode all the characters. It will become

  1. %3C%25%3D%207%20%2A%207%20%25%3E
  2. Add prefix Miachel%0A to it, so the final input will look like this
  3. Miachel%0A%3C%25%3D%207%20%2A%207%20%25%3E

If that works, we should see the output of 49 in the response page.

Nice, it is successfully running our command at the server’s side. We could then exploit this with other malicious injections, reverse shell, file reading, deletion, etc.

Prevention

Up till this point I am getting tired a little bit, so I asked Chat-GPT how to prevent such exploit, it said Escape User Input


require 'erb'
require 'erb/util'

user_input = "<script>alert('Injected!')</script>"
safe_input = ERB::Util.html_escape(user_input)
template = "Hello, #{safe_input}"

erb = ERB.new(template)
result = erb.result

puts result # Output: Hello, &lt;script&gt;alert(&#39;Injected!&#39;)&lt;/script&gt;

Well, there are actually many other ways, like an easy if statement to check if the user input contains a new line character. I am not a Ruby Expert, so I am not going to dig too deep here.

Cheers!