Link Search Menu Expand Document

Unsafe Deserialization in Ruby

Ruby uses the Marshal library to serialize and unserialize objects. For example, the following script creates an instance of the object User, serializes it, and then prints the string representation of the object.

User =, :role)
user ='Mike', :admin)
puts Marshal.dump(user).inspect

# Prints the following string representation:
# "\x04\bS:\tUser\a:\tnameI\"\tMike\x06:\x06ET:\trole:\nadmin"

The string representation can then be deserialized again to recreate the object instance and access its attributes.

user = Marshal.load("\x04\bS:\tUser\a:\tnameI\"\tMike\x06:\x06ET:\trole:\nadmin")

# It prints the following string:
# Mike

Vulnerable Example

The exploitation of deserialization in Ruby happens when user-controlled input is passed as the first argument of the Marshal.load() function.

To be exploitable, the vulnerable piece of code must have enough Ruby code in scope to build a gadget chain, which means a chain of reusable code that causes a meaningful impact when invoked.

For example, assume that Marshal.load() deserializes user-provided data. An attacker could craft a malicious payload like the following one, which abuses an existing class to execute a command when deserialized.

class FSResource
  def initialize path
    @path    = path

  def to_s

# Craft the payload to execute `id` via the `open` function instead of opening a file
obj ='|id')
payload = Marshal.dump(obj)

# Unserializing the payload allows to execute arbitrary commands
serialized_obj = Marshal.load(payload)
puts serialized_obj

# It prints the output of id:
# uid=1002(admin) gid=1002(admin) groups=1002(admin)

A number of real code chains against Ruby and Ruby on Rails have been discovered and published by security researchers in the past.


Wikipedia - Serialization

Ruby - Marshal

ZDI - Remote Code Execution via Ruby on Rails Active Storage Insecure Deserialization

ELTTAM - Ruby 2.x Universal RCE Deserialization Gadgets Chain