Files
metasploit-gs/lib/msf/core/exploit/remote/winrm.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

176 lines
6.3 KiB
Ruby
Raw Normal View History

2012-10-19 15:08:58 -05:00
# -*- coding: binary -*-
require 'uri'
require 'digest'
2022-11-30 10:26:19 +11:00
require 'net/winrm/connection'
2012-10-23 16:32:26 -05:00
2012-10-19 15:08:58 -05:00
module Msf
2012-10-24 13:50:44 -05:00
module Exploit::Remote::WinRM
include Exploit::Remote::NTLM::Client
include Exploit::Remote::HttpClient
include Msf::Exploit::Remote::Kerberos::Ticket::Storage
include Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Options
2012-10-24 13:50:44 -05:00
#
# 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
2013-08-30 16:28:33 -05:00
2012-10-24 13:50:44 -05:00
def initialize(info = {})
super
register_options(
[
Opt::RPORT(5985),
2022-07-28 13:16:56 -04:00
OptString.new('DOMAIN', [ true, 'The domain to use for Windows authentication', 'WORKSTATION']),
2012-10-24 13:50:44 -05:00
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
)
2013-08-30 16:28:33 -05:00
2022-11-30 10:26:19 +11:00
register_advanced_options(
[
*kerberos_storage_options(protocol: 'Winrm'),
*kerberos_auth_options(protocol: 'Winrm', auth_methods: Msf::Exploit::Remote::AuthOption::WINRM_OPTIONS),
],
2022-11-30 10:26:19 +11:00
)
2012-10-24 13:50:44 -05:00
register_autofilter_ports([ 80,443,5985,5986 ])
register_autofilter_services(%W{ winrm })
end
2013-08-30 16:28:33 -05:00
2023-01-03 11:26:07 +11:00
def check_winrm_parameters
2023-01-24 14:30:39 +00:00
if datastore['Winrm::Auth'] == Msf::Exploit::Remote::AuthOption::KERBEROS
fail_with(Msf::Exploit::Failure::BadConfig, 'The Winrm::Rhostname option is required when using Kerberos authentication.') if datastore['Winrm::Rhostname'].blank?
2023-01-03 11:26:07 +11:00
fail_with(Msf::Exploit::Failure::BadConfig, 'The DOMAIN option is required when using Kerberos authentication.') if datastore['DOMAIN'].blank?
2023-01-24 14:30:39 +00:00
offered_etypes = Msf::Exploit::Remote::AuthOption.as_default_offered_etypes(datastore['Winrm::KrbOfferedEncryptionTypes'])
2023-01-09 17:15:52 -05:00
fail_with(Msf::Exploit::Failure::BadConfig, 'At least one encryption type is required when using Kerberos authentication.') if offered_etypes.empty?
2023-01-03 11:26:07 +11:00
else
fail_with(Msf::Exploit::Failure::BadConfig, 'The PASSWORD option is required unless using Kerberos authentication.') if datastore['PASSWORD'].blank?
end
end
2022-11-30 11:53:57 +11:00
# Sets up a connection to a WinRM server, based on the datastore parameters
# May use NTLM or Kerberos auth, depending on the params
# @return [Net::MsfWinRM::RexWinRMConnection] Connection to the WinRM server
2022-11-30 10:26:19 +11:00
def create_winrm_connection
rhost = datastore['RHOST']
rport = datastore['RPORT']
uri = datastore['URI']
ssl = datastore['SSL']
schema = ssl ? 'https' : 'http'
2023-01-03 11:26:07 +11:00
endpoint = URI.join("#{schema}://#{rhost}:#{rport}", uri)
2022-11-30 10:26:19 +11:00
opts = {
endpoint: endpoint,
host: rhost,
port: rport,
2023-06-14 00:40:33 +01:00
proxies: datastore['Proxies'],
2022-11-30 10:26:19 +11:00
uri: uri,
ssl: ssl,
transport: :rexhttp,
no_ssl_peer_verification: true,
operation_timeout: 1,
timeout: 20,
retry_limit: 1,
realm: datastore['DOMAIN']
}
2023-01-24 14:30:39 +00:00
case datastore['Winrm::Auth']
when Msf::Exploit::Remote::AuthOption::KERBEROS
2022-11-30 10:26:19 +11:00
kerberos_authenticator = Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::HTTP.new(
host: datastore['DomainControllerRhost'].blank? ? nil : datastore['DomainControllerRhost'],
2023-01-24 14:30:39 +00:00
hostname: datastore['Winrm::Rhostname'],
2023-06-14 00:40:33 +01:00
proxies: datastore['Proxies'],
2022-11-30 10:26:19 +11:00
realm: datastore['DOMAIN'],
username: datastore['USERNAME'],
password: datastore['PASSWORD'],
timeout: 20, # datastore['timeout']
framework: framework,
framework_module: self,
2023-01-24 14:30:39 +00:00
cache_file: datastore['Winrm::Krb5Ccname'].blank? ? nil : datastore['Winrm::Krb5Ccname'],
offered_etypes: Msf::Exploit::Remote::AuthOption.as_default_offered_etypes(datastore['Winrm::KrbOfferedEncryptionTypes']),
2022-11-30 10:26:19 +11:00
mutual_auth: true,
use_gss_checksum: true
)
opts = opts.merge({
user: '', # Need to provide it, otherwise the WinRM module complains
password: '', # Need to provide it, otherwise the WinRM module complains
kerberos_authenticator: kerberos_authenticator,
vhost: datastore['RHOSTNAME']
})
else
opts = opts.merge({
user: datastore['USERNAME'],
password: datastore['PASSWORD']
})
end
return Net::MsfWinRM::RexWinRMConnection.new(opts)
end
2022-11-30 11:53:57 +11:00
# Make an unauthenticated request to the WinRM server
# @param [Integer] timeout Timeout for the request in seconds
# @return [Rex::Proto::Http::Response] The HTTP response from an unauthenticated request
2022-11-30 11:31:27 +11:00
def make_unauthenticated_request(timeout = 20)
opts = {
'uri' => datastore['URI'],
'method' => 'POST',
'data' => Rex::Text.rand_text_alpha(8),
'ctype' => "application/soap+xml;charset=UTF-8"
}
send_request_cgi(opts,timeout)
2013-03-26 13:05:33 -05:00
end
2013-08-30 16:28:33 -05:00
2022-11-30 11:53:57 +11:00
# Parse the available auth methods from a WinRM response
# @param [Rex::Proto::Http::Response] resp The HTTP response from the WinRM server
# @return [Array<String>] The auth methods parsed from the HTTP response
2012-10-24 13:50:44 -05:00
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
2013-08-30 16:28:33 -05:00
2022-11-30 11:53:57 +11:00
# Parse out the results from a WQL query
# @param [Hash] The response from a call to Connection.run_wql
# @return [Rex::Text::Table] The results from the WQL query in table form
2022-11-30 10:26:19 +11:00
def parse_wql_hash(response)
columns = []
rows = []
fragments = response[:xml_fragment]
fragments.each do |fragment|
row_data = []
fragment.keys.each do |key|
unless key.starts_with?('@') # xmlns stuff
columns << key.to_s
row_data << fragment[key]
end
end
rows << row_data
end
columns.uniq!
response_data = Rex::Text::Table.new(
'Header' => "#{datastore['WQL']} (#{rhost})",
'Indent' => 1,
'Columns' => columns
)
rows.each do |row|
response_data << row
end
return response_data
end
2022-11-30 11:53:57 +11:00
# The namespace for WQL queries
# @return [String] The WMI namespace
2012-11-07 11:26:48 -06:00
def wmi_namespace
return datastore['NAMESPACE'] if datastore['NAMESPACE']
return @namespace_override if @namespace_override
2022-11-30 11:31:27 +11:00
return "root/cimv2"
2012-10-24 13:50:44 -05:00
end
end
2012-10-19 15:08:58 -05:00
end