Merge branch 'dmaloney-r7-WinRM_piecemeal'
This commit is contained in:
commit
7a1c3e7cf6
|
@ -8,10 +8,8 @@ require 'rex/proto/ntlm/exceptions'
|
||||||
|
|
||||||
module Msf
|
module Msf
|
||||||
module Exploit::Remote::WinRM
|
module Exploit::Remote::WinRM
|
||||||
|
|
||||||
include Exploit::Remote::NTLM::Client
|
include Exploit::Remote::NTLM::Client
|
||||||
include Exploit::Remote::HttpClient
|
include Exploit::Remote::HttpClient
|
||||||
|
|
||||||
#
|
#
|
||||||
# Constants
|
# Constants
|
||||||
#
|
#
|
||||||
|
@ -19,20 +17,15 @@ module Exploit::Remote::WinRM
|
||||||
NTLM_CONST ||= Rex::Proto::NTLM::Constants
|
NTLM_CONST ||= Rex::Proto::NTLM::Constants
|
||||||
NTLM_UTILS ||= Rex::Proto::NTLM::Utils
|
NTLM_UTILS ||= Rex::Proto::NTLM::Utils
|
||||||
NTLM_XCEPT ||= Rex::Proto::NTLM::Exceptions
|
NTLM_XCEPT ||= Rex::Proto::NTLM::Exceptions
|
||||||
|
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super
|
super
|
||||||
register_options(
|
register_options(
|
||||||
[
|
[
|
||||||
Opt::RHOST,
|
|
||||||
Opt::RPORT(5985),
|
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('DOMAIN', [ true, 'The domain to use for Windows authentification', 'WORKSTATION']),
|
||||||
OptString.new('URI', [ true, "The URI of the WinRM service", "/wsman" ]),
|
OptString.new('URI', [ true, "The URI of the WinRM service", "/wsman" ]),
|
||||||
OptString.new('USERNAME', [ false, 'A specific username to authenticate as' ]),
|
OptString.new('USERNAME', [ false, 'A specific username to authenticate as' ]),
|
||||||
OptString.new('PASSWORD', [ false, 'A specific password to authenticate with' ])
|
OptString.new('PASSWORD', [ false, 'A specific password to authenticate with' ]),
|
||||||
], self.class
|
], self.class
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -45,18 +38,15 @@ module Exploit::Remote::WinRM
|
||||||
'uri' => datastore['URI'],
|
'uri' => datastore['URI'],
|
||||||
'data' => Rex::Text.rand_text_alpha(8)
|
'data' => Rex::Text.rand_text_alpha(8)
|
||||||
}
|
}
|
||||||
|
c = connect(opts)
|
||||||
c = connect(opts)
|
to = opts[:timeout] || timeout
|
||||||
to = opts[:timeout] || timeout
|
|
||||||
ctype = "application/soap+xml;charset=UTF-8"
|
ctype = "application/soap+xml;charset=UTF-8"
|
||||||
|
|
||||||
resp, c = send_request_cgi(opts.merge({
|
resp, c = send_request_cgi(opts.merge({
|
||||||
'uri' => opts['uri'],
|
'uri' => opts['uri'],
|
||||||
'method' => 'POST',
|
'method' => 'POST',
|
||||||
'ctype' => ctype,
|
'ctype' => ctype,
|
||||||
'data' => opts['data']
|
'data' => opts['data']
|
||||||
}), to)
|
}), to)
|
||||||
|
|
||||||
return resp
|
return resp
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -71,18 +61,15 @@ module Exploit::Remote::WinRM
|
||||||
|
|
||||||
def winrm_run_cmd(cmd, timeout=20)
|
def winrm_run_cmd(cmd, timeout=20)
|
||||||
resp,c = send_request_ntlm(winrm_open_shell_msg,timeout)
|
resp,c = send_request_ntlm(winrm_open_shell_msg,timeout)
|
||||||
|
|
||||||
if resp.code == 401
|
if resp.code == 401
|
||||||
print_error "Login failure! Recheck supplied credentials."
|
print_error "Login failure! Recheck supplied credentials."
|
||||||
return resp .code
|
return resp .code
|
||||||
end
|
end
|
||||||
|
|
||||||
unless resp.code == 200
|
unless resp.code == 200
|
||||||
print_error "Got unexpected response: \n #{resp.to_s}"
|
print_error "Got unexpected response: \n #{resp.to_s}"
|
||||||
retval == resp.code || 0
|
retval == resp.code || 0
|
||||||
return retval
|
return retval
|
||||||
end
|
end
|
||||||
|
|
||||||
shell_id = winrm_get_shell_id(resp)
|
shell_id = winrm_get_shell_id(resp)
|
||||||
resp,c = send_request_ntlm(winrm_cmd_msg(cmd, shell_id),timeout)
|
resp,c = send_request_ntlm(winrm_cmd_msg(cmd, shell_id),timeout)
|
||||||
cmd_id = winrm_get_cmd_id(resp)
|
cmd_id = winrm_get_cmd_id(resp)
|
||||||
|
@ -90,7 +77,6 @@ module Exploit::Remote::WinRM
|
||||||
streams = winrm_get_cmd_streams(resp)
|
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_terminate_cmd_msg(shell_id,cmd_id),timeout)
|
||||||
resp,c = send_request_ntlm(winrm_delete_shell_msg(shell_id))
|
resp,c = send_request_ntlm(winrm_delete_shell_msg(shell_id))
|
||||||
|
|
||||||
return streams
|
return streams
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -98,7 +84,6 @@ module Exploit::Remote::WinRM
|
||||||
action = winrm_uri_action("wql")
|
action = winrm_uri_action("wql")
|
||||||
contents = winrm_header(action) + winrm_wql_body(wql)
|
contents = winrm_header(action) + winrm_wql_body(wql)
|
||||||
msg = winrm_envelope(contents)
|
msg = winrm_envelope(contents)
|
||||||
|
|
||||||
return msg
|
return msg
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -108,7 +93,6 @@ module Exploit::Remote::WinRM
|
||||||
header_data = action + options
|
header_data = action + options
|
||||||
contents = winrm_header(header_data) + winrm_open_shell_body
|
contents = winrm_header(header_data) + winrm_open_shell_body
|
||||||
msg = winrm_envelope(contents)
|
msg = winrm_envelope(contents)
|
||||||
|
|
||||||
return msg
|
return msg
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -119,7 +103,6 @@ module Exploit::Remote::WinRM
|
||||||
header_data = action + options + selectors
|
header_data = action + options + selectors
|
||||||
contents = winrm_header(header_data) + winrm_cmd_body(cmd)
|
contents = winrm_header(header_data) + winrm_cmd_body(cmd)
|
||||||
msg = winrm_envelope(contents)
|
msg = winrm_envelope(contents)
|
||||||
|
|
||||||
return msg
|
return msg
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -129,7 +112,6 @@ module Exploit::Remote::WinRM
|
||||||
header_data = action + selectors
|
header_data = action + selectors
|
||||||
contents = winrm_header(header_data) + winrm_cmd_recv_body(cmd_id)
|
contents = winrm_header(header_data) + winrm_cmd_recv_body(cmd_id)
|
||||||
msg = winrm_envelope(contents)
|
msg = winrm_envelope(contents)
|
||||||
|
|
||||||
return msg
|
return msg
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -139,7 +121,6 @@ module Exploit::Remote::WinRM
|
||||||
header_data = action + selectors
|
header_data = action + selectors
|
||||||
contents = winrm_header(header_data) + winrm_terminate_cmd_body(cmd_id)
|
contents = winrm_header(header_data) + winrm_terminate_cmd_body(cmd_id)
|
||||||
msg = winrm_envelope(contents)
|
msg = winrm_envelope(contents)
|
||||||
|
|
||||||
return msg
|
return msg
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -149,7 +130,6 @@ module Exploit::Remote::WinRM
|
||||||
header_data = action + selectors
|
header_data = action + selectors
|
||||||
contents = winrm_header(header_data) + winrm_empty_body
|
contents = winrm_header(header_data) + winrm_empty_body
|
||||||
msg = winrm_envelope(contents)
|
msg = winrm_envelope(contents)
|
||||||
|
|
||||||
return msg
|
return msg
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -159,28 +139,23 @@ module Exploit::Remote::WinRM
|
||||||
rows =[]
|
rows =[]
|
||||||
rxml = REXML::Document.new(xml).root
|
rxml = REXML::Document.new(xml).root
|
||||||
items = rxml.elements["///w:Items"]
|
items = rxml.elements["///w:Items"]
|
||||||
|
|
||||||
items.elements.to_a("///w:XmlFragment").each do |node|
|
items.elements.to_a("///w:XmlFragment").each do |node|
|
||||||
row_data = []
|
row_data = []
|
||||||
|
|
||||||
node.elements.to_a.each do |sub_node|
|
node.elements.to_a.each do |sub_node|
|
||||||
columns << sub_node.name
|
columns << sub_node.name
|
||||||
row_data << sub_node.text
|
row_data << sub_node.text
|
||||||
end
|
end
|
||||||
|
|
||||||
rows << row_data
|
rows << row_data
|
||||||
end
|
end
|
||||||
|
columns.uniq!
|
||||||
response_data = Rex::Ui::Text::Table.new(
|
response_data = Rex::Ui::Text::Table.new(
|
||||||
'Header' => "#{datastore['WQL']} (#{rhost})",
|
'Header' => "#{datastore['WQL']} (#{rhost})",
|
||||||
'Indent' => 1,
|
'Indent' => 1,
|
||||||
'Columns' => columns.uniq!
|
'Columns' => columns
|
||||||
)
|
)
|
||||||
|
|
||||||
rows.each do |row|
|
rows.each do |row|
|
||||||
response_data << row
|
response_data << row
|
||||||
end
|
end
|
||||||
|
|
||||||
return response_data
|
return response_data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -197,17 +172,14 @@ module Exploit::Remote::WinRM
|
||||||
def winrm_get_cmd_streams(response)
|
def winrm_get_cmd_streams(response)
|
||||||
streams = {
|
streams = {
|
||||||
'stdout' => '',
|
'stdout' => '',
|
||||||
'stderr' => '',
|
'stderr' => '',
|
||||||
}
|
}
|
||||||
|
|
||||||
xml = response.body
|
xml = response.body
|
||||||
rxml = REXML::Document.new(xml).root
|
rxml = REXML::Document.new(xml).root
|
||||||
|
|
||||||
rxml.elements.to_a("//rsp:Stream").each do |node|
|
rxml.elements.to_a("//rsp:Stream").each do |node|
|
||||||
next if node.text.nil?
|
next if node.text.nil?
|
||||||
streams[node.attributes['Name']] << Rex::Text.base64_decode(node.text)
|
streams[node.attributes['Name']] << Rex::Text.base64_decode(node.text)
|
||||||
end
|
end
|
||||||
|
|
||||||
return streams
|
return streams
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -222,25 +194,20 @@ module Exploit::Remote::WinRM
|
||||||
'username' => datastore['USERNAME'],
|
'username' => datastore['USERNAME'],
|
||||||
'password' => datastore['PASSWORD']
|
'password' => datastore['PASSWORD']
|
||||||
}
|
}
|
||||||
|
ntlm_options = {
|
||||||
ntlm_options =
|
:signing => false,
|
||||||
{
|
:usentlm2_session => datastore['NTLM::UseNTLM2_session'],
|
||||||
:signing => false,
|
:use_ntlmv2 => datastore['NTLM::UseNTLMv2'],
|
||||||
:usentlm2_session => datastore['NTLM::UseNTLM2_session'],
|
:send_lm => datastore['NTLM::SendLM'],
|
||||||
:use_ntlmv2 => datastore['NTLM::UseNTLMv2'],
|
:send_ntlm => datastore['NTLM::SendNTLM']
|
||||||
:send_lm => datastore['NTLM::SendLM'],
|
}
|
||||||
:send_ntlm => datastore['NTLM::SendNTLM']
|
|
||||||
}
|
|
||||||
|
|
||||||
ntlmssp_flags = NTLM_UTILS.make_ntlm_flags(ntlm_options)
|
ntlmssp_flags = NTLM_UTILS.make_ntlm_flags(ntlm_options)
|
||||||
workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
|
workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
|
||||||
domain_name = datastore['DOMAIN']
|
domain_name = datastore['DOMAIN']
|
||||||
ntlm_message_1 = "NEGOTIATE " + Rex::Text::encode_base64(NTLM_UTILS::make_ntlmssp_blob_init( domain_name,
|
ntlm_message_1 = "NEGOTIATE " + Rex::Text::encode_base64(NTLM_UTILS::make_ntlmssp_blob_init( domain_name,
|
||||||
workstation_name,
|
workstation_name,
|
||||||
ntlmssp_flags))
|
ntlmssp_flags))
|
||||||
|
|
||||||
to = opts[:timeout] || timeout
|
to = opts[:timeout] || timeout
|
||||||
|
|
||||||
begin
|
begin
|
||||||
c = connect(opts)
|
c = connect(opts)
|
||||||
ctype = "application/soap+xml;charset=UTF-8"
|
ctype = "application/soap+xml;charset=UTF-8"
|
||||||
|
@ -251,14 +218,11 @@ module Exploit::Remote::WinRM
|
||||||
'ctype' => ctype,
|
'ctype' => ctype,
|
||||||
'headers' => { 'Authorization' => ntlm_message_1},
|
'headers' => { 'Authorization' => ntlm_message_1},
|
||||||
'data' => opts['data']
|
'data' => opts['data']
|
||||||
}))
|
}))
|
||||||
|
|
||||||
resp = c.send_recv(r, to)
|
resp = c.send_recv(r, to)
|
||||||
|
|
||||||
unless resp.kind_of? Rex::Proto::Http::Response
|
unless resp.kind_of? Rex::Proto::Http::Response
|
||||||
return [nil,nil]
|
return [nil,nil]
|
||||||
end
|
end
|
||||||
|
|
||||||
return [nil,nil] if resp.code == 404
|
return [nil,nil] if resp.code == 404
|
||||||
return [nil,nil] unless resp.code == 401 && resp.headers['WWW-Authenticate']
|
return [nil,nil] unless resp.code == 401 && resp.headers['WWW-Authenticate']
|
||||||
# Get the challenge and craft the response
|
# Get the challenge and craft the response
|
||||||
|
@ -293,7 +257,6 @@ module Exploit::Remote::WinRM
|
||||||
ntlm_message_3 = NTLM_UTILS.make_ntlmssp_blob_auth(domain_name, workstation_name, opts['username'],
|
ntlm_message_3 = NTLM_UTILS.make_ntlmssp_blob_auth(domain_name, workstation_name, opts['username'],
|
||||||
resp_lm, resp_ntlm, '', ntlmssp_flags)
|
resp_lm, resp_ntlm, '', ntlmssp_flags)
|
||||||
ntlm_message_3 = Rex::Text::encode_base64(ntlm_message_3)
|
ntlm_message_3 = Rex::Text::encode_base64(ntlm_message_3)
|
||||||
|
|
||||||
# Send the response
|
# Send the response
|
||||||
r = c.request_cgi(opts.merge({
|
r = c.request_cgi(opts.merge({
|
||||||
'uri' => opts['uri'],
|
'uri' => opts['uri'],
|
||||||
|
@ -302,13 +265,10 @@ module Exploit::Remote::WinRM
|
||||||
'headers' => { 'Authorization' => "NEGOTIATE #{ntlm_message_3}"},
|
'headers' => { 'Authorization' => "NEGOTIATE #{ntlm_message_3}"},
|
||||||
'data' => opts['data']
|
'data' => opts['data']
|
||||||
}))
|
}))
|
||||||
|
|
||||||
resp = c.send_recv(r, to, true)
|
resp = c.send_recv(r, to, true)
|
||||||
|
|
||||||
unless resp.kind_of? Rex::Proto::Http::Response
|
unless resp.kind_of? Rex::Proto::Http::Response
|
||||||
return [nil,nil]
|
return [nil,nil]
|
||||||
end
|
end
|
||||||
|
|
||||||
return [nil,nil] if resp.code == 404
|
return [nil,nil] if resp.code == 404
|
||||||
return [resp,c]
|
return [resp,c]
|
||||||
rescue ::Errno::EPIPE, ::Timeout::Error
|
rescue ::Errno::EPIPE, ::Timeout::Error
|
||||||
|
@ -324,7 +284,6 @@ module Exploit::Remote::WinRM
|
||||||
if rport == 5986 or datastore['SSL']
|
if rport == 5986 or datastore['SSL']
|
||||||
proto = "https"
|
proto = "https"
|
||||||
end
|
end
|
||||||
|
|
||||||
if datastore['VHOST']
|
if datastore['VHOST']
|
||||||
return "#{proto}://#{datastore ['VHOST']}:#{rport}#{@uri.to_s}"
|
return "#{proto}://#{datastore ['VHOST']}:#{rport}#{@uri.to_s}"
|
||||||
else
|
else
|
||||||
|
@ -332,17 +291,13 @@ module Exploit::Remote::WinRM
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def winrm_option_set(options)
|
def winrm_option_set(options)
|
||||||
xml = "<w:OptionSet>"
|
xml = "<w:OptionSet>"
|
||||||
|
|
||||||
options.each do |option_pair|
|
options.each do |option_pair|
|
||||||
xml << winrm_option(*option_pair)
|
xml << winrm_option(*option_pair)
|
||||||
end
|
end
|
||||||
|
|
||||||
xml << "</w:OptionSet>"
|
xml << "</w:OptionSet>"
|
||||||
return xml
|
return xml
|
||||||
end
|
end
|
||||||
|
@ -353,11 +308,9 @@ module Exploit::Remote::WinRM
|
||||||
|
|
||||||
def winrm_selector_set(selectors)
|
def winrm_selector_set(selectors)
|
||||||
xml = "<w:SelectorSet>"
|
xml = "<w:SelectorSet>"
|
||||||
|
|
||||||
selectors.each do |selector_pair|
|
selectors.each do |selector_pair|
|
||||||
xml << winrm_selector(*selector_pair)
|
xml << winrm_selector(*selector_pair)
|
||||||
end
|
end
|
||||||
|
|
||||||
xml << "</w:SelectorSet>"
|
xml << "</w:SelectorSet>"
|
||||||
return xml
|
return xml
|
||||||
end
|
end
|
||||||
|
|
|
@ -52,9 +52,9 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
:name => 'winrm',
|
:name => 'winrm',
|
||||||
:info => desc
|
:info => desc
|
||||||
)
|
)
|
||||||
print_good "Negotiate protocol supported" if methods.include? "Negotiate"
|
print_good "#{ip}:#{rport}: Negotiate protocol supported" if methods.include? "Negotiate"
|
||||||
print_good "Kerberos protocol supported" if methods.include? "Kerberos"
|
print_good "#{ip}:#{rport}: Kerberos protocol supported" if methods.include? "Kerberos"
|
||||||
print_good "Basic protocol supported" if methods.include? "Basic"
|
print_good "#{ip}:#{rport}: Basic protocol supported" if methods.include? "Basic"
|
||||||
else
|
else
|
||||||
print_error "#{ip}:#{rport} Does not appear to be a WinRM server"
|
print_error "#{ip}:#{rport} Does not appear to be a WinRM server"
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
##
|
||||||
|
# $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::AuthBrute
|
||||||
|
|
||||||
|
include Msf::Auxiliary::Scanner
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
super(
|
||||||
|
'Name' => 'WinRM Login Utility',
|
||||||
|
'Version' => '$Revision$',
|
||||||
|
'Description' => %q{
|
||||||
|
This module attempts to authenticate to a WinRM service. It currently
|
||||||
|
works only if the remote end allows Negotiate(NTLM) authentication.
|
||||||
|
Kerberos is not currently supported.
|
||||||
|
},
|
||||||
|
'Author' => [ 'thelightcosine' ],
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
[ 'CVE', '1999-0502'] # Weak password
|
||||||
|
],
|
||||||
|
'License' => MSF_LICENSE
|
||||||
|
)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def run_host(ip)
|
||||||
|
unless accepts_ntlm_auth
|
||||||
|
print_error "The Remote WinRM server (#{ip} does not appear to allow Negotiate(NTLM) auth"
|
||||||
|
return
|
||||||
|
end
|
||||||
|
each_user_pass do |user, pass|
|
||||||
|
resp,c = send_request_ntlm(test_request)
|
||||||
|
if resp.nil?
|
||||||
|
print_error "#{ip}:#{rport}: Got no reply from the server, connection may have timed out"
|
||||||
|
return
|
||||||
|
elsif resp.code == 200
|
||||||
|
cred_hash = {
|
||||||
|
:host => ip,
|
||||||
|
:port => rport,
|
||||||
|
:sname => 'winrm',
|
||||||
|
:pass => pass,
|
||||||
|
:user => user,
|
||||||
|
:source_type => "user_supplied",
|
||||||
|
:active => true
|
||||||
|
}
|
||||||
|
report_auth_info(cred_hash)
|
||||||
|
print_good "#{ip}:#{rport}: Valid credential found: #{user}:#{pass}"
|
||||||
|
elsif resp.code == 401
|
||||||
|
print_error "#{ip}:#{rport}: Login failed: #{user}:#{pass}"
|
||||||
|
else
|
||||||
|
print_error "Recieved unexpected Response Code: #{resp.code}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def test_request
|
||||||
|
data = winrm_wql_msg("Select Name,Status from Win32_Service")
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in New Issue