CVE-2018-1000079
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 Directory Traversal vulnerability in gem installation that can result in the gem could write to arbitrary filesystem locations during installation. This attack appear to be exploitable via the victim must install a malicious gem. This vulnerability appears to have been fixed in 2.7.6.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Directory traversal vulnerability in RubyGems allows arbitrary file write during gem installation via a malicious gem.
Vulnerability
CVE-2018-1000079 is a directory traversal vulnerability in RubyGems' gem installation process. The flaw lies in how RubyGems handles path names when extracting gem contents, allowing a malicious gem to write files to arbitrary filesystem locations outside the intended installation directory. This affects 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 [2][4].
Exploitation
An attacker can exploit this vulnerability by crafting a malicious gem with specially crafted file paths that traverse directories. The victim must install the malicious gem, either by downloading it from an untrusted source or being tricked into installing it. No additional authentication or network position is required beyond what is necessary to install the gem.
Impact
Successful exploitation allows the attacker to write arbitrary files to the victim's filesystem at attacker-controlled locations. This could lead to overwriting system files, configuration files, or injecting malicious code, potentially resulting in arbitrary code execution with the privileges of the user running the gem installation.
Mitigation
The vulnerability is fixed in Ruby 2.7.6 and later. Red Hat has released patches in RHSA-2018:3731, RHSA-2018:3730, RHSA-2018:3729, and RHSA-2020:0663 for affected Red Hat Enterprise Linux versions [1][2][3][4]. Users should update Ruby or RubyGems to the latest patched version. If immediate update is not possible, avoid installing gems from untrusted sources.
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.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.7.6+ 28 more
- (no CPE)range: < 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
40b06b48ab443Apply 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
5971b486d4dbVersion 2.7.6 with changelog
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
666ef793cad4Implement a safe mkdir for package that verifies were inside the destination dir for all new directories ccreated
2 files changed · +29 −6
lib/rubygems/package.rb+28 −5 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 File.open destination, 'wb' do |out| out.write entry.read @@ -416,13 +416,10 @@ 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.realpath destination if - File.respond_to? :realpath destination = File.expand_path destination raise Gem::Package::PathError.new(destination, destination_dir) unless @@ -432,6 +429,22 @@ def install_location filename, destination_dir # :nodoc: 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+ @@ -622,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'
test/rubygems/test_gem_package.rb+1 −1 modified@@ -476,7 +476,7 @@ def test_extract_symlink_parent package.extract_tar_gz tgz_io, destination_subdir end - assert_equal("installing into parent path ../outside.txt of " + + assert_equal("installing into parent path lib/link/outside.txt of " + "#{destination_subdir} is not allowed", e.message) end
f83f911e19e2Add bug fix for #270068
1 file changed · +1 −1
lib/rubygems/package.rb+1 −1 modified@@ -424,7 +424,7 @@ def install_location filename, destination_dir # :nodoc: 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
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-8qxg-mff5-j3wcghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2018-1000079ghsaADVISORY
- 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
- bugs.debian.org/cgi-bin/bugreport.cgighsaWEB
- github.com/jruby/jruby/commit/0b06b48ab4432237ce5fc1bef47f2c6bcf7843f7ghsaWEB
- github.com/rubygems/rubygems/commit/5971b486d4dbb2bad5d3445b3801c456eb0ce183ghsaWEB
- github.com/rubygems/rubygems/commit/666ef793cad42eed96f7aee1cdf77865db921099ghsax_refsource_MISCWEB
- github.com/rubygems/rubygems/commit/f83f911e19e27cbac1ccce7471d96642241dd759ghsax_refsource_MISCWEB
- lists.debian.org/debian-lts-announce/2018/07/msg00012.htmlghsamailing-listx_refsource_MLISTWEB
- security-tracker.debian.org/tracker/CVE-2018-1000079ghsaWEB
- usn.ubuntu.com/3621-1ghsaWEB
News mentions
0No linked articles in our index yet.