Hi, I've used the attached debdiff to fix CVE-2015-3225. Any review is welcome!
I've put packages to test here[1] in case anybody wants to try them. Besides verifying that the test suite still works with tests added to detect the issue I've used the package on a puppetmaster for testing. Cheers, -- Guido [1] http://honk.sigxcpu.org/projects/debian-lts/snapshots/
diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..a16803b --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,5 @@ +To run the tests use e.g.: + + RUBYLIB=lib rspec test/spec_rack_utils.rb + +with rspec + ruby-test-spec installed. diff --git a/debian/changelog b/debian/changelog index 643e6d1..a108214 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +librack-ruby (1.1.0-4+squeeze3) debian-lts; urgency=medium + + * CVE-2015-3225: Avoid infinite depth param normalization + Patch based on + http://seclists.org/oss-sec/2015/q2/729 + + -- Guido Günther <a...@sigxcpu.org> Fri, 19 Jun 2015 17:52:46 +0200 + librack-ruby (1.1.0-4+squeeze2) oldstable-security; urgency=high * Team upload. diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb index 3fb1663..72100fd 100644 --- a/lib/rack/utils.rb +++ b/lib/rack/utils.rb @@ -30,12 +30,17 @@ module Rack class << self attr_accessor :key_space_limit + attr_accessor :param_depth_limit end # The default number of bytes to allow parameter keys to take up. # This helps prevent a rogue client from flooding a Request. self.key_space_limit = 65536 + # Default depth at which the parameter parser will raise an exception for + # being too deep. This helps prevent SystemStackErrors + self.param_depth_limit = 100 + # Stolen from Mongrel, with some small modifications: # Parses a query string by breaking it up at the '&' # and ';' characters. You can also use this to parse @@ -98,7 +103,8 @@ module Rack end module_function :parse_nested_query - def normalize_params(params, name, v = nil) + def normalize_params(params, name, v = nil, depth = Utils.param_depth_limit) + raise RangeError if depth <= 0 if v and v =~ /^("|')(.*)\1$/ v = $2.gsub('\\'+$1, $1) end @@ -119,14 +125,14 @@ module Rack params[k] ||= [] raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key) - normalize_params(params[k].last, child_key, v) + normalize_params(params[k].last, child_key, v, depth - 1) else - params[k] << normalize_params({}, child_key, v) + params[k] << normalize_params({}, child_key, v, depth - 1) end else params[k] ||= {} raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash) - params[k] = normalize_params(params[k], after, v) + params[k] = normalize_params(params[k], after, v, depth - 1) end return params diff --git a/test/spec_rack_utils.rb b/test/spec_rack_utils.rb index 98c848c..7304f16 100644 --- a/test/spec_rack_utils.rb +++ b/test/spec_rack_utils.rb @@ -43,6 +43,18 @@ context "Rack::Utils" do Rack::Utils.parse_query("foo%3Dbaz=bar").should.equal "foo=baz" => "bar" end + specify "raise an exception if the params are too deep" do + len = Rack::Utils.param_depth_limit + + lambda { + Rack::Utils.parse_nested_query("foo#{"[a]" * len}=bar") + }.should.raise(RangeError) + + lambda { + Rack::Utils.parse_nested_query("foo#{"[a]" * (len - 1)}=bar") + }.should.not.raise + end + specify "should parse nested query strings correctly" do Rack::Utils.parse_nested_query("foo"). should.equal "foo" => nil