cgi_multipart_eof_fix 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.txt +43 -0
- data/Rakefile +53 -0
- data/cgi_multipart_eof_fix_test.rb +31 -0
- data/lib/cgi_multipart_eof_fix.rb +112 -0
- metadata +49 -0
data/README.txt
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
DESCRIPTION
|
3
|
+
|
4
|
+
Fix an exploitable bug in CGI multipart parsing which affects Ruby <= 1.8.5
|
5
|
+
when multipart boundary attribute contains a non-halting regular expression
|
6
|
+
string. The boundary searcher in the CGI module does not properly escape
|
7
|
+
the user-supplied parameter and will execute arbitrary regular expressions.
|
8
|
+
The fix adds escaping for the user data.
|
9
|
+
|
10
|
+
This is fix is cumulative with previous CGI multipart vulnerability fixes; see
|
11
|
+
version 1.0.0 of the gem by Zed Shaw.
|
12
|
+
|
13
|
+
SCOPE
|
14
|
+
|
15
|
+
Affected: standalone CGI, Mongrel, WEBrick
|
16
|
+
Unaffected: FastCGI
|
17
|
+
Unknown: mod_ruby
|
18
|
+
|
19
|
+
USAGE
|
20
|
+
|
21
|
+
Install the hotfix gem and run the included test to verify the flaw is
|
22
|
+
corrected. You must require the gem in every affected application, as follows:
|
23
|
+
|
24
|
+
require 'rubygems'
|
25
|
+
require 'cgi_multipart_eof_fix'
|
26
|
+
|
27
|
+
If you only use mongrel_rails for application hosting, you may install mongrel
|
28
|
+
like so:
|
29
|
+
|
30
|
+
sudo gem install mongrel --source=http://mongrel.rubyforge.org/releases
|
31
|
+
|
32
|
+
Then mongrel will require the fix for you, provided you have installed version 2.0.0
|
33
|
+
of this gem. This is a hack, and mongrel may change in the future.
|
34
|
+
|
35
|
+
RESOURCES
|
36
|
+
|
37
|
+
http://www.ruby-lang.org/en/news/2006/12/04/another-dos-vulnerability-in-cgi-library/
|
38
|
+
http://blog.evanweaver.com/articles/2006/12/05/cgi-rb-vulnerability-hotfix
|
39
|
+
|
40
|
+
LICENSE
|
41
|
+
|
42
|
+
Licensed under the same license as Ruby itself. Software contains the work of others.
|
43
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
NAME = "cgi_multipart_eof_fix"
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'rake/clean'
|
8
|
+
require 'echoe'
|
9
|
+
require 'fileutils'
|
10
|
+
|
11
|
+
AUTHOR = "Evan Weaver"
|
12
|
+
EMAIL = "evan at cloudbur dot st"
|
13
|
+
DESCRIPTION = "Fix an exploitable bug in CGI multipart parsing which affects Ruby <= 1.8.5 when multipart boundary attribute contains a non-halting regular expression string."
|
14
|
+
RUBYFORGE_NAME = "fauna"
|
15
|
+
GEM_NAME = "cgi_multipart_eof_fix"
|
16
|
+
HOMEPATH = "http://blog.evanweaver.com"
|
17
|
+
RELEASE_TYPES = ["gem"]
|
18
|
+
REV = nil
|
19
|
+
VERS = "2.0.1"
|
20
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
21
|
+
RDOC_OPTS = ['--quiet', '--title', "cgi_multipart_eof_fix documentation",
|
22
|
+
"--opname", "index.html",
|
23
|
+
"--line-numbers",
|
24
|
+
"--main", "README",
|
25
|
+
"--inline-source"]
|
26
|
+
|
27
|
+
include FileUtils
|
28
|
+
require File.join(File.dirname(__FILE__), 'lib', 'cgi_multipart_eof_fix')
|
29
|
+
|
30
|
+
echoe = Echoe.new(GEM_NAME, VERS) do |p|
|
31
|
+
p.author = AUTHOR
|
32
|
+
p.rubyforge_name = RUBYFORGE_NAME
|
33
|
+
p.name = NAME
|
34
|
+
p.description = DESCRIPTION
|
35
|
+
p.email = EMAIL
|
36
|
+
p.summary = DESCRIPTION
|
37
|
+
p.url = HOMEPATH
|
38
|
+
p.test_globs = ["*_test.rb"]
|
39
|
+
p.clean_globs = CLEAN
|
40
|
+
end
|
41
|
+
|
42
|
+
rescue LoadError => boom
|
43
|
+
puts "You are missing a dependency required for meta-operations on this gem."
|
44
|
+
puts "#{boom.to_s.capitalize}."
|
45
|
+
|
46
|
+
desc 'Run the default tasks'
|
47
|
+
task :default => :test
|
48
|
+
|
49
|
+
desc 'Run the test suite.'
|
50
|
+
task :test do
|
51
|
+
system "ruby -Ibin:lib:test #{NAME}_test.rb"
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'cgi'
|
3
|
+
require 'stringio'
|
4
|
+
require 'timeout'
|
5
|
+
|
6
|
+
def test_read_multipart_eof_fix
|
7
|
+
boundary = '%?%(\w*)\\((\w*)\\)'
|
8
|
+
data = "--#{boundary}\r\nContent-Disposition: form-data; name=\"a_field\"\r\n\r\nBang!\r\n--#{boundary}--\r\n"
|
9
|
+
|
10
|
+
ENV['REQUEST_METHOD'] = "POST"
|
11
|
+
ENV['CONTENT_TYPE'] = "multipart/form-data; boundary=\"#{boundary}\""
|
12
|
+
ENV['CONTENT_LENGTH'] = data.length.to_s
|
13
|
+
|
14
|
+
$stdin = StringIO.new(data)
|
15
|
+
|
16
|
+
begin
|
17
|
+
Timeout.timeout(3) { CGI.new }
|
18
|
+
$stderr.puts ' => CGI is safe: read_multipart does not hang on malicious multipart requests.'
|
19
|
+
rescue TimeoutError
|
20
|
+
$stderr.puts ' => CGI is exploitable: read_multipart hangs on malicious multipart requests.'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
$stderr.puts 'Testing malicious multipart boundary request injection'
|
25
|
+
test_read_multipart_eof_fix
|
26
|
+
|
27
|
+
$stderr.puts 'Patching CGI::QueryExtension.read_multipart'
|
28
|
+
require 'rubygems'
|
29
|
+
require 'cgi_multipart_eof_fix'
|
30
|
+
|
31
|
+
test_read_multipart_eof_fix
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
class CGI
|
4
|
+
module QueryExtension
|
5
|
+
def read_multipart(boundary, content_length)
|
6
|
+
params = Hash.new([])
|
7
|
+
boundary = "--" + boundary
|
8
|
+
quoted_boundary = Regexp.quote(boundary, "n")
|
9
|
+
buf = ""
|
10
|
+
bufsize = 10 * 1024
|
11
|
+
boundary_end=""
|
12
|
+
|
13
|
+
# start multipart/form-data
|
14
|
+
stdinput.binmode if defined? stdinput.binmode
|
15
|
+
boundary_size = boundary.size + EOL.size
|
16
|
+
content_length -= boundary_size
|
17
|
+
status = stdinput.read(boundary_size)
|
18
|
+
if nil == status
|
19
|
+
raise EOFError, "no content body"
|
20
|
+
elsif boundary + EOL != status
|
21
|
+
raise EOFError, "bad content body #{status.inspect} expected, got #{(boundary + EOL).inspect}"
|
22
|
+
end
|
23
|
+
|
24
|
+
loop do
|
25
|
+
head = nil
|
26
|
+
if 10240 < content_length
|
27
|
+
require "tempfile"
|
28
|
+
body = Tempfile.new("CGI")
|
29
|
+
else
|
30
|
+
begin
|
31
|
+
require "stringio"
|
32
|
+
body = StringIO.new
|
33
|
+
rescue LoadError
|
34
|
+
require "tempfile"
|
35
|
+
body = Tempfile.new("CGI")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
body.binmode if defined? body.binmode
|
39
|
+
|
40
|
+
until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf)
|
41
|
+
|
42
|
+
if (not head) and /#{EOL}#{EOL}/n.match(buf)
|
43
|
+
buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
|
44
|
+
head = $1.dup
|
45
|
+
""
|
46
|
+
end
|
47
|
+
next
|
48
|
+
end
|
49
|
+
|
50
|
+
if head and ( (EOL + boundary + EOL).size < buf.size )
|
51
|
+
body.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)]
|
52
|
+
buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
|
53
|
+
end
|
54
|
+
|
55
|
+
c = if bufsize < content_length
|
56
|
+
stdinput.read(bufsize)
|
57
|
+
else
|
58
|
+
stdinput.read(content_length)
|
59
|
+
end
|
60
|
+
if c.nil? || c.empty?
|
61
|
+
raise EOFError, "bad content body"
|
62
|
+
end
|
63
|
+
buf.concat(c)
|
64
|
+
content_length -= c.size
|
65
|
+
end
|
66
|
+
|
67
|
+
buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
|
68
|
+
body.print $1
|
69
|
+
if "--" == $2
|
70
|
+
content_length = -1
|
71
|
+
end
|
72
|
+
boundary_end = $2.dup
|
73
|
+
""
|
74
|
+
end
|
75
|
+
|
76
|
+
body.rewind
|
77
|
+
|
78
|
+
/Content-Disposition:.* filename="?([^\";]*)"?/ni.match(head)
|
79
|
+
filename = ($1 or "")
|
80
|
+
if /Mac/ni.match(env_table['HTTP_USER_AGENT']) and
|
81
|
+
/Mozilla/ni.match(env_table['HTTP_USER_AGENT']) and
|
82
|
+
(not /MSIE/ni.match(env_table['HTTP_USER_AGENT']))
|
83
|
+
filename = CGI::unescape(filename)
|
84
|
+
end
|
85
|
+
|
86
|
+
/Content-Type: (.*)/ni.match(head)
|
87
|
+
content_type = ($1 or "")
|
88
|
+
|
89
|
+
(class << body; self; end).class_eval do
|
90
|
+
alias local_path path
|
91
|
+
define_method(:original_filename) {filename.dup.taint}
|
92
|
+
define_method(:content_type) {content_type.dup.taint}
|
93
|
+
end
|
94
|
+
|
95
|
+
/Content-Disposition:.* name="?([^\";]*)"?/ni.match(head)
|
96
|
+
name = $1.dup
|
97
|
+
|
98
|
+
if params.has_key?(name)
|
99
|
+
params[name].push(body)
|
100
|
+
else
|
101
|
+
params[name] = [body]
|
102
|
+
end
|
103
|
+
break if buf.size == 0
|
104
|
+
break if content_length === -1
|
105
|
+
end
|
106
|
+
raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
|
107
|
+
|
108
|
+
params
|
109
|
+
end # read_multipart
|
110
|
+
private :read_multipart
|
111
|
+
end
|
112
|
+
end
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.0.9
|
3
|
+
specification_version: 1
|
4
|
+
name: cgi_multipart_eof_fix
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 2.0.1
|
7
|
+
date: 2007-01-10 00:00:00 -05:00
|
8
|
+
summary: Fix an exploitable bug in CGI multipart parsing which affects Ruby <= 1.8.5 when multipart boundary attribute contains a non-halting regular expression string.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: evan at cloudbur dot st
|
12
|
+
homepage: http://blog.evanweaver.com
|
13
|
+
rubyforge_project: fauna
|
14
|
+
description: Fix an exploitable bug in CGI multipart parsing which affects Ruby <= 1.8.5 when multipart boundary attribute contains a non-halting regular expression string.
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Evan Weaver
|
31
|
+
files:
|
32
|
+
- README.txt
|
33
|
+
- Rakefile
|
34
|
+
- lib/cgi_multipart_eof_fix.rb
|
35
|
+
- cgi_multipart_eof_fix_test.rb
|
36
|
+
test_files:
|
37
|
+
- cgi_multipart_eof_fix_test.rb
|
38
|
+
rdoc_options: []
|
39
|
+
|
40
|
+
extra_rdoc_files: []
|
41
|
+
|
42
|
+
executables: []
|
43
|
+
|
44
|
+
extensions: []
|
45
|
+
|
46
|
+
requirements: []
|
47
|
+
|
48
|
+
dependencies: []
|
49
|
+
|