From d16062d940b24371ff2db74d0e4dbe67ca19525f Mon Sep 17 00:00:00 2001 From: shixuantong Date: Sat, 31 Jul 2021 15:20:57 +0800 Subject: [PATCH] fix CVE-2021-31799 CVE-2021-31810 CVE-2021-32066 --- backport-CVE-2021-31799.patch | 51 +++++++ backport-CVE-2021-31810.patch | 248 ++++++++++++++++++++++++++++++++++ backport-CVE-2021-32066.patch | 95 +++++++++++++ ruby.spec | 8 +- 4 files changed, 401 insertions(+), 1 deletion(-) create mode 100644 backport-CVE-2021-31799.patch create mode 100644 backport-CVE-2021-31810.patch create mode 100644 backport-CVE-2021-32066.patch diff --git a/backport-CVE-2021-31799.patch b/backport-CVE-2021-31799.patch new file mode 100644 index 0000000..42bffe5 --- /dev/null +++ b/backport-CVE-2021-31799.patch @@ -0,0 +1,51 @@ +From a7f5d6ab88632b3b482fe10611382ff73d14eed7 Mon Sep 17 00:00:00 2001 +From: aycabta +Date: Sun, 2 May 2021 20:52:23 +0900 +Subject: [PATCH] Use File.open to fix the OS Command Injection vulnerability + in CVE-2021-31799 + +Reference:https://github.com/ruby/rdoc/commit/a7f5d6ab88632b3b482fe10611382ff73d14eed7 +--- + lib/rdoc/rdoc.rb | 2 +- + test/rdoc/test_rdoc_rdoc.rb | 12 ++++++++++++ + 2 files changed, 13 insertions(+), 1 deletion(-) + +diff --git a/lib/rdoc/rdoc.rb b/lib/rdoc/rdoc.rb +index 68775c8..0095eb7 100644 +--- a/lib/rdoc/rdoc.rb ++++ b/lib/rdoc/rdoc.rb +@@ -433,7 +433,7 @@ The internal error was: + files.reject do |file| + file =~ /\.(?:class|eps|erb|scpt\.txt|svg|ttf|yml)$/i or + (file =~ /tags$/i and +- open(file, 'rb') { |io| ++ File.open(file, 'rb') { |io| + io.read(100) =~ /\A(\f\n[^,]+,\d+$|!_TAG_)/ + }) + end +diff --git a/test/rdoc/test_rdoc_rdoc.rb b/test/rdoc/test_rdoc_rdoc.rb +index bd47943..07541df 100644 +--- a/test/rdoc/test_rdoc_rdoc.rb ++++ b/test/rdoc/test_rdoc_rdoc.rb +@@ -366,6 +366,18 @@ class TestRDocRDoc < RDoc::TestCase + end + end + ++ def test_remove_unparseable_CVE_2021_31799 ++ temp_dir do ++ file_list = ['| touch evil.txt && echo tags'] ++ file_list.each do |f| ++ FileUtils.touch f ++ end ++ ++ assert_equal file_list, @rdoc.remove_unparseable(file_list) ++ assert_equal file_list, Dir.children('.') ++ end ++ end ++ + def test_setup_output_dir + Dir.mktmpdir {|d| + path = File.join d, 'testdir' +-- +1.8.3.1 + diff --git a/backport-CVE-2021-31810.patch b/backport-CVE-2021-31810.patch new file mode 100644 index 0000000..61b313f --- /dev/null +++ b/backport-CVE-2021-31810.patch @@ -0,0 +1,248 @@ +From 5709ece67cf57a94655e34532f8a7899b28d496a Mon Sep 17 00:00:00 2001l/pkcs5. +From: Shugo Maeda +Date: Wed, 7 Jul 2021 16:00:45 +0900/pkey.rb +Subject: [PATCH] This fixes CVE-2021-31810. Reported by Alexandr Savca inssl.rb + ./x509.rb + +Reference:https://github.com/ruby/net-ftp/commit/5709ece67cf57a94655e34532f8a7899b28d496a +--- + lib/net/ftp.rb | 15 ++++- + test/net/ftp/test_ftp.rb | 159 ++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 170 insertions(+), 4 deletions(-) + +diff --git a/lib/net/ftp.rb b/lib/net/ftp.rb +index 9902f9d..cd446b4 100644 +--- a/lib/net/ftp.rb ++++ b/lib/net/ftp.rb +@@ -97,6 +97,10 @@ module Net + # When +true+, the connection is in passive mode. Default: +true+. + attr_accessor :passive + ++ # When +true+, use the IP address in PASV responses. Otherwise, it uses ++ # the same IP address for the control connection. Default: +false+. ++ attr_accessor :use_pasv_ip ++ + # When +true+, all traffic to and from the server is written + # to +$stdout+. Default: +false+. + attr_accessor :debug_mode +@@ -205,6 +209,9 @@ module Net + # handshake. + # See Net::FTP#ssl_handshake_timeout for + # details. Default: +nil+. ++ # use_pasv_ip:: When +true+, use the IP address in PASV responses. ++ # Otherwise, it uses the same IP address for the control ++ # connection. Default: +false+. + # debug_mode:: When +true+, all traffic to and from the server is + # written to +$stdout+. Default: +false+. + # +@@ -265,6 +272,7 @@ module Net + @open_timeout = options[:open_timeout] + @ssl_handshake_timeout = options[:ssl_handshake_timeout] + @read_timeout = options[:read_timeout] || 60 ++ @use_pasv_ip = options[:use_pasv_ip] || false + if host + connect(host, options[:port] || FTP_PORT) + if options[:username] +@@ -1330,7 +1338,12 @@ module Net + raise FTPReplyError, resp + end + if m = /\((?\d+(,\d+){3}),(?\d+,\d+)\)/.match(resp) +- return parse_pasv_ipv4_host(m["host"]), parse_pasv_port(m["port"]) ++ if @use_pasv_ip ++ host = parse_pasv_ipv4_host(m["host"]) ++ else ++ host = @bare_sock.remote_address.ip_address ++ end ++ return host, parse_pasv_port(m["port"]) + else + raise FTPProtoError, resp + end +diff --git a/test/net/ftp/test_ftp.rb b/test/net/ftp/test_ftp.rb +index 8e0a688..52fb3a9 100644 +--- a/test/net/ftp/test_ftp.rb ++++ b/test/net/ftp/test_ftp.rb +@@ -61,7 +61,7 @@ class FTPTest < Test::Unit::TestCase + end + + def test_parse227 +- ftp = Net::FTP.new ++ ftp = Net::FTP.new(nil, use_pasv_ip: true) + host, port = ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1,12,34)") + assert_equal("192.168.0.1", host) + assert_equal(3106, port) +@@ -80,6 +80,14 @@ class FTPTest < Test::Unit::TestCase + assert_raise(Net::FTPProtoError) do + ftp.send(:parse227, "227 ) foo bar (") + end ++ ++ ftp = Net::FTP.new ++ sock = OpenStruct.new ++ sock.remote_address = OpenStruct.new ++ sock.remote_address.ip_address = "10.0.0.1" ++ ftp.instance_variable_set(:@bare_sock, sock) ++ host, port = ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1,12,34)") ++ assert_equal("10.0.0.1", host) + end + + def test_parse228 +@@ -2360,10 +2368,155 @@ EOF + end + end + ++ def test_ignore_pasv_ip ++ commands = [] ++ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3 ++ server = create_ftp_server(nil, "127.0.0.1") { |sock| ++ sock.print("220 (test_ftp).\r\n") ++ commands.push(sock.gets) ++ sock.print("331 Please specify the password.\r\n") ++ commands.push(sock.gets) ++ sock.print("230 Login successful.\r\n") ++ commands.push(sock.gets) ++ sock.print("200 Switching to Binary mode.\r\n") ++ line = sock.gets ++ commands.push(line) ++ data_server = TCPServer.new("127.0.0.1", 0) ++ port = data_server.local_address.ip_port ++ sock.printf("227 Entering Passive Mode (999,0,0,1,%s).\r\n", ++ port.divmod(256).join(",")) ++ commands.push(sock.gets) ++ sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n") ++ conn = data_server.accept ++ binary_data.scan(/.{1,1024}/nm) do |s| ++ conn.print(s) ++ end ++ conn.shutdown(Socket::SHUT_WR) ++ conn.read ++ conn.close ++ data_server.close ++ sock.print("226 Transfer complete.\r\n") ++ } ++ begin ++ begin ++ ftp = Net::FTP.new ++ ftp.passive = true ++ ftp.read_timeout *= 5 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait ++ ftp.connect("127.0.0.1", server.port) ++ ftp.login ++ assert_match(/\AUSER /, commands.shift) ++ assert_match(/\APASS /, commands.shift) ++ assert_equal("TYPE I\r\n", commands.shift) ++ buf = ftp.getbinaryfile("foo", nil) ++ assert_equal(binary_data, buf) ++ assert_equal(Encoding::ASCII_8BIT, buf.encoding) ++ assert_equal("PASV\r\n", commands.shift) ++ assert_equal("RETR foo\r\n", commands.shift) ++ assert_equal(nil, commands.shift) ++ ensure ++ ftp.close if ftp ++ end ++ ensure ++ server.close ++ end ++ end ++ ++ def test_use_pasv_ip ++ commands = [] ++ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3 ++ server = create_ftp_server(nil, "127.0.0.1") { |sock| ++ sock.print("220 (test_ftp).\r\n") ++ commands.push(sock.gets) ++ sock.print("331 Please specify the password.\r\n") ++ commands.push(sock.gets) ++ sock.print("230 Login successful.\r\n") ++ commands.push(sock.gets) ++ sock.print("200 Switching to Binary mode.\r\n") ++ line = sock.gets ++ commands.push(line) ++ data_server = TCPServer.new("127.0.0.1", 0) ++ port = data_server.local_address.ip_port ++ sock.printf("227 Entering Passive Mode (127,0,0,1,%s).\r\n", ++ port.divmod(256).join(",")) ++ commands.push(sock.gets) ++ sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n") ++ conn = data_server.accept ++ binary_data.scan(/.{1,1024}/nm) do |s| ++ conn.print(s) ++ end ++ conn.shutdown(Socket::SHUT_WR) ++ conn.read ++ conn.close ++ data_server.close ++ sock.print("226 Transfer complete.\r\n") ++ } ++ begin ++ begin ++ ftp = Net::FTP.new ++ ftp.passive = true ++ ftp.use_pasv_ip = true ++ ftp.read_timeout *= 5 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait ++ ftp.connect("127.0.0.1", server.port) ++ ftp.login ++ assert_match(/\AUSER /, commands.shift) ++ assert_match(/\APASS /, commands.shift) ++ assert_equal("TYPE I\r\n", commands.shift) ++ buf = ftp.getbinaryfile("foo", nil) ++ assert_equal(binary_data, buf) ++ assert_equal(Encoding::ASCII_8BIT, buf.encoding) ++ assert_equal("PASV\r\n", commands.shift) ++ assert_equal("RETR foo\r\n", commands.shift) ++ assert_equal(nil, commands.shift) ++ ensure ++ ftp.close if ftp ++ end ++ ensure ++ server.close ++ end ++ end ++ ++ def test_use_pasv_invalid_ip ++ commands = [] ++ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3 ++ server = create_ftp_server(nil, "127.0.0.1") { |sock| ++ sock.print("220 (test_ftp).\r\n") ++ commands.push(sock.gets) ++ sock.print("331 Please specify the password.\r\n") ++ commands.push(sock.gets) ++ sock.print("230 Login successful.\r\n") ++ commands.push(sock.gets) ++ sock.print("200 Switching to Binary mode.\r\n") ++ line = sock.gets ++ commands.push(line) ++ sock.print("227 Entering Passive Mode (999,0,0,1,48,57).\r\n") ++ commands.push(sock.gets) ++ } ++ begin ++ begin ++ ftp = Net::FTP.new ++ ftp.passive = true ++ ftp.use_pasv_ip = true ++ ftp.read_timeout *= 5 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait ++ ftp.connect("127.0.0.1", server.port) ++ ftp.login ++ assert_match(/\AUSER /, commands.shift) ++ assert_match(/\APASS /, commands.shift) ++ assert_equal("TYPE I\r\n", commands.shift) ++ assert_raise(SocketError) do ++ ftp.getbinaryfile("foo", nil) ++ end ++ ensure ++ ftp.close if ftp ++ end ++ ensure ++ server.close ++ end ++ end ++ + private + +- def create_ftp_server(sleep_time = nil) +- server = TCPServer.new(SERVER_ADDR, 0) ++ def create_ftp_server(sleep_time = nil, addr = SERVER_ADDR) ++ server = TCPServer.new(addr, 0) + @thread = Thread.start do + if sleep_time + sleep(sleep_time) +-- +1.8.3.1 + diff --git a/backport-CVE-2021-32066.patch b/backport-CVE-2021-32066.patch new file mode 100644 index 0000000..a893695 --- /dev/null +++ b/backport-CVE-2021-32066.patch @@ -0,0 +1,95 @@ +From adba6f0c3e5c5607c4822b9120322eb7e9a77891 Mon Sep 17 00:00:00 2001 +From: Shugo Maeda +Date: Wed, 7 Jul 2021 16:09:03 +0900 +Subject: [PATCH] This fixes CVE-2021-32066. Reported by Alexandr Savca in + . + +Reference:https://github.com/ruby/net-imap/commit/adba6f0c3e5c5607c4822b9120322eb7e9a77891 +--- + lib/net/imap.rb | 8 +++++++- + test/net/imap/test_imap.rb | 31 +++++++++++++++++++++++++++++++ + 2 files changed, 38 insertions(+), 1 deletion(-) + +diff --git a/lib/net/imap.rb b/lib/net/imap.rb +index da7d0d5..eedcb4f 100644 +--- a/lib/net/imap.rb ++++ b/lib/net/imap.rb +@@ -1213,12 +1213,14 @@ module Net + end + resp = @tagged_responses.delete(tag) + case resp.name ++ when /\A(?:OK)\z/ni ++ return resp + when /\A(?:NO)\z/ni + raise NoResponseError, resp + when /\A(?:BAD)\z/ni + raise BadResponseError, resp + else +- return resp ++ raise UnknownResponseError, resp + end + end + +@@ -3714,6 +3716,10 @@ module Net + class ByeResponseError < ResponseError + end + ++ # Error raised upon an unknown response from the server. ++ class UnknownResponseError < ResponseError ++ end ++ + RESPONSE_ERRORS = Hash.new(ResponseError) + RESPONSE_ERRORS["NO"] = NoResponseError + RESPONSE_ERRORS["BAD"] = BadResponseError +diff --git a/test/net/imap/test_imap.rb b/test/net/imap/test_imap.rb +index 41f25fe..9247062 100644 +--- a/test/net/imap/test_imap.rb ++++ b/test/net/imap/test_imap.rb +@@ -110,6 +110,16 @@ class IMAPTest < Test::Unit::TestCase + end + end + end ++ ++ def test_starttls_stripping ++ starttls_stripping_test do |port| ++ imap = Net::IMAP.new("localhost", :port => port) ++ assert_raise(Net::IMAP::UnknownResponseError) do ++ imap.starttls(:ca_file => CA_FILE) ++ end ++ imap ++ end ++ end + end + + if defined?(OpenSSL::SSL) +@@ -760,6 +770,27 @@ EOF + end + end + ++ def starttls_stripping_test ++ server = create_tcp_server ++ port = server.addr[1] ++ start_server do ++ sock = server.accept ++ begin ++ sock.print("* OK test server\r\n") ++ sock.gets ++ sock.print("RUBY0001 BUG unhandled command\r\n") ++ ensure ++ sock.close ++ server.close ++ end ++ end ++ begin ++ imap = yield(port) ++ ensure ++ imap.disconnect if imap && !imap.disconnected? ++ end ++ end ++ + def create_tcp_server + return TCPServer.new(server_addr, 0) + end +-- +1.8.3.1 + diff --git a/ruby.spec b/ruby.spec index cd45a2d..731c38c 100644 --- a/ruby.spec +++ b/ruby.spec @@ -1,6 +1,6 @@ Name: ruby Version: 2.5.8 -Release: 113 +Release: 114 Summary: Object-oriented scripting language interpreter License: (Ruby or BSD) and Public Domain and MIT and CC0 and zlib and UCD URL: https://www.ruby-lang.org/en/ @@ -40,6 +40,9 @@ Patch6002: CVE-2019-16163.patch Patch6003: CVE-2020-25613.patch Patch6004: backport-0001-CVE-2021-28965.patch Patch6005: backport-0002-CVE-2021-28965.patch +Patch6006: backport-CVE-2021-31799.patch +Patch6007: backport-CVE-2021-31810.patch +Patch6008: backport-CVE-2021-32066.patch Provides: %{name}-libs = %{version}-%{release} Obsoletes: %{name}-libs < %{version}-%{release} @@ -577,6 +580,9 @@ make runruby TESTRUN_SCRIPT=%{SOURCE13} %exclude %{gem_dir}/gems/xmlrpc-0.3.0/.* %changelog +* Sat Jul 31 2021 shixuantong - 2.5.8-114 +- fix CVE-2021-31799 CVE-2021-31810 CVE-2021-32066 + * Sat Jul 31 2021 shixuantong - 2.5.8-113 - fix CVE-2021-28965 -- Gitee