Thursday, August 10, 2006

Light over Ruby on Rails "critical security upgrade" 1.1.5

Yesterday, the core development team of the Ruby on Rails framework, announced in their weblog a critical security upgrade (1.1.5) which should be applied immediately.

*1.1.5* (August 8th, 2006)

* Mention in docs that config.frameworks doesn't work when getting Rails via Gems. #4857 [Alisdair McDiarmid]

* Change the scaffolding layout to use yield rather than @content_for_layout. [Marcel Molina Jr.]

* Includes critical security patch

Without giving details on the issue ("The issue is in fact of such a criticality that we’re not going to dig into the specifics. No need to arm would-be assalients") some people started reviewing the diff between the 1.1.5 and 1.1.4 releases, right away from the Subversion repository. A few hot spots have been indentified so far, one of them being a clear example of a potentially exploitable condition:


@@ -268,7 +273,7 @@
$LOAD_PATH.select do |base|

base = File.expand_path(base)

extended_root = File.expand_path(RAILS_ROOT)

- base[0, extended_root.length] == extended_root || base =~ %r{rails-[\d.]+/builtin}

+ base.match(/\A#{Regexp.escape(extended_root)}\/*#{file_kinds(:lib) * '|'}/)

|| base =~ %r{rails-[\d.]+/builtin}

end

else

$LOAD_PATH

Some already have explained the issue (thanks Brandon for the links), although the development of a proof of concept by some Russian folks brought new issues to the attention of some RoR users. Basically, the fix introduced new problems, apparently.

The original issue was about the possibility of forcing Ruby to load arbitrary code after uploading a file and injecting a path to the LOAD_PATH variable through the HTTP_LOAD_PATH header (which is managed client-side... thus the user has control over it). The flaw in the routing code would allow this file (ex. a controller) to be loaded and executed. This would happen when requesting an URL with the controller name and one of it's methods, leadind the routing engine to walk through LOAD_PATH for the file and then executing it: http://railshost.tld/evil_controller/the_method.

Side-note: If $SAFE mode is enabled, at it's lowest level it's supposed to prevent code from being executed from world-writable directories, thus it shouldn't work from directories like /tmp. This probably doesn't apply to certain win32 installations anyways.

The newly discovered issues are related to loading built-in (already available) code through the autorouting engine, this causes different kinds of situations, from crashes to infinite recursion bugs (leading to the infamous 'Stack level too deep' error).

Nope, guys, the routing problems aren't fully fixed, and one still can require about 500 .rb files from standard Rails vendor/* directories just typing some text as URL in browser.

Apparently, a bug report ticket was submitted time ago:

Where is test code for that "patch"?

And there was already a ticket "#5408 Unhandled urls can cause loading of arbitrary ruby files" on Rails TRAC from 06/16 about mentioned issues...

Although, a follow-up comments that 1.1.5 upgrade fixes the situation for everything except Webrick:

The rails team is trying to contain damage. The fix works on everything except for webrick.

While the current status might have a lower immediate risk, controllers are expected to work all-together with other code, thus managing to execute methods within this controllers could still lead to exploitable conditions (ex. methods involved on filesystem-related operations) and such cases might be waiting for someone to take advantage of them.

Some light over the whole story comes with a new follow-up, explaining that actually, directories containing 'lib' will be used for the loading procedure, leading to an exploitable condition (but just a bit more complicated than the original one). This applies if HTTP_LOAD_PATH is still exposed and has direct influence over LOAD_PATH.

actionpack-1.12.4\lib\action_controller\routing.rb: 276

base.match(/\A#{Regexp.escape(extended_root)}\/*#{file_kinds(:lib) * '|'}/) || base =~ %r{rails-[\d.]+/builtin}

base.match(/\A#{Regexp.escape(extended_root)}\/*(?:#{file_kinds(:lib) * '|'})/) || base =~ %r{rails-[\d.]+/builtin}

An user reported that it affected his Mongrel installation too (Maybe Webrick isn't the only one affected? and what about Lighttpd?).

Regular expressions can be are evil.

Another comment on the original announcement refers to the ticket number 3030 (nice one...), but right now the machine serving the RoR development Trac installation seems to be offline or failing to handle requests. Fortunately, Google cache entry exists.

[PATCH] method_missing exposes private and protected actions

I was recently trying to create a page that shows something other than the default "Unknown Action" message when a user calls an action that isn't defined in the controller. I used the method_missing method to do so which worked as advertised. However, I created an action declared as private in that same controller and the private action is available to anyone including via the web.

While some people might argue that this makes RoR not suitable for production, every so-called web application framework is affected by security flaws at some point (ex. now). Even those considered to be in a mature state of development. Some issues might be known, others might have gone wild.

Anyways, probably a 1.1.6 upgrade is coming soon. Hopefully next time everything will be documented properly, thus users won't have to figure out the problem by themselves, in order to decide if the fix is truly necessary.

The RoR team has already done a great job for bringing such a flexible and nice framework, not even mentioning the Ruby guys. Please, keep up the nice work ;-)

0 comments: