VYPR
High severityNVD Advisory· Published Mar 13, 2018· Updated Aug 5, 2024

CVE-2018-1000075

CVE-2018-1000075

Description

RubyGems version Ruby 2.2 series: 2.2.9 and earlier, Ruby 2.3 series: 2.3.6 and earlier, Ruby 2.4 series: 2.4.3 and earlier, Ruby 2.5 series: 2.5.0 and earlier, prior to trunk revision 62422 contains a infinite loop caused by negative size vulnerability in ruby gem package tar header that can result in a negative size could cause an infinite loop.. This vulnerability appears to have been fixed in 2.7.6.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

RubyGems in Ruby 2.2-2.5 has a denial-of-service vulnerability via a crafted gem tar header with a negative size causing an infinite loop.

Vulnerability

The RubyGem tar header parser in Ruby versions 2.2.9 and earlier, 2.3.6 and earlier, 2.4.3 and earlier, and 2.5.0 and earlier prior to trunk revision 62422 does not properly validate the size field. A negative size in the tar header triggers an infinite loop during gem installation, leading to a denial of service [1] [4].

Exploitation

An attacker can craft a malicious gem with a tar header containing a negative size value. When a user or automated process installs the gem via gem install, the RubyGems library enters an infinite loop, consuming CPU resources and hanging the process. No authentication or special privileges are required; only user interaction (installing the gem) is needed [1] [4].

Impact

Successful exploitation results in a denial of service condition. The Ruby process becomes unresponsive, which can disrupt services or development workflows. The impact is limited to availability; no data confidentiality or integrity is compromised.

Mitigation

Upgrade to Ruby 2.7.6 or later, which includes the fix for this vulnerability. Red Hat has provided updated packages via RHSA-2018:3731, RHSA-2018:3730, RHSA-2018:3729, and RHSA-2020:0663 for affected RHEL versions [1] [4]. If upgrading is not possible, apply the patch from trunk revision 62422 or avoid installing gems from untrusted sources as a workaround.

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
rubygems-updateRubyGems
< 2.7.62.7.6
org.jruby:jruby-stdlibMaven
< 9.1.16.09.1.16.0

Affected products

29

Patches

3
0b06b48ab443

Apply backported RubyGems 2.7.6 patch.

