Permalink
Browse files

Fix CVE-2013-4363, remove regexp backtracking

The Gem::Version regexp used backtracking to validate gem versions, but
in a different way than CVE-2013-4287. This could cause excessive CPU
usage when creating Gem::Version objects including when packaging gems.
See CVE-2013-4363.txt (in this commit) for details.

See #626
  • Loading branch information...
drbrain committed Sep 24, 2013
1 parent 5e809e4 commit 20325c134b5ca1928a15338eeb7ead1239dbf2b9
Showing with 76 additions and 13 deletions.
  1. +45 −0 CVE-2013-4363.txt
  2. +9 −0 History.txt
  3. +1 −0 Manifest.txt
  4. +1 −1 lib/rubygems/version.rb
  5. +11 −9 test/rubygems/test_gem_requirement.rb
  6. +9 −3 test/rubygems/test_gem_version.rb
View
@@ -0,0 +1,45 @@
= Algorithmic complexity vulnerability in RubyGems 2.1.4 and older
The patch for CVE-2013-4287 was insufficiently verified so the combined
regular expression for verifying gem version remains vulnerable following
CVE-2013-4287.
RubyGems validates versions with a regular expression that is vulnerable to
denial of service due to backtracking. For specially crafted RubyGems
versions attackers can cause denial of service through CPU consumption.
RubyGems versions 2.1.4 and older are vulnerable.
Ruby versions 1.9.0 through 2.0.0p247 are vulnerable as they contain embedded
versions of RubyGems.
It does not appear to be possible to exploit this vulnerability by installing a
gem for RubyGems 1.8.x or newer. Vulnerable uses of RubyGems API include
packaging a gem (through `gem build`, Gem::Package or Gem::PackageTask),
sending user input to Gem::Version.new, Gem::Version.correct? or use of the
Gem::Version::VERSION_PATTERN or Gem::Version::ANCHORED_VERSION_PATTERN
constants.
Notably, users of bundler that install gems from git are vulnerable if a
malicious author changes the gemspec to an invalid version.
The vulnerability can be fixed by changing the "*" repetition to a "?"
repetition in Gem::Version::ANCHORED_VERSION_PATTERN in
lib/rubygems/version.rb. For RubyGems 2.1.x:
- ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})*\s*\z/ # :nodoc:
+ ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/ # :nodoc:
For RubyGems 2.0.x:
- ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})*\s*\z/ # :nodoc:
+ ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/ # :nodoc:
For RubyGems 1.8.x:
- ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})*\s*\z/ # :nodoc:
+ ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/ # :nodoc:
This vulnerability was discovered by Alexander Cherepanov <cherepan@mccme.ru>
View
@@ -1,5 +1,14 @@
# coding: UTF-8
=== 2.0.10 / 2013-09-24
Security fixes:
* RubyGems 2.1.4 and earlier are vulnerable to excessive CPU usage due to a
backtracking in Gem::Version validation. See CVE-2013-4363 for full details
including vulnerable APIs. Fixed versions include 2.1.5, 2.0.10, 1.8.27 and
1.8.23.2 (for Ruby 1.9.3).
=== 2.0.9 / 2013-09-13
Bug fixes:
View
@@ -1,6 +1,7 @@
.autotest
.document
CVE-2013-4287.txt
CVE-2013-4363.txt
History.txt
LICENSE.txt
MIT.txt
View
@@ -148,7 +148,7 @@ class Gem::Version
# FIX: These are only used once, in .correct?. Do they deserve to be
# constants?
VERSION_PATTERN = '[0-9]+(?>\.[0-9a-zA-Z]+)*' # :nodoc:
ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})*\s*\z/ # :nodoc:
ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/ # :nodoc:
##
# A string representation of this Version.
@@ -47,18 +47,20 @@ def test_parse
end
def test_parse_bad
e = assert_raises Gem::Requirement::BadRequirementError do
Gem::Requirement.parse nil
end
assert_equal 'Illformed requirement [nil]', e.message
[
nil,
'',
'! 1',
'= junk',
'1..2',
].each do |bad|
e = assert_raises Gem::Requirement::BadRequirementError do
Gem::Requirement.parse bad
end
e = assert_raises Gem::Requirement::BadRequirementError do
Gem::Requirement.parse ""
assert_equal "Illformed requirement [#{bad.inspect}]", e.message
end
assert_equal 'Illformed requirement [""]', e.message
assert_equal Gem::Requirement::BadRequirementError.superclass, ArgumentError
end
@@ -67,12 +67,18 @@ def test_initialize
end
def test_initialize_bad
["junk", "1.0\n2.0"].each do |bad|
e = assert_raises ArgumentError do
%W[
junk
1.0\n2.0
1..2
1.2\ 3.4
1-2-3
].each do |bad|
e = assert_raises ArgumentError, bad do
Gem::Version.new bad
end
assert_equal "Malformed version number string #{bad}", e.message
assert_equal "Malformed version number string #{bad}", e.message, bad
end
end

0 comments on commit 20325c1

Please sign in to comment.