diff options
author | Nobuyoshi Nakada <nobu@ruby-lang.org> | 2021-06-04 10:38:43 +0900 |
---|---|---|
committer | Hiroshi SHIBATA <hsbt@ruby-lang.org> | 2021-07-27 09:25:56 +0900 |
commit | 803c60858e42463d33a00950883b17b1c38a2347 (patch) | |
tree | f487dd700b4087bda80b55032edaf3d6bb284e3e | |
parent | 88e46cf6b83e2ca701cf122756fef9bc2550e845 (diff) |
[rubygems/rubygems] Check requirements classes
Mitigate the security risk:
https://devcraft.io/2021/01/07/universal-deserialisation-gadget-for-ruby-2-x-3-x.html
https://github.com/rubygems/rubygems/commit/141c2f4388
-rw-r--r-- | lib/rubygems/requirement.rb | 2 | ||||
-rw-r--r-- | test/rubygems/test_gem_requirement.rb | 34 |
2 files changed, 36 insertions, 0 deletions
diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb index 0067f6304a..16de45ad91 100644 --- a/lib/rubygems/requirement.rb +++ b/lib/rubygems/requirement.rb @@ -199,6 +199,8 @@ class Gem::Requirement def marshal_load(array) # :nodoc: @requirements = array[0] + + raise TypeError, "wrong @requirements" unless Array === @requirements end def yaml_initialize(tag, vals) # :nodoc: diff --git a/test/rubygems/test_gem_requirement.rb b/test/rubygems/test_gem_requirement.rb index f32d13f3a9..b4367681d0 100644 --- a/test/rubygems/test_gem_requirement.rb +++ b/test/rubygems/test_gem_requirement.rb @@ -422,6 +422,40 @@ class TestGemRequirement < Gem::TestCase assert_requirement_hash_equal "1", "1.0.0" end + class Exploit < RuntimeError + end + + def self.exploit(arg) + raise Exploit, "arg = #{arg}" + end + + def test_marshal_load_attack + wa = Net::WriteAdapter.allocate + wa.instance_variable_set(:@socket, self.class) + wa.instance_variable_set(:@method_id, :exploit) + request_set = Gem::RequestSet.allocate + request_set.instance_variable_set(:@git_set, "id") + request_set.instance_variable_set(:@sets, wa) + wa = Net::WriteAdapter.allocate + wa.instance_variable_set(:@socket, request_set) + wa.instance_variable_set(:@method_id, :resolve) + ent = Gem::Package::TarReader::Entry.allocate + ent.instance_variable_set(:@read, 0) + ent.instance_variable_set(:@header, "aaa") + io = Net::BufferedIO.allocate + io.instance_variable_set(:@io, ent) + io.instance_variable_set(:@debug_output, wa) + reader = Gem::Package::TarReader.allocate + reader.instance_variable_set(:@io, io) + requirement = Gem::Requirement.allocate + requirement.instance_variable_set(:@requirements, reader) + m = [Gem::SpecFetcher, Gem::Installer, requirement] + e = assert_raise(TypeError) do + Marshal.load(Marshal.dump(m)) + end + assert_equal(e.message, "wrong @requirements") + end + # Assert that two requirements are equal. Handles Gem::Requirements, # strings, arrays, numbers, and versions. |