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
2023-01-20 11:31:24 +00:00
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 (
[
2023-01-20 11:31:24 +00:00
* 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' ]
2023-01-20 11:31:24 +00:00
when Msf :: Exploit :: Remote :: AuthOption :: KERBEROS
2022-11-30 10:26:19 +11:00
kerberos_authenticator = Msf :: Exploit :: Remote :: Kerberos :: ServiceAuthenticator :: HTTP . new (
2023-10-11 13:53:08 -04:00
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