Metasploit Rails 3 Remote Code Execution Hours Away
community.rapid7.com
community.rapid7.com
If you've never dealt with a problem like this, you may not be ready. So, here's the most important thing you need to understand:
If you have a vulnerable application anywhere, on any port it will be found and compromised. This is a spray-and-pray vulnerability. It costs attackers nothing to try, attempts don't crash servers, and so people will try everywhere.
If you lose an application in your data center / hosting environment, that's the ballgame. It doesn't matter that the app you lost was the testing instance of a status dashboard with no real data in it, because the exploit coughs up shell access on that server. If there is one thing every black-hat attacker in the world is truly gifted at, it is pivoting from shell access on one server to shell access on every goddam server.
Please make sure you didn't just patch the app servers you know/care about. THEY ALL NEED TO BE PATCHED OR RETIRED.
Additionally:
* If you are one of those "same password on a whole bunch of services people", now is a good time to make sure nothing you care about has that password. Some app somewhere is about to lose that password.
* Now would not be the worst time in the world to go to your Twitter config, hit Settings -> Apps, and scrub out all the stuff you don't use.
* Now you know why you never give 3rd party web apps your Gmail password.
ActionDispatch::ParamsParser::DEFAULT_PARSERS.delete(Mime::XML)
source: https://groups.google.com/forum/#!topic/rubyonrails-security...If you patched as described, and run the exploit code on your local server, the exploit code will return a 200 response. As in:
[-] POSTing puts 'boom' to http://localhost:3000 ...
[-] Success!
This doesn't mean your site is vulnerable. Rails is entirely disregarding the parameters as specified by your initializer code.Testing locally, watch your server when the request comes through and ensure there are no parameters being registered. You don't want to see something like this:
Started POST "/" for 127.0.0.1 at 2013-01-09 21:00:04 -0800
Processing by StartController#index as */*
Parameters: {"secret"=>"--- !ruby/hash:ActionDispatch::Routing::RouteSet::NamedRouteCollection\n? {}\n: !ruby/object:OpenStruct\n table:\n :defaults:\n :action: create\n :controller: foos\n :required_parts: []\n :requirements:\n :action: create\n :controller: foos\n :segment_keys:\n - :format\n modifiable: true"}As a simpler test-case, I modified the rails_rce.rb ( for CVE-2013-0156 )and passed a simpler yaml that creates a Time object:
yaml = %{ --- !ruby/object:Time {} } xml = %{ <?xml version="1.0" encoding="UTF-8"?> <#{param} type="yaml">#{yaml}</#{param}> }.strip
print_info "POSTing #{code} to #{url} ..."
response = http_request( :method => :post, :url => url, :headers => {:content_type => 'text/xml'}, :body => xml )
This way a vulnerable server's log file shows something like give below(ie. the Time object was actually created from the yaml):
Started POST "/users/sign_in" for 127.0.0.1 at 2013-01-10 00:26:40 -0500 Processing by Devise::SessionsController#create as / Parameters: {"secret"=>1969-12-31 19:00:00 -0500}
A patched server raises a Hash::DisallowedType (Disallowed type attribute: "yaml") exception.
Started POST "/user/sign_in" for 127.0.0.1 at 2013-01-09 21:40:56 -0800
Processing by UsersController#sign_in as */*
If I comment out the patch, I see: Parameters: {"secret"=>1969-12-31 16:00:00 -0800}
So I suspect others may not see the same exception... I'm using ActiveSupport 3.0.3, fyi.activesupport-3.2.11/lib/active_support/core_ext/hash/conversions.rb has a -- DISALLOWED_XML_TYPES = %w(symbol yaml) -- which is used by its def typecast_xml_value to raise the exception.
I don't see these lines of code in activesupport-3.0.3/lib/active_support/core_ext/hash/conversions.rb
In my case I could upgrade to 3.2.11.
In your case, I am guessing you added the lines of code that disable xml and yaml parameter parsing to an initializer (or application.rb). This way, activesupport simply wouldn't try to convert the parameter value in question into a ruby object.
http://www.railsperformance.com/2013/01/simple-test-for-rail...
bundle update rails
Then commit and deploy in the usual manner. You'll probably experience no issues with your application, but it's good practice to run your test suite before you do any sort of production deploy anyway.
If you're running an older version of Rails (pre bundler) or if you don't have a solid test-suite, you'll have a tougher time updating. But you definitely need to put aside time to figure it out now.
Eg. These will update fine:
gem "rails"
gem "rails", "~> 3.2"
This wont: gem "rails", "= 3.2.10"If your gemfile looks like:
gem 'rails', '3.2.10'
Then `bundle update` does nothing. gem 'rails', '3.2.10'
Running `bundle update rails` when you've absolutely specified the version number won't do anything. Your Gemfile.lock file (which is the file that bundler uses to determine which gems to require) won't be changed. Instead of absolutely specifying the version of Rails you want to use, considering using the '~>' specifier. My Gemfile at Airtasker looks like: gem 'rails', '~> 3.2.8'
That means that when I run `bundle update rails` it will update the patch version of rails. Our Gemfile.lock has the following entry for rails: rails (3.2.11)
By using the '~>' specifier it means that we can easily update our gems patch versions without worrying about API changes between major and minor versions.You can read: http://gembundler.com/v1.2/gemfile.html to find out more.
So you will want to double-check the specific version that you end up with. If it doesn't work you probably can fix it with `bundle update rails something_else`, unless you have a true conflict in which case you will have to do some spelunking yourself, and perhaps the Rails hotfix should be in place in your app first.
OT: It'd be nice if Twitter would recognize mass abuse of accounts via an oAuth app and automatically disable that app until action can be taken. (They may do this, I don't know).
Fortunately, the community is stepping up with patches[1]. Hopefully, these patches are not adding further vulnerabilities.
3-2-dynamic_finder_injection.patch 3-2-null_array_param.patch 3-2-xml_parsing.patch
The changelogs didn't cleanly apply but everything else did. In your Gemfile,
gem 'rails', :git => 'git://github.com/adamonduty/rails', :branch => '3.2.8_with_security_patches'
This will install version 3.2.8a. If you get a bundler error "NoMethodError: undefined method [] for nil:NilClass", try upgrading your rubygems-bundler gem to version 1.1.0.
See https://github.com/adamonduty/rails/tree/3.2.8_with_security... for the commits.
Given the number of changes and known issues in 3.2.9, I don't understand why the core team didn't perform a similar release.
[1] https://groups.google.com/forum/?fromgroups=#!topic/rubyonra...
I'm not even a hacker, and I have done this before.
To amplify and expand on Thomas here: when this was announced I pushed the Big Red Button and pushed three emergency patches to my servers at 3 to 5 AM Japan time. My perception was "This just can't wait." I went to sleep with the vague feeling that I had probably broken something (there's always something that slips when you're tired and hasty) but that it was almost certainly acceptable given the alternative.
Sure enough: despite automated and smoke tests passing and metrics remaining nominal, Appointment Reminder suffered breaking downtime for some customers (it depended on browser - long story not relevant). This ended up locking them out for about 16 hours, felicitously mostly not during the US working day.
After being told of the issue by a few mighty pissed end users, I fixed it and spent a second awake-to-9 AM night both writing a to-all-customers apology email and fielding questions. I went into detail on why I screwed up (acted too fast) and a simplified version of why I had to (third-party software required an urgent patch; delaying deployment by one day would have been an unacceptable risk to customer data).
Several customers - including a few of the ones most inconvenienced - got it touch to say "Right call." One of them was of the opinion that, if I hadn't patched, he would be in Big Red Button mode today, because no machine or data on a local network with an unpatched Rails instance is safe. "I honestly prefer knowing it broke because you were on top of things than it being stable because you weren't" end quote.
I'm not a security guy, I'm just a systems engineer, but my take on it is that this does not just require the Big Red Button, this is the paradigmatic example of why you have a Big Reg Button. If you don't, or if you pushed it yesterday like you should have and something blew up, this is an excellent opportunity to improve procedures for next time.
Edit: Big Red Button is funny shorthand for "Immediately drop what you're doing, pull out the In Case Shit Happens folder, and have the relevant people immediately execute on the emergency plan." We call it something different in big Japanese megacorps but I always liked the imagery.
http://akibjorklund.com/2009/supergenpass-is-not-that-secure
[1] https://chrome.google.com/webstore/detail/enigmapass/bgkipgf...
OSS, local not cloud-based, encrypted pwd file, excellent pwd generator, easy to use.
Also, if anyone wants help or explanation on the vulnerability (though there are plenty of posts that do a great job), I'd be happy to chat about it - feel free to email me whenever at borski@tinfoilsecurity.com
YOU SHOULD ALSO disable any Oauth access to your Gmail account. If you're like me, you trusted a few "email as a game" or whatever apps to take a peek, but you can't trust them to stay on top of this security flaw.
It's hard as hell to find the instructions in Google's documentation, so here's the account management link:
https://www.google.com/settings/account?hl=en
Click Security, then the bottom option in the main body of the page.
The Rails community seems unusually keen on the 'curl example.org/script | sh' as an installation method (see Pow.cx etc.).
I'd usually recommend reading these scripts before execution, but for now especially so as it would seem an obvious target if people are looking to leverage this exploit to acquire more boxen.
Arguably, HTTPS is one step forward, however vulnerabilities like the one discussed here make us defenceless. To make matters worse the line of defence based on reading the script works only in the case of relatively short, unobfuscated and unminified scripts written in plain text. It also requires the person who's downloading to have skills which despite being common for this community's audience are not widely spread across the population.
Sure, many projects sign their releases or announce cryptographic hashes of published files. But let's be honest: how many of us actually run `gpg` od `sha256sum -c` to verify them?
Spreading paranoia is not my goal here, however I hope that this comment will end up being thought-provoking.
O should be generally quite wary of it in the first place given the ease one could swap out a single file & wreck havoc.
If anyone needs convincing, here's how it will go down:
- Google will help them find you. They'll search for things found in pages typically produced by different Rails apps.
- They will look at the entire network allocation and scan the whole range. (Try running whois against an IP address.)
- If they can identify the hosting platform, it will make it that much easier to know where to look.
- Even if you don't show up in the results, your neighbor might and you're next.
Edit: formatting.
There are several programs that build on the Metasploit Framework and take advantage of it. Rapid7 has commercial penetration testing products. I build the open source Armitage GUI for it and a commercial add-on called Cobalt Strike.
It's worth spending some time to learn how it works and what it does. Here's a few links:
Metasploit Unleashed Wiki: http://www.offensive-security.com/metasploit-unleashed/Main_...
My 7-part course on pen testing (with Cobalt Strike & MSF): http://www.advancedpentest.com/training
Quick demo of what it looks like to attack a workstation and use it as a hop point to get other things: http://www.youtube.com/watch?feature=player_embedded&v=S...
The best way to try the Metasploit Framework is to setup BackTrack Linux in a virtual machine: http://www.backtrack-linux.org/
A free vulnerable target is the Metasploitable virtual machine, available at: http://sourceforge.net/projects/metasploitable/files/Metaspl...
I somehow feel that defensive programming practices would have caught this bug. There is a lot of "magic" going on that lead to this exploit.
Sending text/xml to an application shouldn't have a huge impact but when you are dynamically creating objects out of the content it can lead to some serious problems.
Python has this too with pickle. However with Python it is pretty damn obvious that pickle is NOT SAFE. You also have to import it manually.
There is lots of magic in rails, but I bet there are a number of popular PHP and Python libraries/frameworks that are unserializing in an unsafe way.
It's really criminally negligent that no such method exists in Ruby's YAML library.
rails_rce.rb (https://gist.github.com/4499206)
rails_sqli.rb (https://gist.github.com/4499032)
rails_dos.rb (https://gist.github.com/4499017)
rails_jsonq.rb (https://gist.github.com/4499030)
https://github.com/rapid7/metasploit-framework/blob/master/m...
Same story I submitted yesterday : http://news.ycombinator.com/item?id=5030906
The way the Rails team responded so quickly in patching this.
The Bad:
The patches for this and other recent security issues had little time for testing and hence broke things. The old failed idea of trying to prevent full disclosure, which ultimately harms the community whilst doing nothing to really prevent the bad guys arming themselves with working exploit code, and all the resulting kerfuffle we saw.
The Ugly:
The Rails codebase. Seriously. As you read this, interested people are now pouring over it, looking for new vectors of attack, and we are awaiting the next series of having to scramble and fix the bad things that the magic in Rails enabled.
Use the conversions.rb patch for 2.x from https://groups.google.com/forum/#!topic/rubyonrails-security...
"We're not patching it" statement: https://launchpad.net/bugs/320813
That's not what was said. They don't maintain it.
Re. bundler/gems, I don't know what those are - the file "core_ext/hash/conversions.rb" I hand-patched was from a package called ruby-activesupport-2.3 which is a dependency of the Rails package.
I think if 100% of your eco system is from the package manager you would be fine, but if even a single component needs to come from outside I would reach straight for rvm and bundler (no prejudice against rbenv, rvm is just what I use)
Is a simple code review all it took for this fail cascade?
Do not make the mistake of assuming that because there's no CVE, that a vulnerability is unknown. Not everyone who analyzes code is wearing a white hat.
<?xml version="1.0" encoding="UTF-8"?> <bang type="yaml">--- !ruby/object:Time {} </bang>
An exploit that's simple to use does not mean that it was simple to discover. In fact, the opposite is often the case.
http://en.wikipedia.org/wiki/Metasploit_Project
edit : "metasploit module" is likely a reference to the metasploit framework, which is written in Ruby.
It will probe websites (and local networks) to find out the OS/server information (IIS, apache, etc), database info (mysql, mssql) and language (asp, php).
It then uses a database of known exploits and scripts (all types XSS, SQL injections, etc).
If it's a good tool, why would he release it so soon without giving people much time to update?
If it's malicious, why is he telling people about it?
But anyway, in the end, it doesn't matter, it exists. Authorial intent is so 20th century.
> The Metasploit Project is also well known for anti-forensic and evasion tools, some of which are built into the Metasploit Framework.
If so, how are such functions valuable for a pentest/audit?
Now a blackhat would be able to do this without BT or Metasploit. The tools are out there, and well known. So the fact that these tools are in BT and Metasploit doesn't change that. But it does make it easier for a pentester to prove a system is vulnerable, and to help a company address their vulnerabilities through remediation.
Well, they can do that without MSF. It's just harder.
Where MSF helps is with pentesters and other security professionals. When they perform a pentest or audit, tools like MSF/SET/Nexpose allow them to rapidly and accurately determine if a network or system is vulnerable, and prove it (within the bounds of the engagement's scope). Without these tools, a pentest would require far more tedious work.
See http://en.wikipedia.org/wiki/Full_disclosure it's a debate that has been going on a long time. Consider that right now the only people scanning IP blocks for vulnerable apps are bad guys.
Because the impact of warning people is almost surely greater than someone new and malicious stumbling onto metasploit at exactly this time when such a large vulnerability is at play. Especially given that metasploit has been around for some time and will continue to long after this exploit is a smudge on RoR's history.
Ruh roh.
You can test by running this ruby file: https://gist.github.com/4499206
$ ruby rails_rce.rb http://localhost:3000 param "User.destroy_all"
Monitor your server and ensure it is disregarding the post parameters.ActionController::Base.param_parsers.delete(Mime::XML)
if i just stick it at the bottom of environment.rb, will that work? i am not sure how to test to confirm that i've fixed it.
curl -i -H "Content-Type: application/xml" -X POST -d '<id type="yaml">--- !ruby/object:ActionController::Base bar: 1</id>' http://localhost:3000
If in your logs the params[:id] is an object, then you are vulnerable. If it's just a string, then your fix worked.I put mine in an intializers file.
ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING.delete('symbol')
ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING.delete('yaml')EDIT AGAIN: I think putting it at the end of environment.rb works. I somehow messed it up and got confused, but then I tried it again and it worked. Just make sure you confirm your fix with the curl command!
Parameters: {"action"=>"list", "id"=>#<ActionController::Base:0x6dc4177ed940 @bar=1>, "controller"=>"news"}
Good would look something like this:
Parameters: {"action"=>"index", "id"=>"--- !ruby/object:ActionController::BaseIt's been like 4 years since i did this and haven't touched rails since.
What else do i need to do here to fix this? i Thought rails 3.2.11 was ok..
Hash::DisallowedType (Disallowed type attribute: "yaml"):
activesupport (3.2.11) lib/active_support/core_ext/hash/conversions.rb:112:in `typecast_xml_value'ActionController::Base.param_parsers.delete(Mime::XML)
This will disable parsing of xml which most people never use anyway
Parameters: {"id"=>#<ActionController::Base:0xb570d2e8 @bar=1>}
Why do you think I'm still unprotected after updating to the fixed version 2.3.15 referenced here? http://weblog.rubyonrails.org/2013/1/8/Rails-3-2-11-3-1-10-3...Or is there something I'm missing?
Disallowed type attribute: "yaml" curl -i -H "Content-Type: application/xml" -X POST -d '<id type="yaml">--- !ruby/object:ActionController::Base bar: 1</id>' --insecure https:localhost1. Edit Gemfile and change rails version from 2.3.14 to 2.3.15.
2. Commit and push changes
3. bundle exec cap deploy (this is our standard method and works well.)
4. Double check rails -v returns 2.3.15 on production server.
5. On the production server run
curl -i -H "Content-Type: application/xml" -X POST -d '<id type="yaml">--- !ruby/object:ActionController::Base bar: 1</id>' --insecure http://localhost
Result: Parameters: {"id"=>#<ActionController::Base:0xb570d2e8 @bar=1>}
Once I got the curl command to run against my production site (see my comment just below) and saw that it was still vulnerable, I quickly hacked ActionController::Base.param_parsers.delete(Mime::XML)
into environments.rb and re-deployed and that fixed it. Now when I run the curl command I get a parameter-less GET request in the log. I still do not understand why updating to 2.3.15 per the recommended method did not fix the problem, but at least our app doesn't need the xml in that way. "If you mean that your Ruby on Rails version is 1.2.6 then, no the vulnerability does not affect you as the feature was introduced in Ruby on Rails 2.0"
source: http://weblog.rubyonrails.org/2013/1/8/Rails-3-2-11-3-1-10-3...Thanks for the help!
Edit: fixed using above info from vinhboy. thanks heaps :)
But don't quote me on that...
I suspect I'm not the only person out there with legacy Rails Version 1 sites, so it would be very good to know the answer to that!
Has this never happened before? Has there never been an exploit of this significance that has made it to Metasploit, and why not?
Is there a quick way to fix this problem for sites running older versions of Rails? I can't be the only one who is in that situation. Frankly, provided it leaves some version of the website online, I'm OK with losing some functionality, even.
http://www.insinuator.net/2013/01/rails-yaml/
From the comments:
"From what i’ve see here is that versions below 2.0 should not be affected by this issue as they do not support xml parameters and don’t have any XMLMini implementations."
however, there are multiple PoCs floating around so it is fairly safe to assume attackers have access to it.