diff --git a/lib/msf/core/exploit/mixins.rb b/lib/msf/core/exploit/mixins.rb
index 4bda434584..76cfac86e4 100644
--- a/lib/msf/core/exploit/mixins.rb
+++ b/lib/msf/core/exploit/mixins.rb
@@ -88,3 +88,6 @@ require 'msf/core/exploit/java'
# WBEM
require 'msf/core/exploit/wbemexec'
+#WinRM
+require 'msf/core/exploit/winrm'
+
diff --git a/lib/msf/core/exploit/winrm.rb b/lib/msf/core/exploit/winrm.rb
new file mode 100644
index 0000000000..40db61aa8b
--- /dev/null
+++ b/lib/msf/core/exploit/winrm.rb
@@ -0,0 +1,471 @@
+# -*- coding: binary -*-
+require 'uri'
+require 'digest'
+require 'rex/proto/ntlm/crypt'
+require 'rex/proto/ntlm/constants'
+require 'rex/proto/ntlm/utils'
+require 'rex/proto/ntlm/exceptions'
+
+module Msf
+module Exploit::Remote::WinRM
+
+ include Exploit::Remote::NTLM::Client
+ include Exploit::Remote::HttpClient
+
+ #
+ # Constants
+ #
+ NTLM_CRYPT ||= Rex::Proto::NTLM::Crypt
+ NTLM_CONST ||= Rex::Proto::NTLM::Constants
+ NTLM_UTILS ||= Rex::Proto::NTLM::Utils
+ NTLM_XCEPT ||= Rex::Proto::NTLM::Exceptions
+
+ def initialize(info = {})
+ super
+ register_options(
+ [
+ Opt::RHOST,
+ Opt::RPORT(5985),
+ 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('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' ])
+ ], self.class
+ )
+
+ register_autofilter_ports([ 80,443,5985,5986 ])
+ register_autofilter_services(%W{ winrm })
+ end
+
+ def winrm_poke(timeout = 20)
+ opts = {
+ 'uri' => datastore['URI'],
+ 'data' => Rex::Text.rand_text_alpha(8)
+ }
+
+ c = connect(opts)
+ to = opts[:timeout] || timeout
+ ctype = "application/soap+xml;charset=UTF-8"
+
+ resp, c = send_request_cgi(opts.merge({
+ 'uri' => opts['uri'],
+ 'method' => 'POST',
+ 'ctype' => ctype,
+ 'data' => opts['data']
+ }), to)
+
+ return resp
+ end
+
+ def parse_auth_methods(resp)
+ 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
+ print_error "Login failure! Recheck supplied credentials."
+ return resp .code
+ end
+
+ unless resp.code == 200
+ print_error "Got unexpected response: \n #{resp.to_s}"
+ 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)
+ cmd_id = winrm_get_cmd_id(resp)
+ resp,c = send_request_ntlm(winrm_cmd_recv_msg(shell_id,cmd_id),timeout)
+ streams = winrm_get_cmd_streams(resp)
+ resp,c = send_request_ntlm(winrm_terminate_cmd_msg(shell_id,cmd_id),timeout)
+ resp,c = send_request_ntlm(winrm_delete_shell_msg(shell_id))
+
+ return streams
+ end
+
+ def winrm_wql_msg(wql)
+ action = winrm_uri_action("wql")
+ contents = winrm_header(action) + winrm_wql_body(wql)
+ 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']])
+ header_data = action + options
+ contents = winrm_header(header_data) + winrm_open_shell_body
+ 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']])
+ selectors = winrm_selector_set([['ShellId', shell_id]])
+ header_data = action + options + selectors
+ contents = winrm_header(header_data) + winrm_cmd_body(cmd)
+ 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]])
+ header_data = action + selectors
+ contents = winrm_header(header_data) + winrm_cmd_recv_body(cmd_id)
+ 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]])
+ header_data = action + selectors
+ contents = winrm_header(header_data) + winrm_terminate_cmd_body(cmd_id)
+ 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]])
+ header_data = action + selectors
+ contents = winrm_header(header_data) + winrm_empty_body
+ msg = winrm_envelope(contents)
+
+ return msg
+ end
+
+ def parse_wql_response(response)
+ xml = response.body
+ columns = []
+ rows =[]
+ rxml = REXML::Document.new(xml).root
+ items = rxml.elements["///w:Items"]
+
+ items.elements.to_a("///w:XmlFragment").each do |node|
+ row_data = []
+
+ node.elements.to_a.each do |sub_node|
+ columns << sub_node.name
+ row_data << sub_node.text
+ end
+
+ rows << row_data
+ end
+
+ response_data = Rex::Ui::Text::Table.new(
+ 'Header' => "#{datastore['WQL']} (#{rhost})",
+ 'Indent' => 1,
+ 'Columns' => columns.uniq!
+ )
+
+ rows.each do |row|
+ response_data << row
+ 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' => '',
+ 'stderr' => '',
+ }
+
+ xml = response.body
+ rxml = REXML::Document.new(xml).root
+
+ rxml.elements.to_a("//rsp:Stream").each do |node|
+ next if node.text.nil?
+ streams[node.attributes['Name']] << Rex::Text.base64_decode(node.text)
+ end
+
+ return streams
+ end
+
+ def generate_uuid
+ ::Rex::Proto::DCERPC::UUID.uuid_unpack(Rex::Text.rand_text(16))
+ end
+
+ def send_request_ntlm(data, timeout = 20)
+ opts = {
+ 'uri' => datastore['URI'],
+ 'data' => data,
+ 'username' => datastore['USERNAME'],
+ 'password' => datastore['PASSWORD']
+ }
+
+ ntlm_options =
+ {
+ :signing => false,
+ :usentlm2_session => datastore['NTLM::UseNTLM2_session'],
+ :use_ntlmv2 => datastore['NTLM::UseNTLMv2'],
+ :send_lm => datastore['NTLM::SendLM'],
+ :send_ntlm => datastore['NTLM::SendNTLM']
+ }
+
+ ntlmssp_flags = NTLM_UTILS.make_ntlm_flags(ntlm_options)
+ workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
+ domain_name = datastore['DOMAIN']
+ ntlm_message_1 = "NEGOTIATE " + Rex::Text::encode_base64(NTLM_UTILS::make_ntlmssp_blob_init( domain_name,
+ workstation_name,
+ ntlmssp_flags))
+
+ to = opts[:timeout] || timeout
+
+ begin
+ c = connect(opts)
+ ctype = "application/soap+xml;charset=UTF-8"
+ # First request to get the challenge
+ r = c.request_cgi(opts.merge({
+ 'uri' => opts['uri'],
+ 'method' => 'POST',
+ 'ctype' => ctype,
+ 'headers' => { 'Authorization' => ntlm_message_1},
+ 'data' => opts['data']
+ }))
+
+ resp = c.send_recv(r, to)
+
+ unless resp.kind_of? Rex::Proto::Http::Response
+ return [nil,nil]
+ end
+
+ return [nil,nil] if resp.code == 404
+ return [nil,nil] unless resp.code == 401 && resp.headers['WWW-Authenticate']
+ # Get the challenge and craft the response
+ ntlm_challenge = resp.headers['WWW-Authenticate'].match(/NEGOTIATE ([A-Z0-9\x2b\x2f=]+)/i)[1]
+ return [nil,nil] unless ntlm_challenge
+
+ #old and simplier method but not compatible with windows 7/2008r2
+ #ntlm_message_2 = Rex::Proto::NTLM::Message.decode64(ntlm_challenge)
+ #ntlm_message_3 = ntlm_message_2.response( {:user => opts['username'],:password => opts['password']}, {:ntlmv2 => true})
+ ntlm_message_2 = Rex::Text::decode_base64(ntlm_challenge)
+ blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(ntlm_message_2)
+ challenge_key = blob_data[:challenge_key]
+ server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
+ #netbios name
+ default_name = blob_data[:default_name] || ''
+ #netbios domain
+ default_domain = blob_data[:default_domain] || ''
+ #dns name
+ dns_host_name = blob_data[:dns_host_name] || ''
+ #dns domain
+ dns_domain_name = blob_data[:dns_domain_name] || ''
+ #Client time
+ chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
+ spnopt = {:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost}
+ resp_lm,
+ resp_ntlm,
+ client_challenge,
+ ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(opts['username'], opts['password'], challenge_key,
+ domain_name, default_name, default_domain,
+ dns_host_name, dns_domain_name, chall_MsvAvTimestamp,
+ spnopt, ntlm_options)
+ ntlm_message_3 = NTLM_UTILS.make_ntlmssp_blob_auth(domain_name, workstation_name, opts['username'],
+ resp_lm, resp_ntlm, '', ntlmssp_flags)
+ ntlm_message_3 = Rex::Text::encode_base64(ntlm_message_3)
+
+ # Send the response
+ r = c.request_cgi(opts.merge({
+ 'uri' => opts['uri'],
+ 'method' => 'POST',
+ 'ctype' => ctype,
+ 'headers' => { 'Authorization' => "NEGOTIATE #{ntlm_message_3}"},
+ 'data' => opts['data']
+ }))
+
+ resp = c.send_recv(r, to, true)
+
+ unless resp.kind_of? Rex::Proto::Http::Response
+ return [nil,nil]
+ end
+
+ return [nil,nil] if resp.code == 404
+ return [resp,c]
+ 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']
+ proto = "https"
+ end
+
+ if datastore['VHOST']
+ return "#{proto}://#{datastore ['VHOST']}:#{rport}#{@uri.to_s}"
+ else
+ 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
new file mode 100644
index 0000000000..910fe06ff2
--- /dev/null
+++ b/modules/auxiliary/scanner/winrm/winrm_auth_methods.rb
@@ -0,0 +1,64 @@
+##
+# $Id$
+##
+
+##
+# This file is part of the Metasploit Framework and may be subject to
+# redistribution and commercial restrictions. Please see the Metasploit
+# web site for more information on licensing and terms of use.
+# http://metasploit.com/
+##
+
+
+require 'msf/core'
+require 'rex/proto/ntlm/message'
+
+
+class Metasploit3 < Msf::Auxiliary
+
+ include Msf::Exploit::Remote::WinRM
+ include Msf::Auxiliary::Report
+
+
+ include Msf::Auxiliary::Scanner
+
+ def initialize
+ super(
+ 'Name' => 'WinRM Authentication Method Detection',
+ 'Version' => '$Revision$',
+ 'Description' => %q{
+ 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' ],
+ 'License' => MSF_LICENSE
+ )
+
+ deregister_options('USERNAME', 'PASSWORD')
+
+ end
+
+
+ def run_host(ip)
+ resp = winrm_poke
+ return nil if resp.nil?
+ if resp.code == 401 and resp.headers['Server'].include? "Microsoft-HTTPAPI"
+ methods = parse_auth_methods(resp)
+ desc = resp.headers['Server'] + " Authentication Methods: " + methods.to_s
+ report_service(
+ :host => ip,
+ :port => rport,
+ :proto => 'tcp',
+ :name => 'winrm',
+ :info => desc
+ )
+ print_good "Negotiate protocol supported" if methods.include? "Negotiate"
+ print_good "Kerberos protocol supported" if methods.include? "Kerberos"
+ print_good "Basic protocol supported" if methods.include? "Basic"
+ else
+ print_error "#{ip}:#{rport} Does not appear to be a WinRM server"
+ end
+ end
+
+
+end