Bug 25965 - ruby-rubyzip new security issue CVE-2019-16892
Summary: ruby-rubyzip new security issue CVE-2019-16892
Status: RESOLVED OLD
Alias: None
Product: Mageia
Classification: Unclassified
Component: Security (show other bugs)
Version: 7
Hardware: All Linux
Priority: Normal major
Target Milestone: ---
Assignee: Nicolas Lécureuil
QA Contact: Sec team
URL:
Whiteboard:
Keywords: feedback
Depends on:
Blocks:
 
Reported: 2019-12-27 03:22 CET by David Walser
Modified: 2021-07-01 18:20 CEST (History)
2 users (show)

See Also:
Source RPM: ruby-rubyzip-1.2.2-1.mga7.src.rpm
CVE:
Status comment:


Attachments

Description David Walser 2019-12-27 03:22:04 CET
Fedora has issued an advisory on November 22:
https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/thread/J45KSFPP6DFVWLC7Z73L7SX735CKZYO6/

The issue is fixed upstream in 1.3.0.

Mageia 7 is also affected.
David Walser 2019-12-27 03:22:17 CET

Whiteboard: (none) => MGA7TOO

Comment 1 Lewis Smith 2019-12-27 10:26:38 CET
Ne registered or evident maintainer, so assigning globally.

Assignee: bugsquad => pkg-bugs

David Walser 2020-01-14 17:37:28 CET

Status comment: (none) => Fixed upstream in 1.3.0

Comment 2 Nicolas Lécureuil 2020-05-24 02:16:45 CEST
not valid on cauldron

CC: (none) => mageia
Version: Cauldron => 7
Whiteboard: MGA7TOO => (none)

Comment 3 David Walser 2020-05-24 02:28:34 CEST
Correction: was valid, has since been fixed by updating to 2.3.0.
Comment 4 Nicolas Lécureuil 2021-03-14 23:35:20 CET
fix pushed in mga7:

src:
    - ruby-rubyzip-1.2.2-1.1.mga7

Status comment: Fixed upstream in 1.3.0 => (none)
Assignee: pkg-bugs => qa-bugs

Comment 5 David Walser 2021-03-15 14:41:08 CET
Advisory:
========================

Updated ruby-rubyzip packages fix security vulnerability:

A vulnerability in Rubyzip, versions prior to 1.3.0, allows a crafted ZIP file
to bypass application checks on ZIP entry sizes. This allows an attacker to
spoof data regarding the uncompressed size of the ZIP file, causing a denial of
service due to disk consumption. Availability of the system is the highest
threat (CVE-2019-16892).

References:
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16892
https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/thread/J45KSFPP6DFVWLC7Z73L7SX735CKZYO6/
========================

Updated packages in core/updates_testing:
========================
ruby-rubyzip-1.2.2-1.1.mga7
ruby-rubyzip-doc-1.2.2-1.1.mga7

from ruby-rubyzip-1.2.2-1.1.mga7.src.rpm
Comment 6 Len Lawrence 2021-03-18 13:25:27 CET
There seems to be a problem here.
Before updating I wrote a minimal script to zip an archive using rubyzip, tested that and all was fine.
Updated the rubyzip package and found that the test script failed at the point of requiring zip.  The fault occurs in an irb session also.  Tried some of my homespun scripts and did not see this problem.

The message is always:
/usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require': /usr/share/gems/gems/rubyzip-1.2.2/lib/zip/entry.rb:701: syntax error, unexpected end-of-input, expecting keyword_end (SyntaxError)

Line 701 does correspond with the end of that particular script.  I scanned it by eye but could not spot the missing keyword or character.  I assume it could be a bracket, parenthesis or end.  When I have time I shall check it in detail but you might have quicker means.

In Mageia 8 there is no problem with the zip module but the read method has disappeared from entry.get_input_stream.

Keywords: (none) => feedback
CC: (none) => tarazed25

