lib/rex/proto/ntlm/utils.rb in librex-0.0.65 vs lib/rex/proto/ntlm/utils.rb in librex-0.0.66

- old
+ new

@@ -1,5 +1,6 @@ +# -*- coding: binary -*- require 'rex/proto/ntlm/constants' require 'rex/proto/ntlm/crypt' require 'rex/proto/ntlm/exceptions' module Rex @@ -55,48 +56,48 @@ # 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( + 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( + "\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: + # mechListMIC: # principal: account@domain def self.make_negotiate_secblob_resp(account, domain) - blob = - "\x60" + self.asn1encode( + 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( @@ -105,14 +106,14 @@ "\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( @@ -124,12 +125,12 @@ ) ) ) ) - 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') + @@ -142,23 +143,23 @@ [ name.length, #length name.length, #max length domain.length + 32 - ].pack('vvV') + + ].pack('vvV') + 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( + 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( @@ -173,48 +174,48 @@ ) ) ) ) - return blob + return blob end - # BLOB without GSS usefull for ntlm type 2 message + # 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 + 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 + + chall + "\x00\x00\x00\x00\x00\x00\x00\x00" + [ addr_list.length, # length addr_list.length, # max length - (ptr += win_domain.length) + (ptr += win_domain.length) ].pack('vvV') + - win_domain + + 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) - + blob = "\xa1" + self.asn1encode( "\x30" + self.asn1encode( "\xa0" + self.asn1encode( "\x0a" + self.asn1encode( @@ -229,75 +230,75 @@ "\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 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 - + 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 + ptr = 64 blob = "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') + - + ].pack('vvV') + + [ # Domain Name domain_uni.length, domain_uni.length, (ptr += ntlm.length) - ].pack('vvV') + + ].pack('vvV') + [ # Username user_uni.length, user_uni.length, (ptr += domain_uni.length) - ].pack('vvV') + + ].pack('vvV') + [ # Hostname name_uni.length, name_uni.length, (ptr += user_uni.length) - ].pack('vvV') + - + ].pack('vvV') + + [ # Session Key (none) session.length, session.length, (ptr += name_uni.length) - ].pack('vvV') + + ].pack('vvV') + [ flags ].pack('V') + lm + ntlm + domain_uni + user_uni + - name_uni + - session + "\x00" + name_uni + + session + "\x00" return blob end # GSS BLOB Usefull for ntlmssp type 3 message @@ -325,11 +326,11 @@ "\xa0" + self.asn1encode( "\x0a" + self.asn1encode( "\x00" ) ) - ) + ) ) return blob end # Return the correct ntlmflags upon the configuration @@ -340,32 +341,32 @@ 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 + if signing ntlmssp_flags = 0xe2088215 else ntlmssp_flags = 0xa2080205 end if usentlm2_session if use_ntlmv2 #set Negotiate Target Info - ntlmssp_flags |= CONST::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 - + #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 @@ -412,25 +413,25 @@ #A 32-bit value indicating server or client configuration when 7 #Client time data[:chall_MsvAvTimestamp] = addr when 8 - #A Restriction_Encoding structure + #A Restriction_Encoding structure when 9 - #The SPN of the target server. + #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, + 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 @@ -446,32 +447,32 @@ 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 + # 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 + # 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 - + 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 + [0].pack("V") + #Reserved3 addr_list + "\x00" * 4 end # create lm/ntlm responses @@ -490,139 +491,139 @@ 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 , + 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 = { + argntlm = { :ntlmv2_hash => CRYPT::ntlmv2_hash( - user, - [ pass.upcase()[33,65] ].pack('H32'), + user, + [ pass.upcase()[33,65] ].pack('H32'), domain,{:pass_is_hash => true} ), - :challenge => challenge_key + :challenge => challenge_key } else argntlm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user, pass, domain), - :challenge => challenge_key + :challenge => challenge_key } end optntlm = { :nt_client_challenge => ntlm_cli_challenge} ntlmv2_response = CRYPT::ntlmv2_response(argntlm,optntlm) - resp_ntlm = ntlmv2_response - + 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'), + user, + [ pass.upcase()[33,65] ].pack('H32'), domain,{:pass_is_hash => true} ), - :challenge => challenge_key + :challenge => challenge_key } else arglm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user,pass, domain), - :challenge => challenge_key + :challenge => challenge_key } 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 + argntlm = { + :ntlm_hash => [ pass.upcase()[33,65] ].pack('H32'), + :challenge => challenge_key } else argntlm = { - :ntlm_hash => CRYPT::ntlm_hash(pass), - :challenge => challenge_key + :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] - + # 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 + :ntlm_hash => [ pass.upcase()[33,65] ].pack('H32'), + :challenge => challenge_key } else argntlm = { - :ntlm_hash => CRYPT::ntlm_hash(pass), - :challenge => challenge_key + :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 + :challenge => challenge_key } else arglm = { :lm_hash => CRYPT::lm_hash(pass), - :challenge => challenge_key + :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 + 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'), + user, + [ pass.upcase()[33,65] ].pack('H32'), domain,{:pass_is_hash => true} ), - :challenge => challenge_key + :challenge => challenge_key } else arglm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user,pass, domain), - :challenge => challenge_key + :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 + :challenge => challenge_key } else arglm = { :lm_hash => CRYPT::lm_hash(pass), - :challenge => challenge_key + :challenge => challenge_key } end resp_lm = CRYPT::lm_response(arglm) end resp_ntlm = "" @@ -675,44 +676,44 @@ 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, + user_session_key = CRYPT::ntlmv2_user_session_key(user, [ pass.upcase()[33,65] ].pack('H32'), - domain, - challenge_key, ntlm_cli_challenge, + domain, + challenge_key, ntlm_cli_challenge, {:pass_is_hash => true}) else - user_session_key = CRYPT::ntlmv2_user_session_key(user, pass, domain, + 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, + 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, + 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 + # 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, + 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 @@ -721,42 +722,42 @@ 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, + 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, + 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'), + 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 - + return signing_key, enc_session_key, ntlmssp_flags - - + + end - + end end end