From 6611a70ec0199507c5f58c871faa63812b93f671 Mon Sep 17 00:00:00 2001 From: bizhiyuan Date: Thu, 8 Jun 2023 19:03:54 +0800 Subject: [PATCH 1/3] Fix_CVE-2023-27530 --- 2-2-multipart-dos.patch | 199 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 2-2-multipart-dos.patch diff --git a/2-2-multipart-dos.patch b/2-2-multipart-dos.patch new file mode 100644 index 0000000..be0fe88 --- /dev/null +++ b/2-2-multipart-dos.patch @@ -0,0 +1,199 @@ +From 9aac3757fe19cdb0476504c9245170115bec9668 Mon Sep 17 00:00:00 2001 +From: John Hawthorn +Date: Thu, 8 Dec 2022 15:54:28 -0800 +Subject: [PATCH] Limit all multipart parts, not just files + +Previously we would limit the number of multipart parts which were +files, but not other parts. In some cases this could cause parsing of +maliciously crafted inputs to take longer than expected. + +[CVE-2023-27530] +--- + README.rdoc | 20 +++++++++++++++++--- + lib/rack/multipart/parser.rb | 19 +++++++++++++++---- + lib/rack/utils.rb | 19 +++++++++++++++---- + test/spec_multipart.rb | 12 ++++++++++++ + test/spec_request.rb | 18 +++++++++++++++++- + 5 files changed, 76 insertions(+), 12 deletions(-) + +diff --git a/README.rdoc b/README.rdoc +index 8533846f..cbb25723 100644 +--- a/README.rdoc ++++ b/README.rdoc +@@ -202,16 +202,30 @@ Limiting the depth prevents a possible stack overflow when parsing parameters. + + Defaults to 100. + +-=== multipart_part_limit ++=== multipart_file_limit + +-The maximum number of parts a request can contain. ++The maximum number of parts with a filename a request can contain. + Accepting too many part can lead to the server running out of file handles. + + The default is 128, which means that a single request can't upload more than 128 files at once. + + Set to 0 for no limit. + +-Can also be set via the +RACK_MULTIPART_PART_LIMIT+ environment variable. ++Can also be set via the +RACK_MULTIPART_FILE_LIMIT+ environment variable. ++ ++(This is also aliased as +multipart_part_limit+ and +RACK_MULTIPART_PART_LIMIT+ for compatibility) ++ ++=== multipart_total_part_limit ++ ++The maximum total number of parts a request can contain of any type, including ++both file and non-file form fields. ++ ++The default is 4096, which means that a single request can't contain more than ++4096 parts. ++ ++Set to 0 for no limit. ++ ++Can also be set via the +RACK_MULTIPART_TOTAL_PART_LIMIT+ environment variable. + + == Changelog + +diff --git a/lib/rack/multipart/parser.rb b/lib/rack/multipart/parser.rb +index e8ed3e97..0fc18560 100644 +--- a/lib/rack/multipart/parser.rb ++++ b/lib/rack/multipart/parser.rb +@@ -5,6 +5,7 @@ require 'strscan' + module Rack + module Multipart + class MultipartPartLimitError < Errno::EMFILE; end ++ class MultipartTotalPartLimitError < StandardError; end + + class Parser + (require_relative '../core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4' +@@ -140,7 +141,7 @@ module Rack + + @mime_parts[mime_index] = klass.new(body, head, filename, content_type, name) + +- check_open_files ++ check_part_limits + end + + def on_mime_body(mime_index, content) +@@ -152,13 +153,23 @@ module Rack + + private + +- def check_open_files +- if Utils.multipart_part_limit > 0 +- if @open_files >= Utils.multipart_part_limit ++ def check_part_limits ++ file_limit = Utils.multipart_file_limit ++ part_limit = Utils.multipart_total_part_limit ++ ++ if file_limit && file_limit > 0 ++ if @open_files >= file_limit + @mime_parts.each(&:close) + raise MultipartPartLimitError, 'Maximum file multiparts in content reached' + end + end ++ ++ if part_limit && part_limit > 0 ++ if @mime_parts.size >= part_limit ++ @mime_parts.each(&:close) ++ raise MultipartTotalPartLimitError, 'Maximum total multiparts in content reached' ++ end ++ end + end + end + +diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb +index 14d9e17d..c8e61ea1 100644 +--- a/lib/rack/utils.rb ++++ b/lib/rack/utils.rb +@@ -58,13 +58,24 @@ module Rack + end + + class << self +- attr_accessor :multipart_part_limit ++ attr_accessor :multipart_total_part_limit ++ ++ attr_accessor :multipart_file_limit ++ ++ # multipart_part_limit is the original name of multipart_file_limit, but ++ # the limit only counts parts with filenames. ++ alias multipart_part_limit multipart_file_limit ++ alias multipart_part_limit= multipart_file_limit= + end + +- # The maximum number of parts a request can contain. Accepting too many part +- # can lead to the server running out of file handles. ++ # The maximum number of file parts a request can contain. Accepting too ++ # many parts can lead to the server running out of file handles. + # Set to `0` for no limit. +- self.multipart_part_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || 128).to_i ++ self.multipart_file_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || ENV['RACK_MULTIPART_FILE_LIMIT'] || 128).to_i ++ ++ # The maximum total number of parts a request can contain. Accepting too ++ # many can lead to excessive memory use and parsing time. ++ self.multipart_total_part_limit = (ENV['RACK_MULTIPART_TOTAL_PART_LIMIT'] || 4096).to_i + + def self.param_depth_limit + default_query_parser.param_depth_limit +diff --git a/test/spec_multipart.rb b/test/spec_multipart.rb +index f5115045..d5bf30d3 100644 +--- a/test/spec_multipart.rb ++++ b/test/spec_multipart.rb +@@ -632,6 +632,18 @@ Content-Type: image/jpeg\r + end + end + ++ it "reach a multipart total limit" do ++ begin ++ previous_limit = Rack::Utils.multipart_total_part_limit ++ Rack::Utils.multipart_total_part_limit = 5 ++ ++ env = Rack::MockRequest.env_for '/', multipart_fixture(:three_files_three_fields) ++ lambda { Rack::Multipart.parse_multipart(env) }.must_raise Rack::Multipart::MultipartTotalPartLimitError ++ ensure ++ Rack::Utils.multipart_total_part_limit = previous_limit ++ end ++ end ++ + it "return nil if no UploadedFiles were used" do + data = Rack::Multipart.build_multipart("people" => [{ "submit-name" => "Larry", "files" => "contents" }]) + data.must_be_nil +diff --git a/test/spec_request.rb b/test/spec_request.rb +index c8c439b5..51cfcdc8 100644 +--- a/test/spec_request.rb ++++ b/test/spec_request.rb +@@ -1000,7 +1000,7 @@ EOF + f[:tempfile].size.must_equal 76 + end + +- it "MultipartPartLimitError when request has too many multipart parts if limit set" do ++ it "MultipartPartLimitError when request has too many multipart file parts if limit set" do + begin + data = 10000.times.map { "--AaB03x\r\nContent-Type: text/plain\r\nContent-Disposition: attachment; name=#{SecureRandom.hex(10)}; filename=#{SecureRandom.hex(10)}\r\n\r\ncontents\r\n" }.join("\r\n") + data += "--AaB03x--\r" +@@ -1016,6 +1016,22 @@ EOF + end + end + ++ it "MultipartPartLimitError when request has too many multipart total parts if limit set" do ++ begin ++ data = 10000.times.map { "--AaB03x\r\ncontent-type: text/plain\r\ncontent-disposition: attachment; name=#{SecureRandom.hex(10)}\r\n\r\ncontents\r\n" }.join("\r\n") ++ data += "--AaB03x--\r" ++ ++ options = { ++ "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x", ++ "CONTENT_LENGTH" => data.length.to_s, ++ :input => StringIO.new(data) ++ } ++ ++ request = make_request Rack::MockRequest.env_for("/", options) ++ lambda { request.POST }.must_raise Rack::Multipart::MultipartTotalPartLimitError ++ end ++ end ++ + it 'closes tempfiles it created in the case of too many created' do + begin + data = 10000.times.map { "--AaB03x\r\nContent-Type: text/plain\r\nContent-Disposition: attachment; name=#{SecureRandom.hex(10)}; filename=#{SecureRandom.hex(10)}\r\n\r\ncontents\r\n" }.join("\r\n") +-- +2.37.1 + -- Gitee From 03574e8e8e6a00ba7f9169f6eb1c25d81597820a Mon Sep 17 00:00:00 2001 From: bizhiyuan Date: Thu, 8 Jun 2023 23:24:08 +0800 Subject: [PATCH 2/3] Fix CVE-2023-27530 --- rubygem-rack.spec | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rubygem-rack.spec b/rubygem-rack.spec index e853db2..1f449f5 100644 --- a/rubygem-rack.spec +++ b/rubygem-rack.spec @@ -9,6 +9,7 @@ Summary: A modular Ruby webserver interface License: MIT and BSD URL: https://rack.github.io/ Source0: https://rubygems.org/downloads/%{gem_name}-%{version}.gem +Patch0: 2-2-multipart-dos.patch BuildRequires: ruby(release) rubygems-devel ruby >= 2.2.2 BuildRequires: memcached rubygem(memcache-client) rubygem(minitest) BuildRequires: rubygem(memcache-client) @@ -97,6 +98,13 @@ popd %doc %{gem_instdir}/contrib %changelog +* Tue Jun 13 2023 bizhiyuan 1:2.2.3.1-2 +- Add 2-2-multipart-dos.patch +- Type:bugfix +- ID:NA +- SUG:NA +- DESC: Fix CVE-2023-27530 + * Tue Jun 28 2022 wangkai - 1:2.2.3.1-1 - Upgrade to 2.2.3.1 for fix CVE-2020-8184 CVE-2022-30122 CVE-2022-30123 -- Gitee From 598c07622c65cac1602ea8c52c1b271f4b378861 Mon Sep 17 00:00:00 2001 From: bizhiyuan Date: Thu, 8 Jun 2023 23:29:53 +0800 Subject: [PATCH 3/3] Fix CVE-2023-27530 --- rubygem-rack.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rubygem-rack.spec b/rubygem-rack.spec index 1f449f5..2dd2805 100644 --- a/rubygem-rack.spec +++ b/rubygem-rack.spec @@ -100,8 +100,8 @@ popd %changelog * Tue Jun 13 2023 bizhiyuan 1:2.2.3.1-2 - Add 2-2-multipart-dos.patch -- Type:bugfix -- ID:NA +- Type:CVES +- ID:CVE-2023-27530 - SUG:NA - DESC: Fix CVE-2023-27530 -- Gitee