From 50b2ae477fa6d19db6cbb30631ce0f07aec365f5 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Thu, 12 Mar 2015 21:07:46 -0400 Subject: [PATCH 01/11] Add a plugin for making curl-like http requests --- plugins/requests.rb | 202 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 plugins/requests.rb diff --git a/plugins/requests.rb b/plugins/requests.rb new file mode 100644 index 0000000000..3ed8e9045f --- /dev/null +++ b/plugins/requests.rb @@ -0,0 +1,202 @@ +require 'uri' + +module Msf + +class Plugin::HTTPRequest < Msf::Plugin + + class ConsoleCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + def name + 'Request' + end + + def commands + { + 'http_request' => 'Make an HTTP request' + } + end + + def parse_args(args) + help_line = 'Usage: http_request [options] uri' + opt_parser = Rex::Parser::Arguments.new( + '-0' => [ false, 'Use HTTP 1.0' ], + '-1' => [ false, 'Use TLSv1 (SSL)' ], + '-2' => [ false, 'Use SSLv2 (SSL)' ], + '-3' => [ false, 'Use SSLv3 (SSL)' ], + '-A' => [ true, 'User-Agent to send to server' ], + '-d' => [ true, 'HTTP POST data' ], + '-G' => [ false, 'Send the -d data with a HTTP GET' ], + '-h' => [ false, 'This help text' ], + '-H' => [ true, 'Custom header to pass to server' ], + '-i' => [ false, 'Include headers in the output' ], + '-I' => [ false, 'Show document info only' ], + '-o' => [ true, 'Write output to instead of stdout' ], + '-u' => [ true, 'Server user and password', ], + '-X' => [ true, 'Request method to use' ] + ) + + options = { + :auth_password => nil, + :auth_username => nil, + :headers => { }, + :print_body => true, + :print_headers => false, + :method => nil, + :output_file => nil, + :ssl_version => 'Auto', + :uri => nil, + :user_agent => Rex::Proto::Http::Client::DefaultUserAgent, + :version => '1.1' + } + + opt_parser.parse(args) do |opt, idx, val| + case opt + when '-0' + options[:version] = '1.0' + when '-1' + options[:ssl_version] = 'TLS1' + when '-2' + options[:ssl_version] = 'SSL2' + when '-3' + options[:ssl_version] = 'SSL3' + when '-A' + options[:user_agent] = val + when '-d' + options[:data] = val + options[:method] ||= 'POST' + when '-G' + options[:method] = 'GET' + when '-h' + print_line(help_line) + print_line(opt_parser.usage) + return + when '-H' + name, _, value = val.partition(':') + options[:headers][name] = value.strip + when '-i' + options[:print_headers] = true + when '-I' + options[:print_headers] = true + options[:print_body] = false + options[:method] ||= 'HEAD' + when '-o' + options[:output_file] = val + when '-u' + val = val.partition(':') + options[:auth_username] = val[0] + options[:auth_password] = val[2] + when '-X' + options[:method] = val + else + options[:uri] = val + end + end + + if options[:uri].nil? + print_line(help_line) + print_line(opt_parser.usage) + return + end + + options[:method] ||= 'GET' + options[:uri] = URI(options[:uri]) + options + end + + def output_line(opts, line) + if opts[:output_file].nil? + if line[-2..-1] == "\r\n" + print_line(line[0..-3]) + elsif line[-1] == "\n" + print_line(line[0..-2]) + else + print_line(line) + end + else + opts[:output_file].write(line) + end + end + + def cmd_http_request(*args) + opts = parse_args(args) + return unless opts + + unless opts[:output_file].nil? + begin + opts[:output_file] = File.new(opts[:output_file], 'w') + rescue ::Errno::EACCES, Errno::EISDIR, Errno::ENOTDIR + print_error('Failed to open the specified file for output') + return + end + end + + uri = opts[:uri] + http_client = Rex::Proto::Http::Client.new( + uri.host, + uri.port, + {'Msf' => framework}, + uri.scheme == 'https', + opts[:ssl_version] + ) + + begin + http_client.connect + request = http_client.request_cgi( + 'agent' => opts[:user_agent], + 'data' => opts[:data], + 'headers' => opts[:headers], + 'method' => opts[:method], + 'password' => opts[:auth_password], + 'query' => uri.query, + 'uri' => uri.path, + 'username' => opts[:auth_username], + 'version' => opts[:version] + ) + + response = http_client.send_recv(request) + rescue ::OpenSSL::SSL::SSLError + print_error('Encountered an SSL error') + rescue ::Errno::ECONNRESET => ex + print_error('The connection was reset by the peer') + rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error + print_error('Encountered an error') + rescue ::Exception => ex + print_line("An error of type #{ex.class} happened, message is #{ex.message}") + ensure + http_client.close + end + return unless response + + if opts[:print_headers] + output_line(opts, response.cmd_string) + output_line(opts, response.headers.to_s) + end + + output_line(opts, response.body) if opts[:print_body] + opts[:output_file].close unless opts[:output_file].nil? + end + end + + def initialize(framework, opts) + super + add_console_dispatcher(ConsoleCommandDispatcher) + print_status("#{name} plugin loaded.") + end + + def cleanup + remove_console_dispatcher('HTTP Request') + end + + def name + 'Requests' + end + + def desc + 'Make Requests' + end + +protected +end + +end From 72650d72b15850de87582847781d813915dbc74e Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Fri, 13 Mar 2015 08:58:17 -0400 Subject: [PATCH 02/11] Use an authorization header and fix uri.path --- plugins/requests.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/plugins/requests.rb b/plugins/requests.rb index 3ed8e9045f..874365fcf2 100644 --- a/plugins/requests.rb +++ b/plugins/requests.rb @@ -32,7 +32,7 @@ class Plugin::HTTPRequest < Msf::Plugin '-i' => [ false, 'Include headers in the output' ], '-I' => [ false, 'Show document info only' ], '-o' => [ true, 'Write output to instead of stdout' ], - '-u' => [ true, 'Server user and password', ], + '-u' => [ true, 'Server user and password' ], '-X' => [ true, 'Request method to use' ] ) @@ -140,6 +140,14 @@ class Plugin::HTTPRequest < Msf::Plugin opts[:ssl_version] ) + unless opts[:auth_username].nil? + auth_str = opts[:auth_username].to_s + ':' + opts[:auth_password].to_s + auth_str = 'Basic ' + Rex::Text.encode_base64(auth_str) + opts[:headers]['Authorization'] = auth_str + end + + uri.path = '/' if uri.path.length == 0 + begin http_client.connect request = http_client.request_cgi( @@ -161,8 +169,8 @@ class Plugin::HTTPRequest < Msf::Plugin print_error('The connection was reset by the peer') rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error print_error('Encountered an error') - rescue ::Exception => ex - print_line("An error of type #{ex.class} happened, message is #{ex.message}") + #rescue ::Exception => ex + # print_line("An error of type #{ex.class} happened, message is #{ex.message}") ensure http_client.close end @@ -193,7 +201,7 @@ class Plugin::HTTPRequest < Msf::Plugin end def desc - 'Make Requests' + 'Make HTTP requests from within Metasploit.' end protected From 2070934758d259730ba699d2c5374aa4f9cb881a Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Fri, 13 Mar 2015 09:36:57 -0400 Subject: [PATCH 03/11] Improve output file handling and expand_path --- plugins/requests.rb | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/plugins/requests.rb b/plugins/requests.rb index 874365fcf2..9cc04b19ee 100644 --- a/plugins/requests.rb +++ b/plugins/requests.rb @@ -26,7 +26,7 @@ class Plugin::HTTPRequest < Msf::Plugin '-3' => [ false, 'Use SSLv3 (SSL)' ], '-A' => [ true, 'User-Agent to send to server' ], '-d' => [ true, 'HTTP POST data' ], - '-G' => [ false, 'Send the -d data with a HTTP GET' ], + '-G' => [ false, 'Send the -d data with an HTTP GET' ], '-h' => [ false, 'This help text' ], '-H' => [ true, 'Custom header to pass to server' ], '-i' => [ false, 'Include headers in the output' ], @@ -81,7 +81,7 @@ class Plugin::HTTPRequest < Msf::Plugin options[:print_body] = false options[:method] ||= 'HEAD' when '-o' - options[:output_file] = val + options[:output_file] = File.expand_path(val) when '-u' val = val.partition(':') options[:auth_username] = val[0] @@ -174,7 +174,11 @@ class Plugin::HTTPRequest < Msf::Plugin ensure http_client.close end - return unless response + + unless response + opts[:output_file].close unless opts[:output_file].nil? + return + end if opts[:print_headers] output_line(opts, response.cmd_string) @@ -182,14 +186,16 @@ class Plugin::HTTPRequest < Msf::Plugin end output_line(opts, response.body) if opts[:print_body] - opts[:output_file].close unless opts[:output_file].nil? + unless opts[:output_file].nil? + print_status("Wrote #{opts[:output_file].tell} bytes to #{opts[:output_file].path}") + opts[:output_file].close + end end end def initialize(framework, opts) super add_console_dispatcher(ConsoleCommandDispatcher) - print_status("#{name} plugin loaded.") end def cleanup From 59f40d73e3a1afc2fcfbcb4a108d542a2ee59100 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Fri, 13 Mar 2015 13:13:18 -0400 Subject: [PATCH 04/11] Rename the requests plugin to http_requests --- plugins/{requests.rb => http_requests.rb} | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) rename plugins/{requests.rb => http_requests.rb} (95%) diff --git a/plugins/requests.rb b/plugins/http_requests.rb similarity index 95% rename from plugins/requests.rb rename to plugins/http_requests.rb index 9cc04b19ee..45b42e4d89 100644 --- a/plugins/requests.rb +++ b/plugins/http_requests.rb @@ -2,13 +2,13 @@ require 'uri' module Msf -class Plugin::HTTPRequest < Msf::Plugin +class Plugin::HTTPRequests < Msf::Plugin class ConsoleCommandDispatcher include Msf::Ui::Console::CommandDispatcher def name - 'Request' + 'HTTP Requests' end def commands @@ -75,10 +75,10 @@ class Plugin::HTTPRequest < Msf::Plugin name, _, value = val.partition(':') options[:headers][name] = value.strip when '-i' - options[:print_headers] = true + options[:print_headers] = true when '-I' - options[:print_headers] = true - options[:print_body] = false + options[:print_headers] = true + options[:print_body] = false options[:method] ||= 'HEAD' when '-o' options[:output_file] = File.expand_path(val) @@ -199,11 +199,11 @@ class Plugin::HTTPRequest < Msf::Plugin end def cleanup - remove_console_dispatcher('HTTP Request') + remove_console_dispatcher('HTTP Requests') end def name - 'Requests' + 'HTTP Requests' end def desc From 2e8e350608f864b57ffff233c454f04448bd1443 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Tue, 24 Mar 2015 17:20:32 -0400 Subject: [PATCH 05/11] Rename the http_requests plugin and command to httpr --- plugins/{http_requests.rb => httpr.rb} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename plugins/{http_requests.rb => httpr.rb} (97%) diff --git a/plugins/http_requests.rb b/plugins/httpr.rb similarity index 97% rename from plugins/http_requests.rb rename to plugins/httpr.rb index 45b42e4d89..07ad1ddc97 100644 --- a/plugins/http_requests.rb +++ b/plugins/httpr.rb @@ -13,12 +13,12 @@ class Plugin::HTTPRequests < Msf::Plugin def commands { - 'http_request' => 'Make an HTTP request' + 'httpr' => 'Make an HTTP request' } end def parse_args(args) - help_line = 'Usage: http_request [options] uri' + help_line = 'Usage: httpr [options] uri' opt_parser = Rex::Parser::Arguments.new( '-0' => [ false, 'Use HTTP 1.0' ], '-1' => [ false, 'Use TLSv1 (SSL)' ], @@ -118,7 +118,7 @@ class Plugin::HTTPRequests < Msf::Plugin end end - def cmd_http_request(*args) + def cmd_httpr(*args) opts = parse_args(args) return unless opts From cefec81dbd386b76d97a63f60f7df0bc1045d046 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Thu, 14 May 2015 21:21:18 -0500 Subject: [PATCH 06/11] move plugins/http to plugins/request --- plugins/{httpr.rb => request.rb} | 235 +++++++++++++++++-------------- 1 file changed, 133 insertions(+), 102 deletions(-) rename plugins/{httpr.rb => request.rb} (61%) diff --git a/plugins/httpr.rb b/plugins/request.rb similarity index 61% rename from plugins/httpr.rb rename to plugins/request.rb index 07ad1ddc97..cd2a837a85 100644 --- a/plugins/httpr.rb +++ b/plugins/request.rb @@ -2,23 +2,68 @@ require 'uri' module Msf -class Plugin::HTTPRequests < Msf::Plugin +class Plugin::Requests < Msf::Plugin class ConsoleCommandDispatcher include Msf::Ui::Console::CommandDispatcher def name - 'HTTP Requests' + 'Request' end def commands { - 'httpr' => 'Make an HTTP request' + 'request' => 'Make a request of the specified type', } end - def parse_args(args) - help_line = 'Usage: httpr [options] uri' + def types + # dynamically figure out what types are supported based on parse_args_* + parse_methods = self.public_methods.select {|m| m.to_s =~ /^parse_args_/} + parse_methods.collect {|m| m.to_s.split('_').slice(2..-1).join('_')} + end + + def cmd_request(*args) + # grab and validate the first arg as type, which will affect how the + # remaining args are parsed + type = args.shift + # short circuit the whole deal if they need help + return help if (!type || type =~ /^-?-h(?:elp)?$/) + type.downcase! + opts, opt_parser = parse_args(type, args) + if opts && opt_parser + if opts[:output_file] + begin + opts[:output_file] = File.new(opts[:output_file], 'w') + rescue ::Errno::EACCES, Errno::EISDIR, Errno::ENOTDIR + return help(opt_parser, 'Failed to open the specified file for output') + end + end + handler_method = "handle_request_#{type}".to_sym + if self.respond_to?(handler_method) + # call the appropriate request handler + self.send(handler_method, opts, opt_parser) + else + help(opt_parser, "No request handler found for type (#{type.to_s}).") + end + else + help(opt_parser, "No valid options provided for request #{type}") + end + end + + def parse_args(type, args) + type.downcase! + #print_line "type is #{type}" + parse_method = "parse_args_#{type}".to_sym + if self.respond_to?(parse_method) + self.send(parse_method, args, type) + else + print_line('Unrecognized type.') + help + end + end + + def parse_args_http(args = [], type = 'http') opt_parser = Rex::Parser::Arguments.new( '-0' => [ false, 'Use HTTP 1.0' ], '-1' => [ false, 'Use TLSv1 (SSL)' ], @@ -37,15 +82,10 @@ class Plugin::HTTPRequests < Msf::Plugin ) options = { - :auth_password => nil, - :auth_username => nil, - :headers => { }, + :headers => {}, :print_body => true, :print_headers => false, - :method => nil, - :output_file => nil, :ssl_version => 'Auto', - :uri => nil, :user_agent => Rex::Proto::Http::Client::DefaultUserAgent, :version => '1.1' } @@ -68,11 +108,9 @@ class Plugin::HTTPRequests < Msf::Plugin when '-G' options[:method] = 'GET' when '-h' - print_line(help_line) - print_line(opt_parser.usage) - return + return help(opt_parser, "Usage: request #{type} [options] uri") when '-H' - name, _, value = val.partition(':') + name, _, value = val.split(':') options[:headers][name] = value.strip when '-i' options[:print_headers] = true @@ -83,25 +121,83 @@ class Plugin::HTTPRequests < Msf::Plugin when '-o' options[:output_file] = File.expand_path(val) when '-u' - val = val.partition(':') - options[:auth_username] = val[0] - options[:auth_password] = val[2] + options[:auth_username] = val + when '-p' + options[:auth_password] = val when '-X' options[:method] = val else options[:uri] = val end end - - if options[:uri].nil? - print_line(help_line) - print_line(opt_parser.usage) - return + unless options[:uri] + return help(opt_parser, "Usage: request #{type} [options] uri") end - options[:method] ||= 'GET' options[:uri] = URI(options[:uri]) - options + [options, opt_parser] + end + + def handle_request_http(opts, opt_parser) + uri = opts[:uri] + http_client = Rex::Proto::Http::Client.new( + uri.host, + uri.port, + {'Msf' => framework}, + uri.scheme == 'https', + opts[:ssl_version] + ) + + if opts[:auth_username] + auth_str = opts[:auth_username] + ':' + opts[:auth_password] + auth_str = 'Basic ' + Rex::Text.encode_base64(auth_str) + opts[:headers]['Authorization'] = auth_str + end + + uri.path = '/' if uri.path.length == 0 + + begin + http_client.connect + req = http_client.request_cgi( + 'agent' => opts[:user_agent], + 'data' => opts[:data], + 'headers' => opts[:headers], + 'method' => opts[:method], + 'password' => opts[:auth_password], + 'query' => uri.query, + 'uri' => uri.path, + 'username' => opts[:auth_username], + 'version' => opts[:version] + ) + + response = http_client.send_recv(req) + rescue ::OpenSSL::SSL::SSLError + print_error('Encountered an SSL error') + rescue ::Errno::ECONNRESET => ex + print_error('The connection was reset by the peer') + rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error + print_error('Encountered an error') + #rescue ::Exception => ex + # print_line("An error of type #{ex.class} happened, message is #{ex.message}") + ensure + http_client.close + end + + unless response + opts[:output_file].close if opts[:output_file] + return nil + end + + if opts[:print_headers] + output_line(opts, response.cmd_string) + output_line(opts, response.headers.to_s) + end + + output_line(opts, response.body) if opts[:print_body] + if opts[:output_file] + print_status("Wrote #{opts[:output_file].tell} bytes to #{opts[:output_file].path}") + opts[:output_file].close + end end def output_line(opts, line) @@ -118,79 +214,15 @@ class Plugin::HTTPRequests < Msf::Plugin end end - def cmd_httpr(*args) - opts = parse_args(args) - return unless opts - - unless opts[:output_file].nil? - begin - opts[:output_file] = File.new(opts[:output_file], 'w') - rescue ::Errno::EACCES, Errno::EISDIR, Errno::ENOTDIR - print_error('Failed to open the specified file for output') - return - end - end - - uri = opts[:uri] - http_client = Rex::Proto::Http::Client.new( - uri.host, - uri.port, - {'Msf' => framework}, - uri.scheme == 'https', - opts[:ssl_version] - ) - - unless opts[:auth_username].nil? - auth_str = opts[:auth_username].to_s + ':' + opts[:auth_password].to_s - auth_str = 'Basic ' + Rex::Text.encode_base64(auth_str) - opts[:headers]['Authorization'] = auth_str - end - - uri.path = '/' if uri.path.length == 0 - - begin - http_client.connect - request = http_client.request_cgi( - 'agent' => opts[:user_agent], - 'data' => opts[:data], - 'headers' => opts[:headers], - 'method' => opts[:method], - 'password' => opts[:auth_password], - 'query' => uri.query, - 'uri' => uri.path, - 'username' => opts[:auth_username], - 'version' => opts[:version] - ) - - response = http_client.send_recv(request) - rescue ::OpenSSL::SSL::SSLError - print_error('Encountered an SSL error') - rescue ::Errno::ECONNRESET => ex - print_error('The connection was reset by the peer') - rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error - print_error('Encountered an error') - #rescue ::Exception => ex - # print_line("An error of type #{ex.class} happened, message is #{ex.message}") - ensure - http_client.close - end - - unless response - opts[:output_file].close unless opts[:output_file].nil? - return - end - - if opts[:print_headers] - output_line(opts, response.cmd_string) - output_line(opts, response.headers.to_s) - end - - output_line(opts, response.body) if opts[:print_body] - unless opts[:output_file].nil? - print_status("Wrote #{opts[:output_file].tell} bytes to #{opts[:output_file].path}") - opts[:output_file].close + def help(opt_parser = nil, msg = 'Usage: request type [options]') + print_line(msg) + if opt_parser + print_line(opt_parser.usage) + else + print_line("Valid types are: #{types.join(', ')}") end end + end def initialize(framework, opts) @@ -199,18 +231,17 @@ class Plugin::HTTPRequests < Msf::Plugin end def cleanup - remove_console_dispatcher('HTTP Requests') + remove_console_dispatcher('Request') end def name - 'HTTP Requests' + 'Request' end def desc - 'Make HTTP requests from within Metasploit.' + 'Make requests from within Metasploit using various protocols.' end -protected -end +end # end class -end +end # end module From f1e48b93345de8e4f03ac69274464534dc71f087 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Wed, 27 May 2015 15:40:27 -0500 Subject: [PATCH 07/11] genericizes http request plugin --- plugins/request.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/plugins/request.rb b/plugins/request.rb index cd2a837a85..aeb4dbccec 100644 --- a/plugins/request.rb +++ b/plugins/request.rb @@ -13,7 +13,7 @@ class Plugin::Requests < Msf::Plugin def commands { - 'request' => 'Make a request of the specified type', + 'request' => "Make a request of the specified type (#{types.join(', ')})", } end @@ -31,7 +31,9 @@ class Plugin::Requests < Msf::Plugin return help if (!type || type =~ /^-?-h(?:elp)?$/) type.downcase! opts, opt_parser = parse_args(type, args) + if opts && opt_parser + # handle any "global" options if opts[:output_file] begin opts[:output_file] = File.new(opts[:output_file], 'w') @@ -39,6 +41,7 @@ class Plugin::Requests < Msf::Plugin return help(opt_parser, 'Failed to open the specified file for output') end end + # hand off the actual request to the appropriate request handler handler_method = "handle_request_#{type}".to_sym if self.respond_to?(handler_method) # call the appropriate request handler @@ -63,6 +66,7 @@ class Plugin::Requests < Msf::Plugin end end + # arg parsing for requests of type 'http' def parse_args_http(args = [], type = 'http') opt_parser = Rex::Parser::Arguments.new( '-0' => [ false, 'Use HTTP 1.0' ], @@ -78,7 +82,9 @@ class Plugin::Requests < Msf::Plugin '-I' => [ false, 'Show document info only' ], '-o' => [ true, 'Write output to instead of stdout' ], '-u' => [ true, 'Server user and password' ], - '-X' => [ true, 'Request method to use' ] + '-X' => [ true, 'Request method to use' ], + '-x' => [ true, 'Proxy to use, format: [proto://][user:pass@]host[:port]' + + ' Proto defaults to http:// and port to 1080'], ) options = { @@ -126,6 +132,8 @@ class Plugin::Requests < Msf::Plugin options[:auth_password] = val when '-X' options[:method] = val + #when '-x' + # @TODO proxy else options[:uri] = val end @@ -138,6 +146,7 @@ class Plugin::Requests < Msf::Plugin [options, opt_parser] end + # handling for requests of type 'http' def handle_request_http(opts, opt_parser) uri = opts[:uri] http_client = Rex::Proto::Http::Client.new( From 7d896ba5d2129a468cea97e4d7b785d1e8803330 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Wed, 27 May 2015 15:44:53 -0500 Subject: [PATCH 08/11] removes a proxy vestige --- plugins/request.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/request.rb b/plugins/request.rb index aeb4dbccec..6b6d4d82cb 100644 --- a/plugins/request.rb +++ b/plugins/request.rb @@ -82,9 +82,9 @@ class Plugin::Requests < Msf::Plugin '-I' => [ false, 'Show document info only' ], '-o' => [ true, 'Write output to instead of stdout' ], '-u' => [ true, 'Server user and password' ], - '-X' => [ true, 'Request method to use' ], - '-x' => [ true, 'Proxy to use, format: [proto://][user:pass@]host[:port]' + - ' Proto defaults to http:// and port to 1080'], + '-X' => [ true, 'Request method to use' ] + #'-x' => [ true, 'Proxy to use, format: [proto://][user:pass@]host[:port]' + + # ' Proto defaults to http:// and port to 1080'], ) options = { From b236187e3f428a48e2ede0e1228886343ede31f0 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Thu, 28 May 2015 11:42:33 -0500 Subject: [PATCH 09/11] restores proper -u usage --- plugins/request.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/request.rb b/plugins/request.rb index 6b6d4d82cb..908e3c2fd9 100644 --- a/plugins/request.rb +++ b/plugins/request.rb @@ -127,7 +127,12 @@ class Plugin::Requests < Msf::Plugin when '-o' options[:output_file] = File.expand_path(val) when '-u' - options[:auth_username] = val + val = val.split(':', 2) # only split on first ':' as per curl: + # from curl man page: "The user name and passwords are split up on the + # first colon, which makes it impossible to use a colon in the user + # name with this option. The password can, still. + options[:auth_username] = val.first + options[:auth_password] = val.last when '-p' options[:auth_password] = val when '-X' @@ -186,8 +191,6 @@ class Plugin::Requests < Msf::Plugin print_error('The connection was reset by the peer') rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error print_error('Encountered an error') - #rescue ::Exception => ex - # print_line("An error of type #{ex.class} happened, message is #{ex.message}") ensure http_client.close end From fa9a222d97f65b6b9294223142aacc018552ba6e Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Thu, 28 May 2015 21:38:43 -0500 Subject: [PATCH 10/11] changes type handling to be fully automatic --- plugins/request.rb | 61 +++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/plugins/request.rb b/plugins/request.rb index 908e3c2fd9..f503950a55 100644 --- a/plugins/request.rb +++ b/plugins/request.rb @@ -7,6 +7,8 @@ class Plugin::Requests < Msf::Plugin class ConsoleCommandDispatcher include Msf::Ui::Console::CommandDispatcher + HELP_REGEX = /^-?-h(?:elp)?$/ + def name 'Request' end @@ -24,14 +26,21 @@ class Plugin::Requests < Msf::Plugin end def cmd_request(*args) - # grab and validate the first arg as type, which will affect how the - # remaining args are parsed - type = args.shift # short circuit the whole deal if they need help - return help if (!type || type =~ /^-?-h(?:elp)?$/) - type.downcase! - opts, opt_parser = parse_args(type, args) + return help if args.length == 0 + return help if args.length == 1 && args.first =~ HELP_REGEX + # detect the request type from the uri which must be the last arg given + uri = args.last + if uri && uri =~ /^[A-Za-z]{3,5}:\/\// + type = uri.split('://', 2).first + else + print_error("The last argument must be a valid and supported URI") + return help + end + + # parse options + opts, opt_parser = parse_args(args, type) if opts && opt_parser # handle any "global" options if opts[:output_file] @@ -47,25 +56,34 @@ class Plugin::Requests < Msf::Plugin # call the appropriate request handler self.send(handler_method, opts, opt_parser) else + # this should be dead code if parse_args is doing it's job correctly help(opt_parser, "No request handler found for type (#{type.to_s}).") end else - help(opt_parser, "No valid options provided for request #{type}") + if types.include? type + help(opt_parser) + else + help + end end end - def parse_args(type, args) + def parse_args(args, type = 'http') type.downcase! - #print_line "type is #{type}" parse_method = "parse_args_#{type}".to_sym if self.respond_to?(parse_method) self.send(parse_method, args, type) else - print_line('Unrecognized type.') - help + print_error("Unsupported URI type: #{type}") end end + # arg parsing for requests of type 'http' + def parse_args_https(args = [], type = 'https') + # just let http do it + parse_args_http(args, type) + end + # arg parsing for requests of type 'http' def parse_args_http(args = [], type = 'http') opt_parser = Rex::Parser::Arguments.new( @@ -113,10 +131,12 @@ class Plugin::Requests < Msf::Plugin options[:method] ||= 'POST' when '-G' options[:method] = 'GET' - when '-h' - return help(opt_parser, "Usage: request #{type} [options] uri") + when HELP_REGEX + #help(opt_parser) + # guard to prevent further option processing & stymie request handling + return [nil, opt_parser] when '-H' - name, _, value = val.split(':') + name, value = val.split(':', 2) options[:headers][name] = value.strip when '-i' options[:print_headers] = true @@ -144,13 +164,19 @@ class Plugin::Requests < Msf::Plugin end end unless options[:uri] - return help(opt_parser, "Usage: request #{type} [options] uri") + help(opt_parser) end options[:method] ||= 'GET' options[:uri] = URI(options[:uri]) [options, opt_parser] end + # handling for requests of type 'https' + def handle_request_https(opts, opt_parser) + # let http do it + handle_request_http(opts, opt_parser) + end + # handling for requests of type 'http' def handle_request_http(opts, opt_parser) uri = opts[:uri] @@ -226,12 +252,13 @@ class Plugin::Requests < Msf::Plugin end end - def help(opt_parser = nil, msg = 'Usage: request type [options]') + def help(opt_parser = nil, msg = 'Usage: request [options] uri') print_line(msg) if opt_parser print_line(opt_parser.usage) else - print_line("Valid types are: #{types.join(', ')}") + print_line("Supported uri types are: #{types.collect{|t| t + '://'}.join(', ')}") + print_line("To see usage for a specific uri type, use request -h uri") end end From 0313f0b0cf77db02c2ca2eaf5c20cc9023929609 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Tue, 2 Jun 2015 18:31:48 -0400 Subject: [PATCH 11/11] Check for a nil header value --- plugins/request.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/request.rb b/plugins/request.rb index f503950a55..de5c720a49 100644 --- a/plugins/request.rb +++ b/plugins/request.rb @@ -137,7 +137,7 @@ class Plugin::Requests < Msf::Plugin return [nil, opt_parser] when '-H' name, value = val.split(':', 2) - options[:headers][name] = value.strip + options[:headers][name] = value.to_s.strip when '-i' options[:print_headers] = true when '-I'