lib/rex/proto/ntlm/utils.rb in librex-0.0.68 vs lib/rex/proto/ntlm/utils.rb in librex-0.0.70

- old
+ new

@@ -6,757 +6,757 @@ module Rex module Proto module NTLM class Utils - CONST = Rex::Proto::NTLM::Constants - CRYPT = Rex::Proto::NTLM::Crypt - XCEPT = Rex::Proto::NTLM::Exceptions + CONST = Rex::Proto::NTLM::Constants + CRYPT = Rex::Proto::NTLM::Crypt + XCEPT = Rex::Proto::NTLM::Exceptions #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 + # 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 - # Determine whether the password is a known hash format - def self.is_pass_ntlm_hash?(str) - str.downcase =~ /^[0-9a-f]{32}:[0-9a-f]{32}$/ - end + # Determine whether the password is a known hash format + def self.is_pass_ntlm_hash?(str) + str.downcase =~ /^[0-9a-f]{32}:[0-9a-f]{32}$/ + end - # - # Prepends an ASN1 formatted length field to a piece of data - # - def self.asn1encode(str = '') - res = '' + # + # 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 + # 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 + 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 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" - ) - ) - ) - ) - ) - ) + # 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 + 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 - ) - ) - ) - ) - ) - ) - ) + # 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 + return blob + end - # BLOB without GSS usefull for ntlmssp type 1 message - def self.make_ntlmssp_blob_init(domain = 'WORKGROUP', name = 'WORKSTATION', flags=0x80201) - blob = "NTLMSSP\x00" + - [1, flags].pack('VV') + + # BLOB without GSS usefull for ntlmssp type 1 message + def self.make_ntlmssp_blob_init(domain = 'WORKGROUP', name = 'WORKSTATION', flags=0x80201) + blob = "NTLMSSP\x00" + + [1, flags].pack('VV') + - [ - domain.length, #length - domain.length, #max length - 32 - ].pack('vvV') + + [ + domain.length, #length + domain.length, #max length + 32 + ].pack('vvV') + - [ - name.length, #length - name.length, #max length - domain.length + 32 - ].pack('vvV') + + [ + name.length, #length + name.length, #max length + domain.length + 32 + ].pack('vvV') + - domain + name - return blob - end + domain + name + 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( - make_ntlmssp_blob_init(domain, name, flags) - ) - ) - ) - ) - ) + # 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( + make_ntlmssp_blob_init(domain, name, flags) + ) + ) + ) + ) + ) - return blob - end + 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) + # 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') + 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 + 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 2 message - def self.make_ntlmssp_secblob_chall(win_domain, win_name, dns_domain, dns_name, chall, flags) + # 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) - ) - ) - ) - ) + 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 + return blob + end - # BLOB without GSS Usefull for ntlmssp type 3 message - def self.make_ntlmssp_blob_auth(domain, name, user, lm, ntlm, enc_session_key, flags = 0x080201) - lm ||= "\x00" * 24 - ntlm ||= "\x00" * 24 + # BLOB without GSS Usefull for ntlmssp type 3 message + def self.make_ntlmssp_blob_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 + 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 + ptr = 64 - blob = "NTLMSSP\x00" + - [ 3 ].pack('V') + + blob = "NTLMSSP\x00" + + [ 3 ].pack('V') + - [ # Lan Manager Response - lm.length, - lm.length, - (ptr) - ].pack('vvV') + + [ # Lan Manager Response + lm.length, + lm.length, + (ptr) + ].pack('vvV') + - [ # NTLM Manager Response - ntlm.length, - ntlm.length, - (ptr += lm.length) - ].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') + + [ # 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') + + [ # 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') + + [ # 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') + + [ # Session Key (none) + session.length, + session.length, + (ptr += name_uni.length) + ].pack('vvV') + - [ flags ].pack('V') + + [ flags ].pack('V') + - lm + - ntlm + - domain_uni + - user_uni + - name_uni + - session + "\x00" - return blob + lm + + ntlm + + domain_uni + + user_uni + + name_uni + + session + "\x00" + return blob - end + 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) + # GSS BLOB Usefull for ntlmssp type 3 message + def self.make_ntlmssp_secblob_auth(domain, name, user, lm, ntlm, enc_session_key, flags = 0x080201) - blob = - "\xa1" + self.asn1encode( - "\x30" + self.asn1encode( - "\xa2" + self.asn1encode( - "\x04" + self.asn1encode( - make_ntlmssp_blob_auth(domain, name, user, lm, ntlm, enc_session_key, flags ) - ) - ) - ) - ) - return blob - end + blob = + "\xa1" + self.asn1encode( + "\x30" + self.asn1encode( + "\xa2" + self.asn1encode( + "\x04" + self.asn1encode( + make_ntlmssp_blob_auth(domain, name, user, lm, ntlm, enc_session_key, flags ) + ) + ) + ) + ) + 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 + # 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 - # Return the correct ntlmflags upon the configuration - def self.make_ntlm_flags(opt = {}) + # Return the correct ntlmflags upon the configuration + def self.make_ntlm_flags(opt = {}) - signing = opt[:signing] != nil ? opt[:signing] : false - usentlm2_session = opt[:usentlm2_session] != nil ? opt[:usentlm2_session] : true - use_ntlmv2 = opt[:use_ntlmv2] != nil ? opt[:use_ntlmv2] : false - send_lm = opt[:send_lm] != nil ? opt[:send_lm] : true - send_ntlm = opt[:send_ntlm] != nil ? opt[:send_ntlm] : true - use_lanman_key = opt[:use_lanman_key] != nil ? opt[:use_lanman_key] : false + signing = opt[:signing] != nil ? opt[:signing] : false + usentlm2_session = opt[:usentlm2_session] != nil ? opt[:usentlm2_session] : true + use_ntlmv2 = opt[:use_ntlmv2] != nil ? opt[:use_ntlmv2] : false + send_lm = opt[:send_lm] != nil ? opt[:send_lm] : true + send_ntlm = opt[:send_ntlm] != nil ? opt[:send_ntlm] : true + use_lanman_key = opt[:use_lanman_key] != nil ? opt[:use_lanman_key] : false - if signing - ntlmssp_flags = 0xe2088215 - else + if signing + ntlmssp_flags = 0xe2088215 + else - ntlmssp_flags = 0xa2080205 - end + ntlmssp_flags = 0xa2080205 + end - if usentlm2_session - if use_ntlmv2 - #set Negotiate Target Info - ntlmssp_flags |= CONST::NEGOTIATE_TARGET_INFO - end + if usentlm2_session + if use_ntlmv2 + #set Negotiate Target Info + ntlmssp_flags |= CONST::NEGOTIATE_TARGET_INFO + end - else - #remove the ntlm2_session flag - ntlmssp_flags &= 0xfff7ffff - #set lanmanflag only when lm and ntlm are sent - if send_lm - ntlmssp_flags |= CONST::NEGOTIATE_LMKEY if use_lanman_key - end - end + else + #remove the ntlm2_session flag + ntlmssp_flags &= 0xfff7ffff + #set lanmanflag only when lm and ntlm are sent + if send_lm + ntlmssp_flags |= CONST::NEGOTIATE_LMKEY if use_lanman_key + end + end - #we can also downgrade ntlm2_session when we send only lmv1 - ntlmssp_flags &= 0xfff7ffff if usentlm2_session && (not use_ntlmv2) && (not send_ntlm) + #we can also downgrade ntlm2_session when we send only lmv1 + ntlmssp_flags &= 0xfff7ffff if usentlm2_session && (not use_ntlmv2) && (not send_ntlm) - return ntlmssp_flags - end + return ntlmssp_flags + end - # Parse an ntlm type 2 challenge blob and return usefull data - def self.parse_ntlm_type_2_blob(blob) - data = {} - # Extract the NTLM challenge key the lazy way - cidx = blob.index("NTLMSSP\x00\x02\x00\x00\x00") + # Parse an ntlm type 2 challenge blob and return usefull data + def self.parse_ntlm_type_2_blob(blob) + data = {} + # Extract the NTLM challenge key the lazy way + cidx = blob.index("NTLMSSP\x00\x02\x00\x00\x00") - if not cidx - raise XCEPT::NTLMMissingChallenge - end + if not cidx + raise XCEPT::NTLMMissingChallenge + end - data[:challenge_key] = blob[cidx + 24, 8] + data[:challenge_key] = blob[cidx + 24, 8] - data[:server_ntlmssp_flags] = blob[cidx + 20, 4].unpack("V")[0] + data[:server_ntlmssp_flags] = blob[cidx + 20, 4].unpack("V")[0] - # Extract the address list from the blob - alist_len,alist_mlen,alist_off = blob[cidx + 40, 8].unpack("vvV") - alist_buf = blob[cidx + alist_off, alist_len] + # Extract the address list from the blob + alist_len,alist_mlen,alist_off = blob[cidx + 40, 8].unpack("vvV") + alist_buf = blob[cidx + alist_off, alist_len] - while(alist_buf.length > 0) - atype, alen = alist_buf.slice!(0,4).unpack('vv') - break if atype == 0x00 - addr = alist_buf.slice!(0, alen) - case atype - when 1 - #netbios name - data[:default_name] = addr.gsub("\x00", '') - when 2 - #netbios domain - data[:default_domain] = addr.gsub("\x00", '') - when 3 - #dns name - data[:dns_host_name] = addr.gsub("\x00", '') - when 4 - #dns domain - data[:dns_domain_name] = addr.gsub("\x00", '') - when 5 - #The FQDN of the forest. - when 6 - #A 32-bit value indicating server or client configuration - when 7 - #Client time - data[:chall_MsvAvTimestamp] = addr - when 8 - #A Restriction_Encoding structure - when 9 - #The SPN of the target server. - when 10 - #A channel bindings hash. - end - end - return data - end + while(alist_buf.length > 0) + atype, alen = alist_buf.slice!(0,4).unpack('vv') + break if atype == 0x00 + addr = alist_buf.slice!(0, alen) + case atype + when 1 + #netbios name + data[:default_name] = addr.gsub("\x00", '') + when 2 + #netbios domain + data[:default_domain] = addr.gsub("\x00", '') + when 3 + #dns name + data[:dns_host_name] = addr.gsub("\x00", '') + when 4 + #dns domain + data[:dns_domain_name] = addr.gsub("\x00", '') + when 5 + #The FQDN of the forest. + when 6 + #A 32-bit value indicating server or client configuration + when 7 + #Client time + data[:chall_MsvAvTimestamp] = addr + when 8 + #A Restriction_Encoding structure + when 9 + #The SPN of the target server. + when 10 + #A channel bindings hash. + end + end + return data + end - # This function return an ntlmv2 client challenge - # This is a partial implementation, full description is in [MS-NLMP].pdf around 3.1.5.2.1 :-/ - def self.make_ntlmv2_clientchallenge(win_domain, win_name, dns_domain, dns_name, - client_challenge = nil, chall_MsvAvTimestamp = nil, spnopt = {}) + # This function return an ntlmv2 client challenge + # This is a partial implementation, full description is in [MS-NLMP].pdf around 3.1.5.2.1 :-/ + def self.make_ntlmv2_clientchallenge(win_domain, win_name, dns_domain, dns_name, + client_challenge = nil, chall_MsvAvTimestamp = nil, spnopt = {}) - 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/2008 will send a STATUS_INVALID_PARAMETER error packet - timestamp = chall_MsvAvTimestamp != '' ? 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 + 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/2008 will send a STATUS_INVALID_PARAMETER error packet + timestamp = chall_MsvAvTimestamp != '' ? 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 - # Windows Seven / 2008r2 Request this type if in local security policies, - # Microsoft network server : Server SPN target name validation level is set to <Required from client> - # otherwise it send an STATUS_ACCESS_DENIED packet - if spnopt[:use_spn] - spn= Rex::Text.to_unicode("cifs/#{spnopt[:name] || 'unknow'}") - addr_list << [9, spn.length].pack('vv') + spn - end + # Windows Seven / 2008r2 Request this type if in local security policies, + # Microsoft network server : Server SPN target name validation level is set to <Required from client> + # otherwise it send an STATUS_ACCESS_DENIED packet + if spnopt[:use_spn] + spn= Rex::Text.to_unicode("cifs/#{spnopt[:name] || 'unknow'}") + addr_list << [9, spn.length].pack('vv') + spn + end - # 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 + # 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 (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 - 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 + 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 - # create lm/ntlm responses - def self.create_lm_ntlm_responses(user, pass, challenge_key, domain = '', default_name = '', default_domain = '', - dns_host_name = '', dns_domain_name = '', chall_MsvAvTimestamp = nil, spnopt = {}, opt = {} ) + # create lm/ntlm responses + def self.create_lm_ntlm_responses(user, pass, challenge_key, domain = '', default_name = '', default_domain = '', + dns_host_name = '', dns_domain_name = '', chall_MsvAvTimestamp = nil, spnopt = {}, opt = {} ) - usentlm2_session = opt[:usentlm2_session] != nil ? opt[:usentlm2_session] : true - use_ntlmv2 = opt[:use_ntlmv2] != nil ? opt[:use_ntlmv2] : false - send_lm = opt[:send_lm] != nil ? opt[:send_lm] : true - send_ntlm = opt[:send_ntlm] != nil ? opt[:send_ntlm] : true + usentlm2_session = opt[:usentlm2_session] != nil ? opt[:usentlm2_session] : true + use_ntlmv2 = opt[:use_ntlmv2] != nil ? opt[:use_ntlmv2] : false + send_lm = opt[:send_lm] != nil ? opt[:send_lm] : true + send_ntlm = opt[:send_ntlm] != nil ? opt[:send_ntlm] : true - #calculate the lm/ntlm response - resp_lm = "\x00" * 24 - resp_ntlm = "\x00" * 24 + #calculate the lm/ntlm response + resp_lm = "\x00" * 24 + resp_ntlm = "\x00" * 24 - client_challenge = Rex::Text.rand_text(8) - ntlm_cli_challenge = '' - if send_ntlm #should be default - if usentlm2_session - if use_ntlmv2 - ntlm_cli_challenge = self.make_ntlmv2_clientchallenge(default_domain, default_name, dns_domain_name, - dns_host_name,client_challenge , - chall_MsvAvTimestamp, spnopt) - if self.is_pass_ntlm_hash?(pass) - argntlm = { - :ntlmv2_hash => CRYPT::ntlmv2_hash( - user, - [ pass.upcase()[33,65] ].pack('H32'), - domain,{:pass_is_hash => true} - ), - :challenge => challenge_key - } - else - argntlm = { - :ntlmv2_hash => CRYPT::ntlmv2_hash(user, pass, domain), - :challenge => challenge_key - } - end + client_challenge = Rex::Text.rand_text(8) + ntlm_cli_challenge = '' + if send_ntlm #should be default + if usentlm2_session + if use_ntlmv2 + ntlm_cli_challenge = self.make_ntlmv2_clientchallenge(default_domain, default_name, dns_domain_name, + dns_host_name,client_challenge , + chall_MsvAvTimestamp, spnopt) + if self.is_pass_ntlm_hash?(pass) + argntlm = { + :ntlmv2_hash => CRYPT::ntlmv2_hash( + user, + [ pass.upcase()[33,65] ].pack('H32'), + domain,{:pass_is_hash => true} + ), + :challenge => challenge_key + } + else + argntlm = { + :ntlmv2_hash => CRYPT::ntlmv2_hash(user, pass, domain), + :challenge => challenge_key + } + end - optntlm = { :nt_client_challenge => ntlm_cli_challenge} - ntlmv2_response = CRYPT::ntlmv2_response(argntlm,optntlm) - resp_ntlm = ntlmv2_response + optntlm = { :nt_client_challenge => ntlm_cli_challenge} + ntlmv2_response = CRYPT::ntlmv2_response(argntlm,optntlm) + resp_ntlm = ntlmv2_response - if send_lm - if self.is_pass_ntlm_hash?(pass) - arglm = { - :ntlmv2_hash => CRYPT::ntlmv2_hash( - user, - [ pass.upcase()[33,65] ].pack('H32'), - domain,{:pass_is_hash => true} - ), - :challenge => challenge_key - } - else - arglm = { - :ntlmv2_hash => CRYPT::ntlmv2_hash(user,pass, domain), - :challenge => challenge_key - } - end + if send_lm + if self.is_pass_ntlm_hash?(pass) + arglm = { + :ntlmv2_hash => CRYPT::ntlmv2_hash( + user, + [ pass.upcase()[33,65] ].pack('H32'), + domain,{:pass_is_hash => true} + ), + :challenge => challenge_key + } + else + arglm = { + :ntlmv2_hash => CRYPT::ntlmv2_hash(user,pass, domain), + :challenge => challenge_key + } + end - optlm = { :client_challenge => client_challenge } - resp_lm = CRYPT::lmv2_response(arglm, optlm) - else - resp_lm = "\x00" * 24 - end + optlm = { :client_challenge => client_challenge } + resp_lm = CRYPT::lmv2_response(arglm, optlm) + else + resp_lm = "\x00" * 24 + end - else # ntlm2_session - if self.is_pass_ntlm_hash?(pass) - argntlm = { - :ntlm_hash => [ pass.upcase()[33,65] ].pack('H32'), - :challenge => challenge_key - } - else - argntlm = { - :ntlm_hash => CRYPT::ntlm_hash(pass), - :challenge => challenge_key - } - end + else # ntlm2_session + if self.is_pass_ntlm_hash?(pass) + argntlm = { + :ntlm_hash => [ pass.upcase()[33,65] ].pack('H32'), + :challenge => challenge_key + } + else + argntlm = { + :ntlm_hash => CRYPT::ntlm_hash(pass), + :challenge => challenge_key + } + end - optntlm = { :client_challenge => client_challenge} - resp_ntlm = CRYPT::ntlm2_session(argntlm,optntlm).join[24,24] + optntlm = { :client_challenge => client_challenge} + resp_ntlm = CRYPT::ntlm2_session(argntlm,optntlm).join[24,24] - # Generate the fake LANMAN hash - resp_lm = client_challenge + ("\x00" * 16) - end + # Generate the fake LANMAN hash + resp_lm = client_challenge + ("\x00" * 16) + end - else # we use lmv1/ntlmv1 - if self.is_pass_ntlm_hash?(pass) - argntlm = { - :ntlm_hash => [ pass.upcase()[33,65] ].pack('H32'), - :challenge => challenge_key - } - else - argntlm = { - :ntlm_hash => CRYPT::ntlm_hash(pass), - :challenge => challenge_key - } - end + else # we use lmv1/ntlmv1 + if self.is_pass_ntlm_hash?(pass) + argntlm = { + :ntlm_hash => [ pass.upcase()[33,65] ].pack('H32'), + :challenge => challenge_key + } + else + argntlm = { + :ntlm_hash => CRYPT::ntlm_hash(pass), + :challenge => challenge_key + } + end - resp_ntlm = CRYPT::ntlm_response(argntlm) - if send_lm - if self.is_pass_ntlm_hash?(pass) - arglm = { - :lm_hash => [ pass.upcase()[0,32] ].pack('H32'), - :challenge => challenge_key - } - else - arglm = { - :lm_hash => CRYPT::lm_hash(pass), - :challenge => challenge_key - } - end - resp_lm = CRYPT::lm_response(arglm) - else - #when windows does not send lm in ntlmv1 type response, - # it gives lm response the same value as ntlm response - resp_lm = resp_ntlm - end - end - else #send_ntlm = false - #lmv2 - if usentlm2_session && use_ntlmv2 - if self.is_pass_ntlm_hash?(pass) - arglm = { - :ntlmv2_hash => CRYPT::ntlmv2_hash( - user, - [ pass.upcase()[33,65] ].pack('H32'), - domain,{:pass_is_hash => true} - ), - :challenge => challenge_key - } - else - arglm = { - :ntlmv2_hash => CRYPT::ntlmv2_hash(user,pass, domain), - :challenge => challenge_key - } - end - optlm = { :client_challenge => client_challenge } - resp_lm = CRYPT::lmv2_response(arglm, optlm) - else - if self.is_pass_ntlm_hash?(pass) - arglm = { - :lm_hash => [ pass.upcase()[0,32] ].pack('H32'), - :challenge => challenge_key - } - else - arglm = { - :lm_hash => CRYPT::lm_hash(pass), - :challenge => challenge_key - } - end - resp_lm = CRYPT::lm_response(arglm) - end - resp_ntlm = "" - end - return resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge - end + resp_ntlm = CRYPT::ntlm_response(argntlm) + if send_lm + if self.is_pass_ntlm_hash?(pass) + arglm = { + :lm_hash => [ pass.upcase()[0,32] ].pack('H32'), + :challenge => challenge_key + } + else + arglm = { + :lm_hash => CRYPT::lm_hash(pass), + :challenge => challenge_key + } + end + resp_lm = CRYPT::lm_response(arglm) + else + #when windows does not send lm in ntlmv1 type response, + # it gives lm response the same value as ntlm response + resp_lm = resp_ntlm + end + end + else #send_ntlm = false + #lmv2 + if usentlm2_session && use_ntlmv2 + if self.is_pass_ntlm_hash?(pass) + arglm = { + :ntlmv2_hash => CRYPT::ntlmv2_hash( + user, + [ pass.upcase()[33,65] ].pack('H32'), + domain,{:pass_is_hash => true} + ), + :challenge => challenge_key + } + else + arglm = { + :ntlmv2_hash => CRYPT::ntlmv2_hash(user,pass, domain), + :challenge => challenge_key + } + end + optlm = { :client_challenge => client_challenge } + resp_lm = CRYPT::lmv2_response(arglm, optlm) + else + if self.is_pass_ntlm_hash?(pass) + arglm = { + :lm_hash => [ pass.upcase()[0,32] ].pack('H32'), + :challenge => challenge_key + } + else + arglm = { + :lm_hash => CRYPT::lm_hash(pass), + :challenge => challenge_key + } + end + resp_lm = CRYPT::lm_response(arglm) + end + resp_ntlm = "" + end + return resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge + end - # create the session key - def self.create_session_key(ntlmssp_flags, server_ntlmssp_flags, user, pass, domain, challenge_key, - client_challenge = '', ntlm_cli_challenge = '' , opt = {} ) + # create the session key + def self.create_session_key(ntlmssp_flags, server_ntlmssp_flags, user, pass, domain, challenge_key, + client_challenge = '', ntlm_cli_challenge = '' , opt = {} ) - usentlm2_session = opt[:usentlm2_session] != nil ? opt[:usentlm2_session] : true - use_ntlmv2 = opt[:use_ntlmv2] != nil ? opt[:use_ntlmv2] : false - send_lm = opt[:send_lm] != nil ? opt[:send_lm] : true - send_ntlm = opt[:send_ntlm] != nil ? opt[:send_ntlm] : true - use_lanman_key = opt[:use_lanman_key] != nil ? opt[:use_lanman_key] : false + usentlm2_session = opt[:usentlm2_session] != nil ? opt[:usentlm2_session] : true + use_ntlmv2 = opt[:use_ntlmv2] != nil ? opt[:use_ntlmv2] : false + send_lm = opt[:send_lm] != nil ? opt[:send_lm] : true + send_ntlm = opt[:send_ntlm] != nil ? opt[:send_ntlm] : true + use_lanman_key = opt[:use_lanman_key] != nil ? opt[:use_lanman_key] : false - # Create the sessionkey (aka signing key, aka mackey) and encrypted session key - # Server will decide for key_size and key_exchange - enc_session_key = '' - signing_key = '' + # Create the sessionkey (aka signing key, aka mackey) and encrypted session key + # Server will decide for key_size and key_exchange + enc_session_key = '' + signing_key = '' - # Set default key size and key exchange values - key_size = 40 - key_exchange = false - # Remove ntlmssp.negotiate56 - ntlmssp_flags &= 0x7fffffff - # Remove ntlmssp.negotiatekeyexch - ntlmssp_flags &= 0xbfffffff - # Remove ntlmssp.negotiate128 - ntlmssp_flags &= 0xdfffffff - # Check the keyexchange - if server_ntlmssp_flags & CONST::NEGOTIATE_KEY_EXCH != 0 then - key_exchange = true - ntlmssp_flags |= CONST::NEGOTIATE_KEY_EXCH - end - # Check 128bits - if server_ntlmssp_flags & CONST::NEGOTIATE_128 != 0 then - key_size = 128 - ntlmssp_flags |= CONST::NEGOTIATE_128 - ntlmssp_flags |= CONST::NEGOTIATE_56 - # Check 56bits - else - if server_ntlmssp_flags & CONST::NEGOTIATE_56 != 0 then - key_size = 56 - ntlmssp_flags |= CONST::NEGOTIATE_56 - end - end - # Generate the user session key - lanman_weak = false - if send_ntlm # Should be default - if usentlm2_session - if use_ntlmv2 - if self.is_pass_ntlm_hash?(pass) - user_session_key = CRYPT::ntlmv2_user_session_key(user, - [ pass.upcase()[33,65] ].pack('H32'), - domain, - challenge_key, ntlm_cli_challenge, - {:pass_is_hash => true}) - else - user_session_key = CRYPT::ntlmv2_user_session_key(user, pass, domain, - challenge_key, ntlm_cli_challenge) - end - else - if self.is_pass_ntlm_hash?(pass) - user_session_key = CRYPT::ntlm2_session_user_session_key([ pass.upcase()[33,65] ].pack('H32'), - challenge_key, - client_challenge, - {:pass_is_hash => true}) - else - user_session_key = CRYPT::ntlm2_session_user_session_key(pass, challenge_key, - client_challenge) - end - end - else # lmv1/ntlmv1 - # lanman_key may also be used without ntlm response but it is not so much used - # so we don't care about this feature - if send_lm && use_lanman_key - if self.is_pass_ntlm_hash?(pass) - user_session_key = CRYPT::lanman_session_key([ pass.upcase()[0,32] ].pack('H32'), - challenge_key, - {:pass_is_hash => true}) - else - user_session_key = CRYPT::lanman_session_key(pass, challenge_key) - end - lanman_weak = true + # Set default key size and key exchange values + key_size = 40 + key_exchange = false + # Remove ntlmssp.negotiate56 + ntlmssp_flags &= 0x7fffffff + # Remove ntlmssp.negotiatekeyexch + ntlmssp_flags &= 0xbfffffff + # Remove ntlmssp.negotiate128 + ntlmssp_flags &= 0xdfffffff + # Check the keyexchange + if server_ntlmssp_flags & CONST::NEGOTIATE_KEY_EXCH != 0 then + key_exchange = true + ntlmssp_flags |= CONST::NEGOTIATE_KEY_EXCH + end + # Check 128bits + if server_ntlmssp_flags & CONST::NEGOTIATE_128 != 0 then + key_size = 128 + ntlmssp_flags |= CONST::NEGOTIATE_128 + ntlmssp_flags |= CONST::NEGOTIATE_56 + # Check 56bits + else + if server_ntlmssp_flags & CONST::NEGOTIATE_56 != 0 then + key_size = 56 + ntlmssp_flags |= CONST::NEGOTIATE_56 + end + end + # Generate the user session key + lanman_weak = false + if send_ntlm # Should be default + if usentlm2_session + if use_ntlmv2 + if self.is_pass_ntlm_hash?(pass) + user_session_key = CRYPT::ntlmv2_user_session_key(user, + [ pass.upcase()[33,65] ].pack('H32'), + domain, + challenge_key, ntlm_cli_challenge, + {:pass_is_hash => true}) + else + user_session_key = CRYPT::ntlmv2_user_session_key(user, pass, domain, + challenge_key, ntlm_cli_challenge) + end + else + if self.is_pass_ntlm_hash?(pass) + user_session_key = CRYPT::ntlm2_session_user_session_key([ pass.upcase()[33,65] ].pack('H32'), + challenge_key, + client_challenge, + {:pass_is_hash => true}) + else + user_session_key = CRYPT::ntlm2_session_user_session_key(pass, challenge_key, + client_challenge) + end + end + else # lmv1/ntlmv1 + # lanman_key may also be used without ntlm response but it is not so much used + # so we don't care about this feature + if send_lm && use_lanman_key + if self.is_pass_ntlm_hash?(pass) + user_session_key = CRYPT::lanman_session_key([ pass.upcase()[0,32] ].pack('H32'), + challenge_key, + {:pass_is_hash => true}) + else + user_session_key = CRYPT::lanman_session_key(pass, challenge_key) + end + lanman_weak = true - else - if self.is_pass_ntlm_hash?(pass) - user_session_key = CRYPT::ntlmv1_user_session_key([ pass.upcase()[33,65] ].pack('H32'), - {:pass_is_hash => true}) - else - user_session_key = CRYPT::ntlmv1_user_session_key(pass) - end - end - end - else - if usentlm2_session && use_ntlmv2 - if self.is_pass_ntlm_hash?(pass) - user_session_key = CRYPT::lmv2_user_session_key(user, [ pass.upcase()[33,65] ].pack('H32'), - domain, - challenge_key, client_challenge, - {:pass_is_hash => true}) - else - user_session_key = CRYPT::lmv2_user_session_key(user, pass, domain, - challenge_key, client_challenge) - end - else - if self.is_pass_ntlm_hash?(pass) - user_session_key = CRYPT::lmv1_user_session_key([ pass.upcase()[0,32] ].pack('H32'), - {:pass_is_hash => true}) - else - user_session_key = CRYPT::lmv1_user_session_key(pass) - end - end - end + else + if self.is_pass_ntlm_hash?(pass) + user_session_key = CRYPT::ntlmv1_user_session_key([ pass.upcase()[33,65] ].pack('H32'), + {:pass_is_hash => true}) + else + user_session_key = CRYPT::ntlmv1_user_session_key(pass) + end + end + end + else + if usentlm2_session && use_ntlmv2 + if self.is_pass_ntlm_hash?(pass) + user_session_key = CRYPT::lmv2_user_session_key(user, [ pass.upcase()[33,65] ].pack('H32'), + domain, + challenge_key, client_challenge, + {:pass_is_hash => true}) + else + user_session_key = CRYPT::lmv2_user_session_key(user, pass, domain, + challenge_key, client_challenge) + end + else + if self.is_pass_ntlm_hash?(pass) + user_session_key = CRYPT::lmv1_user_session_key([ pass.upcase()[0,32] ].pack('H32'), + {:pass_is_hash => true}) + else + user_session_key = CRYPT::lmv1_user_session_key(pass) + end + end + end - user_session_key = CRYPT::make_weak_sessionkey(user_session_key,key_size, lanman_weak) + user_session_key = CRYPT::make_weak_sessionkey(user_session_key,key_size, lanman_weak) - # Sessionkey and encrypted session key - if key_exchange - signing_key = Rex::Text.rand_text(16) - enc_session_key = CRYPT::encrypt_sessionkey(signing_key, user_session_key) - else - signing_key = user_session_key - end + # Sessionkey and encrypted session key + if key_exchange + signing_key = Rex::Text.rand_text(16) + enc_session_key = CRYPT::encrypt_sessionkey(signing_key, user_session_key) + else + signing_key = user_session_key + end - return signing_key, enc_session_key, ntlmssp_flags + return signing_key, enc_session_key, ntlmssp_flags - end + end end end