Comment 7 Len Lawrence 2021-03-18 18:28:57 CET
This is strange.  Just tried a script using the zip module and all was well.  It seems to have cured itself.  I see there is a new gem available called dead_end which I have just installed.  It is supposed to perform a run-time check for unmatched ends.
Comment 8 Len Lawrence 2021-03-18 19:11:30 CET
mga7, x64

https://github.com/rubyzip/rubyzip for coding examples.
There are local examples in /usr/share/gems/gems/rubyzip-1.2.2/test/

CVE-2019-

Got hold of the 42.zip zip bomb but it would not be wise to test this before updating.

Quick sample before updating:
$ cat makezip.rb
require 'zip'

HOME = Dir.home+"/qa/ruby/"
folder = HOME+ARGV[0]
input_filenames = Dir.entries( folder )
zipfile_name = HOME+"archive.zip"
Zip::File.open( zipfile_name, Zip::File::CREATE ) do |zipfile|
  input_filenames.each do |filename|
    # Two arguments:
    # - The name of the file as it will appear in the archive
    # - The original file, including the path to find it
    zipfile.add( filename, File.join( folder, filename ) )
  end
  zipfile.get_output_stream( "tarfile" ) { |f| f.write( "tarfile contains just this" ) }
end

Development directory is ~/qa/ruby/rubyzip.
$ mv ../rack rack_old
$ ruby makezip.rb rack
$ file archive.zip
archive.zip: Zip archive data, at least v2.0 to extract
$ unzip --ql archive.zip
Archive:  archive.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2021-03-18 09:42   ./
        0  2021-03-18 09:42   ../
      195  2021-03-18 09:42   hello.rb~
      497  2021-03-18 09:42   middle.rb
      195  2021-03-18 09:42   hello.rb
     2967  2021-03-18 09:42   hello
       26  2021-03-18 09:42   tarfile
---------                     -------
     3880                     7 files

$ mkdir ruby
$ mv archive.zip ruby
$ cd ruby
$ unzip archive.zip
Archive:  archive.zip
warning:  skipped "../" path component(s) in ../
  inflating: hello.rb~               
  inflating: middle.rb               
  inflating: hello.rb                
  inflating: hello                   
  inflating: tarfile                 
$ ls
archive.zip  hello  hello.rb  hello.rb~  middle.rb  tarfile
$ cat tarfile
tarfile contains just this

So ruby-rubyzip works fine.
Updated ruby-rubyzip.

Wrote a custom read script based on the github example.

CVE-2019-16892
Tried this, which before update would have been dangerous:
$ ruby readzip.rb 42.zip
Extracting lib 0.zip
Traceback (most recent call last):
	15: from readzip.rb:5:in `<main>'
	14: from /usr/share/gems/gems/rubyzip-1.2.2/lib/zip/file.rb:100:in `open'
	13: from readzip.rb:7:in `block in <main>'
	12: from /usr/share/gems/gems/rubyzip-1.2.2/lib/zip/central_directory.rb:182:in `each'
	11: from /usr/share/gems/gems/rubyzip-1.2.2/lib/zip/entry_set.rb:37:in `each'
	10: from /usr/share/gems/gems/rubyzip-1.2.2/lib/zip/entry_set.rb:37:in `each'
	 9: from /usr/share/gems/gems/rubyzip-1.2.2/lib/zip/entry_set.rb:38:in `block in each'
	 8: from readzip.rb:11:in `block (2 levels) in <main>'
	 7: from /usr/share/gems/gems/rubyzip-1.2.2/lib/zip/entry.rb:173:in `extract'
	 6: from /usr/share/gems/gems/rubyzip-1.2.2/lib/zip/entry.rb:601:in `create_file'
	 5: from /usr/share/gems/gems/rubyzip-1.2.2/lib/zip/entry.rb:601:in `open'
	 4: from /usr/share/gems/gems/rubyzip-1.2.2/lib/zip/entry.rb:602:in `block in create_file'
	 3: from /usr/share/gems/gems/rubyzip-1.2.2/lib/zip/entry.rb:518:in `get_input_stream'
	 2: from /usr/share/gems/gems/rubyzip-1.2.2/lib/zip/input_stream.rb:68:in `get_next_entry'
	 1: from /usr/share/gems/gems/rubyzip-1.2.2/lib/zip/input_stream.rb:137:in `open_entry'
/usr/share/gems/gems/rubyzip-1.2.2/lib/zip/input_stream.rb:156:in `get_decompressor': Unsupported compression method 99 (Zip::CompressionMethodError)

