diff --git a/lib/msf/core/exploit/winrm.rb b/lib/msf/core/exploit/winrm.rb index 9037e99e5a..03fc2a6f3a 100644 --- a/lib/msf/core/exploit/winrm.rb +++ b/lib/msf/core/exploit/winrm.rb @@ -25,7 +25,7 @@ module Msf OptString.new('VHOST', [ false, "HTTP server virtual host" ]), OptBool.new('SSL', [ false, 'Negotiate SSL for outgoing connections', false]), OptEnum.new('SSLVersion', [ false, 'Specify the version of SSL that should be used', 'SSL3', ['SSL2', 'SSL3', 'TLS1']]), - OptString.new('DOMAIN', [ true, 'The domain to use for windows authentification', 'WORKSTATION']), + OptString.new('DOMAIN', [ true, 'The domain to use for Windows authentification', 'WORKSTATION']), OptString.new('URI', [ true, "The URI of the WinRM service", "/wsman" ]), OptString.new('USERNAME', [ false, 'A specific username to authenticate as' ]), OptString.new('PASSWORD', [ false, 'A specific password to authenticate with' ]), @@ -35,10 +35,11 @@ module Msf register_autofilter_ports([ 80,443,5985,5986 ]) register_autofilter_services(%W{ winrm }) end + def winrm_poke(timeout = 20) opts = { 'uri' => datastore['URI'], - 'data' => 'test' + 'data' => Rex::Text.rand_text_alpha(8) } c = connect(opts) to = opts[:timeout] || timeout @@ -51,14 +52,16 @@ module Msf })) return resp end + def parse_auth_methods(resp) - return [] unless resp.code == 401 + return [] unless resp and resp.code == 401 methods = [] methods << "Negotiate" if resp.headers['WWW-Authenticate'].include? "Negotiate" methods << "Kerberos" if resp.headers['WWW-Authenticate'].include? "Kerberos" methods << "Basic" if resp.headers['WWW-Authenticate'].include? "Basic" return methods end + def winrm_run_cmd(cmd, timeout=20) resp,c = send_request_ntlm(winrm_open_shell_msg,timeout) if resp.code == 401 @@ -67,7 +70,8 @@ module Msf end unless resp.code == 200 print_error "Got unexpected response: \n #{resp.to_s}" - return resp.code + retval == resp.code || 0 + return retval end shell_id = winrm_get_shell_id(resp) resp,c = send_request_ntlm(winrm_cmd_msg(cmd, shell_id),timeout) @@ -85,6 +89,7 @@ module Msf msg = winrm_envelope(contents) return msg end + def winrm_open_shell_msg action = winrm_uri_action("create_shell") options = winrm_option_set([['WINRS_NOPROFILE', 'FALSE'], ['WINRS_CODEPAGE', '437']]) @@ -93,6 +98,7 @@ module Msf msg = winrm_envelope(contents) return msg end + def winrm_cmd_msg(cmd,shell_id) action = winrm_uri_action("send_cmd") options = winrm_option_set([['WINRS_CONSOLEMODE_STDIN', 'TRUE'], ['WINRS_SKIP_CMD_SHELL', 'FALSE']]) @@ -102,6 +108,7 @@ module Msf msg = winrm_envelope(contents) return msg end + def winrm_cmd_recv_msg(shell_id,cmd_id) action = winrm_uri_action("recv_cmd") selectors = winrm_selector_set([['ShellId', shell_id]]) @@ -110,6 +117,7 @@ module Msf msg = winrm_envelope(contents) return msg end + def winrm_terminate_cmd_msg(shell_id,cmd_id) action = winrm_uri_action("signal_shell") selectors = winrm_selector_set([['ShellId', shell_id]]) @@ -118,6 +126,7 @@ module Msf msg = winrm_envelope(contents) return msg end + def winrm_delete_shell_msg(shell_id) action = winrm_uri_action("delete_shell") selectors = winrm_selector_set([['ShellId', shell_id]]) @@ -152,14 +161,17 @@ module Msf end return response_data end + def winrm_get_shell_id(response) xml = response.body shell_id = REXML::Document.new(xml).elements["//w:Selector"].text end + def winrm_get_cmd_id(response) xml = response.body cmd_id = REXML::Document.new(xml).elements["//rsp:CommandId"].text end + def winrm_get_cmd_streams(response) streams = { 'stdout' => '', @@ -169,127 +181,13 @@ module Msf rxml = REXML::Document.new(xml).root rxml.elements.to_a("//rsp:Stream").each do |node| next if node.text.nil? - streams[node.attributes['Name']] << Base64.decode64(node.text) + streams[node.attributes['Name']] << Rex::Text.base64_decode(node.text) end return streams end - def winrm_option_set(options) - xml = "" - options.each do |option_pair| - xml << winrm_option(*option_pair) - end - xml << "" - return xml - end - def winrm_option(name,value) - %Q{#{value}} - end - def winrm_selector_set(selectors) - xml = "" - selectors.each do |selector_pair| - xml << winrm_selector(*selector_pair) - end - xml << "" - return xml - end - def winrm_selector(name,value) - %Q{#{value}} - end - def winrm_wql_body(wql) - %Q{ - - - - 32000 - #{wql} - - - } - end - def winrm_open_shell_body - %q{ - - stdin - stdout stderr - - } - end - def winrm_cmd_body(cmd) - %Q{ - - "#{cmd}" - - } - end - def winrm_cmd_recv_body(cmd_id) - %Q{ - - stdout stderr - - } - end - def winrm_terminate_cmd_body(cmd_id) - %Q{ - - http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/terminate - - } - end - def winrm_empty_body - %q{} - end - def winrm_envelope(data) - %Q{ - - #{data} - } - end - def winrm_header(data) - %Q{ - - #{target_url} - - http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous - - 153600 - uuid:#{generate_uuid} - - - PT60S - #{data} - - } - end - def winrm_uri_action(type) - case type - when "wql" - return %q{http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/* - http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate} - when "create_shell" - return %q{http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd - http://schemas.xmlsoap.org/ws/2004/09/transfer/Create} - when "send_cmd" - return %q{http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd - http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command} - when "recv_cmd" - return %q{http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd - http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive} - when "signal_shell" - return %q{http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd - http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Signal} - when "delete_shell" - return %q{http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd - http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete} - end - end + def generate_uuid - bytes = ::SecureRandom.random_bytes(16) - ::Rex::Proto::DCERPC::UUID.uuid_unpack(bytes) + ::Rex::Proto::DCERPC::UUID.uuid_unpack(Rex::Text.rand_text(16)) end def send_request_ntlm(data, timeout = 20) @@ -379,9 +277,11 @@ module Msf rescue ::Errno::EPIPE, ::Timeout::Error end end + def accepts_ntlm_auth parse_auth_methods(winrm_poke).include? "Negotiate" end + def target_url proto = "http" if rport == 5986 or datastore['SSL'] @@ -393,5 +293,135 @@ module Msf return "#{proto}://#{rhost}:#{rport}#{@uri.to_s}" end end + + private + + def winrm_option_set(options) + xml = "" + options.each do |option_pair| + xml << winrm_option(*option_pair) + end + xml << "" + return xml + end + + def winrm_option(name,value) + %Q{#{value}} + end + + def winrm_selector_set(selectors) + xml = "" + selectors.each do |selector_pair| + xml << winrm_selector(*selector_pair) + end + xml << "" + return xml + end + + def winrm_selector(name,value) + %Q{#{value}} + end + + def winrm_wql_body(wql) + %Q{ + + + + 32000 + #{wql} + + + } + end + + def winrm_open_shell_body + %q{ + + stdin + stdout stderr + + } + end + + def winrm_cmd_body(cmd) + %Q{ + + "#{cmd}" + + } + end + + def winrm_cmd_recv_body(cmd_id) + %Q{ + + stdout stderr + + } + end + + def winrm_terminate_cmd_body(cmd_id) + %Q{ + + http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/terminate + + } + end + + def winrm_empty_body + %q{} + end + + def winrm_envelope(data) + %Q{ + + #{data} + } + end + + def winrm_header(data) + %Q{ + + #{target_url} + + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + 153600 + uuid:#{generate_uuid} + + + PT60S + #{data} + + } + end + + def winrm_uri_action(type) + case type + when "wql" + return %q{http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/* + http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate} + when "create_shell" + return %q{http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd + http://schemas.xmlsoap.org/ws/2004/09/transfer/Create} + when "send_cmd" + return %q{http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd + http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command} + when "recv_cmd" + return %q{http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd + http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive} + when "signal_shell" + return %q{http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd + http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Signal} + when "delete_shell" + return %q{http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd + http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete} + end + end + end end diff --git a/modules/auxiliary/scanner/winrm/winrm_auth_methods.rb b/modules/auxiliary/scanner/winrm/winrm_auth_methods.rb index 591ca20093..7dc1b3779f 100644 --- a/modules/auxiliary/scanner/winrm/winrm_auth_methods.rb +++ b/modules/auxiliary/scanner/winrm/winrm_auth_methods.rb @@ -27,7 +27,7 @@ class Metasploit3 < Msf::Auxiliary 'Name' => 'WinRM Authentication Method Detection', 'Version' => '$Revision$', 'Description' => %q{ - This module sends a request to a an http/https service to see if it is a WinRM service. + This module sends a request to an HTTP/HTTPS service to see if it is a WinRM service. If it is a WinRM service, it also gathers the Authentication Methods supported. }, 'Author' => [ 'thelightcosine' ],