https://github.com/jruby/jrubyCharles Oliver NutterFeb 16, 2018via ghsa
7 files changed · +75 20
  • lib/ruby/stdlib/rubygems/commands/owner_command.rb+1 1 modified
    @@ -62,7 +62,7 @@ def show_owners name
         end
     
         with_response response do |resp|
    -      owners = YAML.load resp.body
    +      owners = Gem::SafeYAML.load resp.body
     
           say "Owners for gem: #{name}"
           owners.each do |owner|
    
  • lib/ruby/stdlib/rubygems/package.rb+33 4 modified
    @@ -378,7 +378,7 @@ def extract_tar_gz io, destination_dir, pattern = "*" # :nodoc:
                 File.dirname destination
               end
     
    -        FileUtils.mkdir_p mkdir, mkdir_options
    +        mkdir_p_safe mkdir, mkdir_options, destination_dir, entry.full_name
     
             open destination, 'wb' do |out|
               out.write entry.read
    @@ -416,20 +416,35 @@ def install_location filename, destination_dir # :nodoc:
         raise Gem::Package::PathError.new(filename, destination_dir) if
           filename.start_with? '/'
     
    -    destination_dir = File.realpath destination_dir if
    -      File.respond_to? :realpath
    +    destination_dir = realpath destination_dir
         destination_dir = File.expand_path destination_dir
     
         destination = File.join destination_dir, filename
         destination = File.expand_path destination
     
         raise Gem::Package::PathError.new(destination, destination_dir) unless
    -      destination.start_with? destination_dir
    +      destination.start_with? destination_dir + '/'
     
         destination.untaint
         destination
       end
     
    +  def mkdir_p_safe mkdir, mkdir_options, destination_dir, file_name
    +    destination_dir = realpath File.expand_path(destination_dir)
    +    parts = mkdir.split(File::SEPARATOR)
    +    parts.reduce do |path, basename|
    +      path = realpath path  unless path == ""
    +      path = File.expand_path(path + File::SEPARATOR + basename)
    +      lstat = File.lstat path rescue nil
    +      if !lstat || !lstat.directory?
    +        unless path.start_with? destination_dir and (FileUtils.mkdir path, mkdir_options rescue false)
    +          raise Gem::Package::PathError.new(file_name, destination_dir)
    +        end
    +      end
    +      path
    +    end
    +  end
    +
       ##
       # Loads a Gem::Specification from the TarEntry +entry+
     
    @@ -603,6 +618,10 @@ def verify_files gem
           raise Gem::Package::FormatError.new \
                   'package content (data.tar.gz) is missing', @gem
         end
    +
    +    if duplicates = @files.group_by {|f| f }.select {|k,v| v.size > 1 }.map(&:first) and duplicates.any?
    +      raise Gem::Security::Exception, "duplicate files in the package: (#{duplicates.map(&:inspect).join(', ')})"
    +    end
       end
     
       ##
    @@ -616,6 +635,16 @@ def verify_gz entry # :nodoc:
         raise Gem::Package::FormatError.new(e.message, entry.full_name)
       end
     
    +  if File.respond_to? :realpath
    +    def realpath file
    +      File.realpath file
    +    end
    +  else
    +    def realpath file
    +      file
    +    end
    +  end
    +
     end
     
     require 'rubygems/package/digest_io'
    
  • lib/ruby/stdlib/rubygems/package/tar_header.rb+14 9 modified
    @@ -104,25 +104,30 @@ def self.from(stream)
         fields = header.unpack UNPACK_FORMAT
     
         new :name     => fields.shift,
    -        :mode     => fields.shift.oct,
    -        :uid      => fields.shift.oct,
    -        :gid      => fields.shift.oct,
    -        :size     => fields.shift.oct,
    -        :mtime    => fields.shift.oct,
    -        :checksum => fields.shift.oct,
    +        :mode     => strict_oct(fields.shift),
    +        :uid      => strict_oct(fields.shift),
    +        :gid      => strict_oct(fields.shift),
    +        :size     => strict_oct(fields.shift),
    +        :mtime    => strict_oct(fields.shift),
    +        :checksum => strict_oct(fields.shift),
             :typeflag => fields.shift,
             :linkname => fields.shift,
             :magic    => fields.shift,
    -        :version  => fields.shift.oct,
    +        :version  => strict_oct(fields.shift),
             :uname    => fields.shift,
             :gname    => fields.shift,
    -        :devmajor => fields.shift.oct,
    -        :devminor => fields.shift.oct,
    +        :devmajor => strict_oct(fields.shift),
    +        :devminor => strict_oct(fields.shift),
             :prefix   => fields.shift,
     
             :empty => empty
       end
     
    +  def self.strict_oct(str)
    +    return str.oct if str =~ /\A[0-7]*\z/
    +    raise ArgumentError, "#{str.inspect} is not an octal string"
    +  end
    +
       ##
       # Creates a new TarHeader using +vals+
     
    
  • lib/ruby/stdlib/rubygems/package/tar_writer.rb+2 0 modified
    @@ -196,6 +196,8 @@ def add_file_signed name, mode, signer
           digest_name == signer.digest_name
         end
     
    +    raise "no #{signer.digest_name} in #{digests.values.compact}" unless signature_digest
    +
         if signer.key then
           signature = signer.sign signature_digest.digest
     
    
  • lib/ruby/stdlib/rubygems.rb+1 1 modified
    @@ -10,7 +10,7 @@
     require 'thread'
     
     module Gem
    -  VERSION = "2.6.14"
    +  VERSION = "2.6.14.1"
     end
     
     # Must be first since it unloads the prelude from 1.9.2
    
  • lib/ruby/stdlib/rubygems/server.rb+13 1 modified
    @@ -631,6 +631,18 @@ def root(req, res)
           executables = nil if executables.empty?
           executables.last["is_last"] = true if executables
     
    +      # Pre-process spec homepage for safety reasons
    +      begin
    +        homepage_uri = URI.parse(spec.homepage)
    +        if [URI::HTTP, URI::HTTPS].member? homepage_uri.class
    +          homepage_uri = spec.homepage
    +        else
    +          homepage_uri = "."
    +        end
    +      rescue URI::InvalidURIError
    +        homepage_uri = "."
    +      end
    +
           specs << {
             "authors"             => spec.authors.sort.join(", "),
             "date"                => spec.date.to_s,
    @@ -640,7 +652,7 @@ def root(req, res)
             "only_one_executable" => (executables && executables.size == 1),
             "full_name"           => spec.full_name,
             "has_deps"            => !deps.empty?,
    -        "homepage"            => spec.homepage,
    +        "homepage"            => homepage_uri,
             "name"                => spec.name,
             "rdoc_installed"      => Gem::RDoc.new(spec).rdoc_installed?,
             "ri_installed"        => Gem::RDoc.new(spec).ri_installed?,
    
  • lib/ruby/stdlib/rubygems/specification.rb+11 4 modified
    @@ -15,6 +15,7 @@
     require 'rubygems/stub_specification'
     require 'rubygems/util/list'
     require 'stringio'
    +require 'uri'
     
     ##
     # The Specification class contains the information for a Gem.  Typically
    @@ -2810,10 +2811,16 @@ def validate packaging = true
           raise Gem::InvalidSpecificationException, "#{lazy} is not a summary"
         end
     
    -    if homepage and not homepage.empty? and
    -       homepage !~ /\A[a-z][a-z\d+.-]*:/i then
    -      raise Gem::InvalidSpecificationException,
    -            "\"#{homepage}\" is not a URI"
    +    # Make sure a homepage is valid HTTP/HTTPS URI
    +    if homepage and not homepage.empty?
    +      begin
    +        homepage_uri = URI.parse(homepage)
    +        unless [URI::HTTP, URI::HTTPS].member? homepage_uri.class
    +          raise Gem::InvalidSpecificationException, "\"#{homepage}\" is not a valid HTTP URI"
    +        end
    +      rescue URI::InvalidURIError
    +        raise Gem::InvalidSpecificationException, "\"#{homepage}\" is not a valid HTTP URI"
    +      end
         end
     
         # Warnings
    
5971b486d4db

Version 2.7.6 with changelog

https://github.com/rubygems/rubygemsSamuel GiddinsFeb 16, 2018via ghsa
2 files changed · +20 1
  • History.txt+19 0 modified
    @@ -1,5 +1,24 @@
     # coding: UTF-8
     
    +=== 2.7.6 / 2018-02-16
    +
    +Security fixes:
    +
    +* Prevent path traversal when writing to a symlinked basedir outside of the root.
    +  Discovered by nmalkin, fixed by Jonathan Claudius and Samuel Giddins.
    +* Fix possible Unsafe Object Deserialization Vulnerability in gem owner.
    +  Fixed by Jonathan Claudius.
    +* Strictly interpret octal fields in tar headers.
    +  Discoved by plover, fixed by Samuel Giddins.
    +* Raise a security error when there are duplicate files in a package.
    +  Discovered by plover, fixed by Samuel Giddins.
    +* Enforce URL validation on spec homepage attribute.
    +  Discovered by Yasin Soliman, fixed by Jonathan Claudius.
    +* Mitigate XSS vulnerability in homepage attribute when displayed via `gem server`.
    +  Discovered by Yasin Soliman, fixed by Jonathan Claudius.
    +* Prevent Path Traversal issue during gem installation.
    +  Discovered by nmalkin.
    +
     === 2.7.4
     
     Bug fixes:
    
  • lib/rubygems.rb+1 1 modified
    @@ -10,7 +10,7 @@
     require 'thread'
     
     module Gem
    -  VERSION = "2.7.5"
    +  VERSION = "2.7.6"
     end
     
     # Must be first since it unloads the prelude from 1.9.2
    
92e98bf8f810

Strictly interpret octal fields in tar headers

https://github.com/rubygems/rubygemsSamuel GiddinsFeb 12, 2018via ghsa
2 files changed · +34 9
  • lib/rubygems/package/tar_header.rb+14 9 modified
    @@ -104,25 +104,30 @@ def self.from(stream)
         fields = header.unpack UNPACK_FORMAT
     
         new :name     => fields.shift,
    -        :mode     => fields.shift.oct,
    -        :uid      => fields.shift.oct,
    -        :gid      => fields.shift.oct,
    -        :size     => fields.shift.oct,
    -        :mtime    => fields.shift.oct,
    -        :checksum => fields.shift.oct,
    +        :mode     => strict_oct(fields.shift),
    +        :uid      => strict_oct(fields.shift),
    +        :gid      => strict_oct(fields.shift),
    +        :size     => strict_oct(fields.shift),
    +        :mtime    => strict_oct(fields.shift),
    +        :checksum => strict_oct(fields.shift),
             :typeflag => fields.shift,
             :linkname => fields.shift,
             :magic    => fields.shift,
    -        :version  => fields.shift.oct,
    +        :version  => strict_oct(fields.shift),
             :uname    => fields.shift,
             :gname    => fields.shift,
    -        :devmajor => fields.shift.oct,
    -        :devminor => fields.shift.oct,
    +        :devmajor => strict_oct(fields.shift),
    +        :devminor => strict_oct(fields.shift),
             :prefix   => fields.shift,
     
             :empty => empty
       end
     
    +  def self.strict_oct(str)
    +    return str.oct if str =~ /\A[0-7]*\z/
    +    raise ArgumentError, "#{str.inspect} is not an octal string"
    +  end
    +
       ##
       # Creates a new TarHeader using +vals+
     
    
  • test/rubygems/test_gem_package_tar_header.rb+20 0 modified
    @@ -143,5 +143,25 @@ def test_update_checksum
         assert_equal '012467', @tar_header.checksum
       end
     
    +  def test_from_bad_octal
    +    test_cases = [
    +      "00000006,44\000", # bogus character
    +      "00000006789\000", # non-octal digit
    +      "+0000001234\000", # positive sign
    +      "-0000001000\000", # negative sign
    +      "0x000123abc\000", # radix prefix
    +    ]
    +
    +    test_cases.each do |val|
    +      header_s = @tar_header.to_s
    +      # overwrite the size field
    +      header_s[124, 12] = val
    +      io = TempIO.new header_s
    +      assert_raises ArgumentError do
    +        new_header = Gem::Package::TarHeader.from io
    +      end
    +    end
    +  end
    +
     end
     
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

23

News mentions

0

No linked articles in our index yet.