Looks like it successfully traps the zip bomb.

The zipit and readzip scripts are a bit unsophisticated but are OK for a demo.
$ ruby zipit.rb reports
$ ll update.zip
-rw-r--r-- 1 lcl lcl 8934 Mar 18  2021 update.zip
$ unzip -el update.zip
Archive:  update.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2021-03-18 17:34   ./
        0  2021-03-18 17:34   ../
       37  2021-03-18 17:34   report.19078
     2002  2021-03-18 17:34   report.22696
      163  2021-03-18 17:34   report.2337
     4501  2021-03-18 17:34   report.25897
     2440  2021-03-18 17:34   report.26408
     5109  2021-03-18 17:34   report.26409
     2547  2021-03-18 17:34   report.27401
     1147  2021-03-18 17:34   report.4
       27  2021-03-18 17:34   tarfile
---------                     -------
    17973                     11 files

$ rm -f reports/*
$ cp updates.zip reports
$ cd reports
$ ../readzip.rb update.zip
Extracting ./
WARNING: skipped ./ as unsafe
Extracting ../
WARNING: skipped ../ as unsafe
Extracting report.19078
Extracting report.22696
Extracting report.2337
Extracting report.25897
Extracting report.26408
Extracting report.26409
Extracting report.27401
Extracting report.4
Extracting tarfile
Zip::NullInputStream
$ ls
report.19078  report.2337   report.26408  report.27401  tarfile
report.22696  report.25897  report.26409  report.4      update.zip

This is OK.

Keywords: feedback => (none)
Whiteboard: (none) => MGA7-64-OK

Comment 9 Len Lawrence 2021-03-18 19:17:01 CET
Having to eat my words.  Just discovered that I had not actually updated the packages.  That is what comes of taking a lunch-break in the middle of testing.

If the zip module problem has not been fixed there is no point in repeating any of the tests - and note that the zip bomb did not crash the system.

Whiteboard: MGA7-64-OK => (none)
Keywords: (none) => feedback

Comment 10 Len Lawrence 2021-03-18 19:29:39 CET
This is the output with dead_end in an irb session:
DeadEnd: Missing `end` detected

This code has a missing `end`. Ensure that all
syntax keywords (`def`, `do`, etc.) have a matching `end`.

file: /usr/share/gems/gems/rubyzip-1.2.2/lib/zip/entry.rb
simplified:

        1  module Zip
        2    class Entry
        3      STORED   = 0
        4      DEFLATED = 8
        6      EFS = 0b100000000000
    ❯   8      attr_accessor :comment, :compressed_size, :crc, :extra, :compression_method,
    ❯   9                    :name, :size, :local_header_offset, :zipfile, :fstype, :external_file_attributes,
    ❯  10                    :internal_file_attributes,
    ❯  11                    :gp_flags, :header_signature, :follow_symlinks,
    ❯  12                    :restore_times, :restore_permissions, :restore_ownership,
    ❯  13                    :unix_uid, :unix_gid, :unix_perms,
    ❯  15      attr_reader :ftype, :filepath # :nodoc:
       17      def set_default_vars_values
       46      end
      145      def verify_local_header_size!
      149      end
    ❯ 151      def cdir_header_size #:nodoc:all
    ❯ 152        CDIR_ENTRY_STATIC_HEADER_LENGTH + name_size +
    ❯ 154      end
      156      def next_header_offset #:nodoc:all
      158      end
      185      class << self
      218      end
      220      public
    ❯ 222      def unpack_local_entry(buf)
    ❯ 223        @header_signature,
    ❯ 224          @version,
    ❯ 225          @fstype,
    ❯ 226          @gp_flags,
    ❯ 227          @compression_method,
    ❯ 228          @last_mod_time,
    ❯ 229          @last_mod_date,
    ❯ 230          @crc,
    ❯ 231          @compressed_size,
    ❯ 232          @size,
    ❯ 233          @name_length,
    ❯ 235      end
      237      def read_local_entry(io) #:nodoc:all
      272      end
      289      def write_local_entry(io, rewrite = false) #:nodoc:all
      299      end
    ❯ 301      def unpack_c_dir_entry(buf)
    ❯ 302        @header_signature,
    ❯ 303          @version, # version of encoding software
    ❯ 304          @fstype, # filesystem type
    ❯ 305          @version_needed_to_extract,
    ❯ 306          @gp_flags,
    ❯ 307          @compression_method,
    ❯ 308          @last_mod_time,
    ❯ 309          @last_mod_date,
    ❯ 310          @crc,
    ❯ 311          @compressed_size,
    ❯ 312          @size,
    ❯ 313          @name_length,
    ❯ 314          @extra_length,
    ❯ 315          @comment_length,
    ❯ 316          _, # diskNumberStart
    ❯ 317          @internal_file_attributes,
    ❯ 318          @external_file_attributes,
    ❯ 319          @local_header_offset,
    ❯ 320          @name,
    ❯ 321          @extra,
    ❯ 323      end
    ❯ 325      def set_ftype_from_c_dir_entry
    ❯ 326        @ftype = case @fstype
    ❯ 327                 when ::Zip::FSTYPE_UNIX
    ❯ 328                   @unix_perms = (@external_file_attributes >> 16) & 0o7777
    ❯ 345                 else
    ❯ 351                 end
    ❯ 352      end
    ❯ 354      def check_c_dir_entry_static_header_length(buf)
    ❯ 357      end
    ❯ 359      def check_c_dir_entry_signature
    ❯ 362      end
    ❯ 364      def check_c_dir_entry_comment_size
    ❯ 367      end
    ❯ 402      def get_extra_attributes_from_path(path) # :nodoc:
    ❯ 408      end
    ❯ 410      def set_unix_permissions_on_path(dest_path)
    ❯ 418      end
    ❯ 429      def pack_c_dir_entry
    ❯ 454      end
    ❯ 493      def <=>(other)
    ❯ 495      end
    ❯ 570      def parent_as_string
    ❯ 574      end
    ❯ 584      def clean_up
    ❯ 586      end
    ❯ 588      private
    ❯ 590      def set_time(binary_dos_date, binary_dos_time)
    ❯ 594      end
    ❯ 596      def create_file(dest_path, _continue_on_exists_proc = proc { Zip.continue_on_exists_proc })
    ❯ 597        if ::File.exist?(dest_path) && !yield(self, dest_path)
    ❯ 598          raise ::Zip::DestinationFileExistsError,
    ❯ 600        end
    ❯ 643      def create_symlink(dest_path)
    ❯ 647      end
    ❯ 660      def data_descriptor_size
    ❯ 662      end
      696    end
      697  end

I shall try to digest this later and fix it.
Comment 11 Nicolas Lécureuil 2021-03-18 20:26:17 CET
i will look if there us a pb with the patch.

Assignee: qa-bugs => mageia

Comment 12 David Walser 2021-06-29 01:09:52 CEST
Ping Nicolas.
Comment 13 David Walser 2021-07-01 18:20:30 CEST
https://blog.mageia.org/en/2021/06/08/mageia-7-will-reach-end-of-support-on-30th-of-june-the-king-is-dead-long-live-the-king/

Status: NEW => RESOLVED
Resolution: (none) => OLD


Note You need to log in before you can comment on or make changes to this bug.