CVE-2013-0183 (published 2013-03-01T05:40Z)

multipart/parser.rb in Rack 1.3.x before 1.3.8 and 1.4.x before 1.4.3 allows remote attackers to cause a denial of service (memory consumption and out-of-memory error) via a long string in a Multipart HTTP packet.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:N/I:N/A:P)

Weakness information:
cwe_id: CWE-119
cwe_name: Improper Restriction of Operations within the Bounds of a Memory Buffer
description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
extended_description: 
            Certain languages allow direct addressing of memory locations and do not automatically ensure that these locations are valid for the memory buffer that is being referenced. This can cause read or write operations to be performed on memory locations that may be associated with other variables, data structures, or internal program data.
         
         

Fix information:
repo_url: https://github.com/rack/rack
commit_details: repo_url: https://github.com/rack/rack
author: Eric Wong
author_date: 2012-08-22 22:48:23+00:00
msg: multipart/parser: avoid unbounded #gets method

Malicious clients may send excessively long lines
to trigger out-of-memory errors in a Rack web server.
repository: repo_url: https://github.com/rack/rack
repo_name: rack/rack
description: A modular Ruby web server interface.
repo_language: Ruby
owner: rack
file_changes: filename: parser.rb
change_type: ModificationType.MODIFY
diff: @@ -70,9 +70,16 @@ module Rack
 
       def fast_forward_to_first_boundary
         loop do
-          read_buffer = @io.gets
-          break if read_buffer == full_boundary
-          raise EOFError, "bad content body" if read_buffer.nil?
+          content = @io.read(BUFSIZE)
+          raise EOFError, "bad content body" unless content
+          @buf << content
+
+          while @buf.gsub!(/\A([^\n]*\n)/, '')
+            read_buffer = $1
+            return if read_buffer == full_boundary
+          end
+
+          raise EOFError, "bad content body" if Utils.bytesize(@buf) >= BUFSIZE
         end
       end
 

programming_language: Ruby
filename: spec_multipart.rb
change_type: ModificationType.MODIFY
diff: @@ -48,6 +48,59 @@ describe Rack::Multipart do
     params['profile']['bio'].should.include 'hello'
   end
 
+  should "reject insanely long boundaries" do
+    # using a pipe since a tempfile can use up too much space
+    rd, wr = IO.pipe
+
+    # we only call rewind once at start, so make sure it succeeds
+    # and doesn't hit ESPIPE
+    def rd.rewind; end
+    wr.sync = true
+
+    # mock out length to make this pipe look like a Tempfile
+    def rd.length
+      1024 * 1024 * 8
+    end
+
+    # write to a pipe in a background thread, this will write a lot
+    # unless Rack (properly) shuts down the read end
+    thr = Thread.new do
+      begin
+        wr.write("--AaB03x")
+
+        # make the initial boundary a few gigs long
+        longer = "0123456789" * 1024 * 1024
+        (1024 * 1024).times { wr.write(longer) }
+
+        wr.write("\r\n")
+        wr.write('Content-Disposition: form-data; name="a"; filename="a.txt"')
+        wr.write("\r\n")
+        wr.write("Content-Type: text/plain\r\n")
+        wr.write("\r\na")
+        wr.write("--AaB03x--\r\n")
+        wr.close
+      rescue => err # this is EPIPE if Rack shuts us down
+        err
+      end
+    end
+
+    fixture = {
+      "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
+      "CONTENT_LENGTH" => rd.length.to_s,
+      :input => rd,
+    }
+
+    env = Rack::MockRequest.env_for '/', fixture
+    lambda {
+      Rack::Multipart.parse_multipart(env)
+    }.should.raise(EOFError)
+    rd.close
+
+    err = thr.value
+    err.should.be.instance_of Errno::EPIPE
+    wr.close
+  end
+
   should "parse multipart upload with text file" do
     env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
     params = Rack::Multipart.parse_multipart(env)

programming_language: Ruby