metasploit-framework/lib/rex/proto/ntlm/utils.rb

359 lines
9.5 KiB
Ruby
Raw Normal View History

module Rex
module Proto
module NTLM
class Utils
#duplicate from lib/rex/proto/smb/utils cause we only need this fonction from Rex::Proto::SMB::Utils
# Convert a unix timestamp to a 64-bit signed server time
def self.time_unix_to_smb(unix_time)
t64 = (unix_time + 11644473600) * 10000000
thi = (t64 & 0xffffffff00000000) >> 32
tlo = (t64 & 0x00000000ffffffff)
return [thi, tlo]
end
#
# Prepends an ASN1 formatted length field to a piece of data
#
def self.asn1encode(str = '')
res = ''
# If the high bit of the first byte is 1, it contains the number of
# length bytes that follow
case str.length
when 0 .. 0x7F
res = [str.length].pack('C') + str
when 0x80 .. 0xFF
res = [0x81, str.length].pack('CC') + str
when 0x100 .. 0xFFFF
res = [0x82, str.length].pack('Cn') + str
when 0x10000 .. 0xffffff
res = [0x83, str.length >> 16, str.length & 0xFFFF].pack('CCn') + str
when 0x1000000 .. 0xffffffff
res = [0x84, str.length].pack('CN') + str
else
raise "ASN1 str too long"
end
return res
end
# GSS functions
# GSS BLOB usefull for SMB_NEGOCIATE_RESPONSE message
# 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
# GSS BLOB usefull for SMB_NEGOCIATE_RESPONSE message
# 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(
"\x06" + self.asn1encode(
"\x2b\x06\x01\x05\x05\x02"
) +
"\xa0" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa0" + self.asn1encode(
"\x30" + self.asn1encode(
"\x06" + self.asn1encode(
"\x2a\x86\x48\x82\xf7\x12\x01\x02\x02"
) +
"\x06" + self.asn1encode(
"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"
) +
"\x06" + self.asn1encode(
"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x03"
) +
"\x06" + self.asn1encode(
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
)
)
) +
"\xa3" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa0" + self.asn1encode(
"\x1b" + self.asn1encode(
account + '@' + domain
)
)
)
)
)
)
)
return blob
end
# GSS BLOB usefull for ntlmssp type 1 message
def self.make_ntlmssp_secblob_init(domain = 'WORKGROUP', name = 'WORKSTATION', flags=0x80201)
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"
)
)
) +
"\xa2" + self.asn1encode(
"\x04" + self.asn1encode(
"NTLMSSP\x00" +
[1, flags].pack('VV') +
[
domain.length, #length
domain.length, #max length
32
].pack('vvV') +
[
name.length, #length
name.length, #max length
domain.length + 32
].pack('vvV') +
domain + name
)
)
)
)
)
return blob
end
# GSS BLOB usefull for ntlmssp type 2 message
def self.make_ntlmssp_secblob_chall(win_domain, win_name, dns_domain, dns_name, chall, flags)
blob =
"\xa1" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa0" + self.asn1encode(
"\x0a" + self.asn1encode(
"\x01"
)
) +
"\xa1" + self.asn1encode(
"\x06" + self.asn1encode(
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
)
) +
"\xa2" + self.asn1encode(
"\x04" + self.asn1encode(
make_ntlmssp_blob_chall(win_domain, win_name, dns_domain, dns_name, chall, flags)
)
)
)
)
return blob
end
# BLOB without GSS usefull for ntlm type 2 message
def self.make_ntlmssp_blob_chall(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
# GSS BLOB Usefull for ntlmssp type 3 message
def self.make_ntlmssp_secblob_auth(domain, name, user, lm, ntlm, enc_session_key, flags = 0x080201)
lm ||= "\x00" * 24
ntlm ||= "\x00" * 24
domain_uni = Rex::Text.to_unicode(domain)
user_uni = Rex::Text.to_unicode(user)
name_uni = Rex::Text.to_unicode(name)
session = enc_session_key
ptr = 64
blob =
"\xa1" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa2" + self.asn1encode(
"\x04" + self.asn1encode(
"NTLMSSP\x00" +
[ 3 ].pack('V') +
[ # Lan Manager Response
lm.length,
lm.length,
(ptr)
].pack('vvV') +
[ # NTLM Manager Response
ntlm.length,
ntlm.length,
(ptr += lm.length)
].pack('vvV') +
[ # Domain Name
domain_uni.length,
domain_uni.length,
(ptr += ntlm.length)
].pack('vvV') +
[ # Username
user_uni.length,
user_uni.length,
(ptr += domain_uni.length)
].pack('vvV') +
[ # Hostname
name_uni.length,
name_uni.length,
(ptr += user_uni.length)
].pack('vvV') +
[ # Session Key (none)
session.length,
session.length,
(ptr += name_uni.length)
].pack('vvV') +
[ flags ].pack('V') +
lm +
ntlm +
domain_uni +
user_uni +
name_uni +
session + "\x00"
)
)
)
)
return blob
end
# GSS BLOB Usefull for SMB Success
def self.make_ntlmv2_secblob_success
blob =
"\xa1" + self.asn1encode(
"\x30" + self.asn1encode(
"\xa0" + self.asn1encode(
"\x0a" + self.asn1encode(
"\x00"
)
)
)
)
return blob
end
# This function return an ntlmv2 client challenge
def self.make_ntlmv2_clientchallenge(win_domain, win_name, dns_domain, dns_name, client_challenge = nil, chall_MsvAvTimestamp = nil)
client_challenge ||= Rex::Text.rand_text(8)
# We have to set the timestamps here to the one in the challenge message from server if present
# If we don't do that, recent server like seven will send a STATUS_INVALID_PARAMETER error packet
timestamp = chall_MsvAvTimestamp != nil ? chall_MsvAvTimestamp : self.time_unix_to_smb(Time.now.to_i).reverse.pack("VV")
# Make those values unicode as requested
win_domain = Rex::Text.to_unicode(win_domain)
win_name = Rex::Text.to_unicode(win_name)
dns_domain = Rex::Text.to_unicode(dns_domain)
dns_name = Rex::Text.to_unicode(dns_name)
# Make the AV_PAIRs
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 << [7, 8].pack('vv') + timestamp
# MAY BE USEFUL FOR FUTURE
# Seven (client) add at least one more av that is of type MsAvRestrictions (8)
# maybe this will be usefull with future windows OSs but has no use at all for the moment afaik
# restriction_encoding = [48,0,0,0].pack("VVV") + # Size, Z4, IntegrityLevel, SubjectIntegrityLevel
# Rex::Text.rand_text(32) # MachineId generated on startup on win7 and above
# addr_list << [8, restriction_encoding.length].pack('vv') + restriction_encoding
# Seven (client) and maybe others versions also add an av of type MsvChannelBindings (10) but the hash is "\x00" * 16
# addr_list << [10, 16].pack('vv') + "\x00" * 16
# Seven and maybe other versions also add an av of type MsvAvTargetName(9) with value cifs/target(_ip)
# implementing it will necessary require knowing the target here, todo... :-/
# spn= Rex::Text.to_unicode("cifs/RHOST")
# addr_list << [9, spn.length].pack('vv') + spn
addr_list << [0, 0].pack('vv')
ntlm_clientchallenge = [1,1,0,0].pack("CCvV") + #RespType, HiRespType, Reserved1, Reserved2
timestamp + #Timestamp
client_challenge + #clientchallenge
[0].pack("V") + #Reserved3
addr_list + "\x00" * 4
end
end
end
end
end