CVE-2018-1000076
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 Improper Verification of Cryptographic Signature vulnerability in package.rb that can result in a mis-signed gem could be installed, as the tarball would contain multiple gem signatures.. This vulnerability appears to have been fixed in 2.7.6.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Improper signature verification in RubyGems allows a mis-signed gem to be installed, bypassing cryptographic integrity checks.
Vulnerability
An improper verification of cryptographic signature vulnerability exists in RubyGems' package.rb across 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), and Ruby 2.5 series (2.5.0 and earlier), as well as prior to trunk revision 62422. The flaw allows a mis-signed gem to be installed because the tarball may contain multiple gem signatures, and the verification logic fails to properly validate them. This issue is identified as CVE-2018-1000076 [1][2][3][4].
Exploitation
An attacker who can craft a malicious gem package with multiple signatures can exploit this vulnerability. The attacker must be able to supply the gem to a target user or system, for example via a public gem server or social engineering. No special network position beyond delivering the gem is required; the victim need only install the gem using a vulnerable RubyGems version. The verification process does not correctly reject tarballs containing extra or conflicting signatures, allowing the mis-signed gem to pass integrity checks [1][4].
Impact
Successful exploitation results in installation of a gem whose signature is not properly authenticated. This can lead to arbitrary code execution at the privilege level of the user installing the gem, as the gem's code is run during installation or when loaded. The compromise undermines the cryptographic trust model of RubyGems, potentially enabling supply-chain attacks and unauthorized modifications to installed packages [1][4].
Mitigation
RubyGems fixed this vulnerability in version 2.7.6. Red Hat has released updated packages for Ruby 2.5 in Red Hat Software Collections (RHSA-2018:3731) and for Ruby 2.5 in Red Hat Enterprise Linux (RHSA-2020:0663 and related advisories) [1][2][3][4]. Users should upgrade to Ruby 2.7.6 or later, or apply the relevant vendor patches. No workaround is available; upgrade is the only mitigation.
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.
| Package | Affected versions | Patched versions |
|---|---|---|
rubygems-updateRubyGems | >= 2.2.0, < 2.7.6 | 2.7.6 |
org.jruby:jruby-stdlibMaven | < 9.1.16.0 | 9.1.16.0 |
Affected products
29- ghsa-coords29 versionspkg:gem/rubygems-updatepkg:maven/org.jruby/jruby-stdlibpkg:rpm/opensuse/ruby2.5&distro=openSUSE%20Leap%2015.0pkg:rpm/opensuse/ruby2.5&distro=openSUSE%20Leap%2015.1pkg:rpm/opensuse/ruby-bundled-gems-rpmhelper&distro=openSUSE%20Leap%2015.0pkg:rpm/opensuse/ruby-bundled-gems-rpmhelper&distro=openSUSE%20Leap%2015.1pkg:rpm/suse/ruby2.1&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/ruby2.1&distro=SUSE%20Enterprise%20Storage%205pkg:rpm/suse/ruby2.1&distro=SUSE%20Linux%20Enterprise%20Server%2012%20SP2-BCLpkg:rpm/suse/ruby2.1&distro=SUSE%20Linux%20Enterprise%20Server%2012%20SP2-LTSSpkg:rpm/suse/ruby2.1&distro=SUSE%20Linux%20Enterprise%20Server%2012%20SP3-BCLpkg:rpm/suse/ruby2.1&distro=SUSE%20Linux%20Enterprise%20Server%2012%20SP3-LTSSpkg:rpm/suse/ruby2.1&distro=SUSE%20Linux%20Enterprise%20Server%2012%20SP4pkg:rpm/suse/ruby2.1&distro=SUSE%20Linux%20Enterprise%20Server%2012%20SP5pkg:rpm/suse/ruby2.1&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2012%20SP2pkg:rpm/suse/ruby2.1&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2012%20SP3pkg:rpm/suse/ruby2.1&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2012%20SP4pkg:rpm/suse/ruby2.1&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2012%20SP5pkg:rpm/suse/ruby2.1&distro=SUSE%20Linux%20Enterprise%20Software%20Development%20Kit%2012%20SP4pkg:rpm/suse/ruby2.1&distro=SUSE%20Linux%20Enterprise%20Software%20Development%20Kit%2012%20SP5pkg:rpm/suse/ruby2.1&distro=SUSE%20OpenStack%20Cloud%207pkg:rpm/suse/ruby2.1&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/ruby2.1&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/ruby2.5&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Basesystem%2015pkg:rpm/suse/ruby2.5&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Basesystem%2015%20SP1pkg:rpm/suse/yast2-ruby-bindings&distro=SUSE%20Linux%20Enterprise%20Server%2012%20SP2-BCLpkg:rpm/suse/yast2-ruby-bindings&distro=SUSE%20Linux%20Enterprise%20Server%2012%20SP2-LTSSpkg:rpm/suse/yast2-ruby-bindings&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2012%20SP2pkg:rpm/suse/yast2-ruby-bindings&distro=SUSE%20OpenStack%20Cloud%207
>= 2.2.0, < 2.7.6+ 28 more
- (no CPE)range: >= 2.2.0, < 2.7.6
- (no CPE)range: < 9.1.16.0
- (no CPE)range: < 2.5.5-lp151.4.3.1
- (no CPE)range: < 2.5.5-lp151.4.3.1
- (no CPE)range: < 0.0.2-lp151.2.1
- (no CPE)range: < 0.0.2-lp151.2.1
- (no CPE)range: < 2.1.9-19.3.2
- (no CPE)range: < 2.1.9-19.3.2
- (no CPE)range: < 2.1.9-19.3.2
- (no CPE)range: < 2.1.9-19.3.2
- (no CPE)range: < 2.1.9-19.3.2
- (no CPE)range: < 2.1.9-19.3.2
- (no CPE)range: < 2.1.9-19.3.2
- (no CPE)range: < 2.1.9-19.3.2
- (no CPE)range: < 2.1.9-19.3.2
- (no CPE)range: < 2.1.9-19.3.2
- (no CPE)range: < 2.1.9-19.3.2
- (no CPE)range: < 2.1.9-19.3.2
- (no CPE)range: < 2.1.9-19.3.2
- (no CPE)range: < 2.1.9-19.3.2
- (no CPE)range: < 2.1.9-19.3.2
- (no CPE)range: < 2.1.9-19.3.2
- (no CPE)range: < 2.1.9-19.3.2
- (no CPE)range: < 2.5.5-4.3.1
- (no CPE)range: < 2.5.5-4.3.1
- (no CPE)range: < 3.1.53-9.8.1
- (no CPE)range: < 3.1.53-9.8.1
- (no CPE)range: < 3.1.53-9.8.1
- (no CPE)range: < 3.1.53-9.8.1
Patches
20b06b48ab443Apply backported RubyGems 2.7.6 patch.
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
f5042b879259Raise a security error when there are duplicate files in a package
3 files changed · +39 −1
lib/rubygems/package.rb+4 −0 modified@@ -603,6 +603,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 ##
lib/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
test/rubygems/test_gem_package.rb+33 −1 modified@@ -738,6 +738,32 @@ def test_verify_nonexistent assert_match %r%nonexistent.gem$%, e.message end + def test_verify_duplicate_file + FileUtils.mkdir_p 'lib' + FileUtils.touch 'lib/code.rb' + + build = Gem::Package.new @gem + build.spec = @spec + build.setup_signer + open @gem, 'wb' do |gem_io| + Gem::Package::TarWriter.new gem_io do |gem| + build.add_metadata gem + build.add_contents gem + + gem.add_file_simple 'a.sig', 0444, 0 + gem.add_file_simple 'a.sig', 0444, 0 + end + end + + package = Gem::Package.new @gem + + e = assert_raises Gem::Security::Exception do + package.verify + end + + assert_equal 'duplicate files in the package: ("a.sig")', e.message + end + def test_verify_security_policy skip 'openssl is missing' unless defined?(OpenSSL::SSL) @@ -795,7 +821,13 @@ def test_verify_security_policy_checksum_missing # write bogus data.tar.gz to foil signature bogus_data = Gem.gzip 'hello' - gem.add_file_simple 'data.tar.gz', 0444, bogus_data.length do |io| + fake_signer = Class.new do + def digest_name; 'SHA512'; end + def digest_algorithm; Digest(:SHA512); end + def key; 'key'; end + def sign(*); 'fake_sig'; end + end + gem.add_file_signed 'data2.tar.gz', 0444, fake_signer.new do |io| io.write bogus_data end
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
22- lists.opensuse.org/opensuse-security-announce/2019-07/msg00036.htmlghsavendor-advisoryx_refsource_SUSEWEB
- access.redhat.com/errata/RHSA-2018:3729ghsavendor-advisoryx_refsource_REDHATWEB
- access.redhat.com/errata/RHSA-2018:3730ghsavendor-advisoryx_refsource_REDHATWEB
- access.redhat.com/errata/RHSA-2018:3731ghsavendor-advisoryx_refsource_REDHATWEB
- access.redhat.com/errata/RHSA-2019:2028ghsavendor-advisoryx_refsource_REDHATWEB
- access.redhat.com/errata/RHSA-2020:0542ghsavendor-advisoryx_refsource_REDHATWEB
- access.redhat.com/errata/RHSA-2020:0591ghsavendor-advisoryx_refsource_REDHATWEB
- access.redhat.com/errata/RHSA-2020:0663ghsavendor-advisoryx_refsource_REDHATWEB
- github.com/advisories/GHSA-mc6j-h948-v2p6ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2018-1000076ghsaADVISORY
- usn.ubuntu.com/3621-1/mitrevendor-advisoryx_refsource_UBUNTU
- www.debian.org/security/2018/dsa-4219ghsavendor-advisoryx_refsource_DEBIANWEB
- www.debian.org/security/2018/dsa-4259ghsavendor-advisoryx_refsource_DEBIANWEB
- blog.rubygems.org/2018/02/15/2.7.6-released.htmlghsax_refsource_MISCWEB
- github.com/jruby/jruby/commit/0b06b48ab4432237ce5fc1bef47f2c6bcf7843f7ghsaWEB
- github.com/rubygems/rubygems/commit/f5042b879259b1f1ce95a0c5082622c646376693ghsax_refsource_MISCWEB
- lists.debian.org/debian-lts-announce/2018/04/msg00000.htmlghsamailing-listx_refsource_MLISTWEB
- lists.debian.org/debian-lts-announce/2018/04/msg00001.htmlghsamailing-listx_refsource_MLISTWEB
- lists.debian.org/debian-lts-announce/2018/04/msg00023.htmlghsamailing-listx_refsource_MLISTWEB
- lists.debian.org/debian-lts-announce/2018/07/msg00012.htmlghsamailing-listx_refsource_MLISTWEB
- lists.debian.org/debian-lts-announce/2019/05/msg00028.htmlghsamailing-listx_refsource_MLISTWEB
- usn.ubuntu.com/3621-1ghsaWEB
News mentions
0No linked articles in our index yet.