lib/rex/proto/smb/client.rb in librex-0.0.12 vs lib/rex/proto/smb/client.rb in librex-0.0.13

- old
+ new

@@ -56,10 +56,13 @@ # Signing self.sequence_counter = 0 self.signing_key = '' self.require_signing = false + #Misc + self.spnopt = {} + end # Read a SMB packet from the socket def smb_recv @@ -652,11 +655,11 @@ #raise XCEPT::SigningError if self.require_signing self.require_signing = false if self.require_signing - if UTILS.is_pass_ntlm_hash?(pass) + if NTLM_UTILS.is_pass_ntlm_hash?(pass) arglm = { :lm_hash => [ pass.upcase()[0,32] ].pack('H32'), :challenge => self.challenge_key } hash_lm = NTLM_CRYPT::lm_response(arglm) @@ -718,11 +721,11 @@ # Authenticate without ntlmssp with a precomputed hash pair def session_setup_no_ntlmssp_prehash(user, domain, hash_lm, hash_nt, do_recv = true) - raise XCEPT::NTLM2MissingChallenge if self.require_signing + #raise XCEPT::NTLM2MissingChallenge if self.require_signing data = '' data << hash_lm data << hash_nt data << user + "\x00" @@ -767,37 +770,22 @@ return ack end # Authenticate using extended security negotiation def session_setup_with_ntlmssp(user = '', pass = '', domain = '', name = nil, do_recv = true) - - if require_signing - ntlmssp_flags = 0xe2088215 - else - ntlmssp_flags = 0xa2080205 - end + ntlm_options = { + :signing => self.require_signing, + :usentlm2_session => self.usentlm2_session, + :use_ntlmv2 => self.use_ntlmv2, + :send_lm => self.send_lm, + :send_ntlm => self.send_ntlm, + :use_lanman_key => self.use_lanman_key + } - if self.usentlm2_session - if self.use_ntlmv2 - #set Negotiate Target Info - ntlmssp_flags |= NTLM_CONST::NEGOTIATE_TARGET_INFO - end + ntlmssp_flags = NTLM_UTILS.make_ntlm_flags(ntlm_options) - else - #remove the ntlm2_session flag - ntlmssp_flags &= 0xfff7ffff - #set lanmanflag only when lm and ntlm are sent - if self.send_lm - ntlmssp_flags |= NTLM_CONST::NEGOTIATE_LMKEY if self.use_lanman_key - end - end - - #we can also downgrade ntlm2_session when we send only lmv1 - ntlmssp_flags &= 0xfff7ffff if self.usentlm2_session && (not self.use_ntlmv2) && (not self.send_ntlm) - - if (name == nil) name = Rex::Text.rand_text_alphanumeric(16) end blob = NTLM_UTILS.make_ntlmssp_secblob_init(domain, name, ntlmssp_flags) @@ -859,319 +847,37 @@ self.peer_native_lm = info[1] # Save the temporary UserID for use in the next request temp_user_id = ack['Payload']['SMB'].v['UserID'] - # Extract the NTLM challenge key the lazy way - cidx = blob.index("NTLMSSP\x00\x02\x00\x00\x00") + # Get default data + blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(blob) + self.challenge_key = blob_data[:challenge_key] + server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error + #netbios name + self.default_name = blob_data[:default_name] || '' + #netbios domain + self.default_domain = blob_data[:default_domain] || '' + #dns name + self.dns_host_name = blob_data[:dns_host_name] || '' + #dns domain + self.dns_domain_name = blob_data[:dns_domain_name] || '' + #Client time + chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || '' - if (cidx == -1) - raise XCEPT::NTLM2MissingChallenge - end - # Store the challenge key - self.challenge_key = blob[cidx + 24, 8] - - # 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] - chall_MsvAvTimestamp = nil - 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 - self.default_name = addr.gsub("\x00", '') - when 2 - #netbios domain - self.default_domain = addr.gsub("\x00", '') - when 3 - #dns name - self.dns_host_name = addr.gsub("\x00", '') - when 4 - #dns domain - self.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 - 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 - - #calculate the lm/ntlm response - resp_lm = "\x00" * 24 - resp_ntlm = "\x00" * 24 - - client_challenge = Rex::Text.rand_text(8) - ntlm_cli_challenge = '' - if self.send_ntlm #should be default - if self.usentlm2_session - - if self.use_ntlmv2 - # This is only a partial implementation, in some situation recent servers may send STATUS_INVALID_PARAMETER - # answer must then be somewhere in [MS-NLMP].pdf around 3.1.5.2.1 :-/ - ntlm_cli_challenge = NTLM_UTILS::make_ntlmv2_clientchallenge(default_domain, default_name, dns_domain_name, - dns_host_name,client_challenge , chall_MsvAvTimestamp) - if UTILS.is_pass_ntlm_hash?(pass) - argntlm = { - :ntlmv2_hash => NTLM_CRYPT::ntlmv2_hash( - user, - [ pass.upcase()[33,65] ].pack('H32'), - domain,{:pass_is_hash => true} - ), - :challenge => self.challenge_key - } - else - argntlm = { - :ntlmv2_hash => NTLM_CRYPT::ntlmv2_hash(user, pass, domain), - :challenge => self.challenge_key - } - end - - optntlm = { :nt_client_challenge => ntlm_cli_challenge} - ntlmv2_response = NTLM_CRYPT::ntlmv2_response(argntlm,optntlm) - resp_ntlm = ntlmv2_response - - if self.send_lm - if UTILS.is_pass_ntlm_hash?(pass) - arglm = { - :ntlmv2_hash => NTLM_CRYPT::ntlmv2_hash( - user, - [ pass.upcase()[33,65] ].pack('H32'), - domain,{:pass_is_hash => true} - ), - :challenge => self.challenge_key - } - else - arglm = { - :ntlmv2_hash => NTLM_CRYPT::ntlmv2_hash(user,pass, domain), - :challenge => self.challenge_key - } - end - - optlm = { :client_challenge => client_challenge } - resp_lm = NTLM_CRYPT::lmv2_response(arglm, optlm) - else - resp_lm = "\x00" * 24 - end - - else # ntlm2_session - if UTILS.is_pass_ntlm_hash?(pass) - argntlm = { - :ntlm_hash => [ pass.upcase()[33,65] ].pack('H32'), - :challenge => self.challenge_key - } - else - argntlm = { - :ntlm_hash => NTLM_CRYPT::ntlm_hash(pass), - :challenge => self.challenge_key - } - end - - optntlm = { :client_challenge => client_challenge} - resp_ntlm = 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 UTILS.is_pass_ntlm_hash?(pass) - argntlm = { - :ntlm_hash => [ pass.upcase()[33,65] ].pack('H32'), - :challenge => self.challenge_key - } - else - argntlm = { - :ntlm_hash => NTLM_CRYPT::ntlm_hash(pass), - :challenge => self.challenge_key - } - end - - resp_ntlm = NTLM_CRYPT::ntlm_response(argntlm) - if self.send_lm - if UTILS.is_pass_ntlm_hash?(pass) - arglm = { - :lm_hash => [ pass.upcase()[0,32] ].pack('H32'), - :challenge => self.challenge_key - } - else - arglm = { - :lm_hash => NTLM_CRYPT::lm_hash(pass), - :challenge => self.challenge_key - } - end - resp_lm = NTLM_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 self.usentlm2_session && self.use_ntlmv2 - if UTILS.is_pass_ntlm_hash?(pass) - arglm = { - :ntlmv2_hash => NTLM_CRYPT::ntlmv2_hash( - user, - [ pass.upcase()[33,65] ].pack('H32'), - domain,{:pass_is_hash => true} - ), - :challenge => self.challenge_key - } - else - arglm = { - :ntlmv2_hash => NTLM_CRYPT::ntlmv2_hash(user,pass, domain), - :challenge => self.challenge_key - } - end - optlm = { :client_challenge => client_challenge } - resp_lm = NTLM_CRYPT::lmv2_response(arglm, optlm) - else - if UTILS.is_pass_ntlm_hash?(pass) - arglm = { - :lm_hash => [ pass.upcase()[0,32] ].pack('H32'), - :challenge => self.challenge_key - } - else - arglm = { - :lm_hash => NTLM_CRYPT::lm_hash(pass), - :challenge => self.challenge_key - } - end - resp_lm = NTLM_CRYPT::lm_response(arglm) - end - resp_ntlm = "" - end - - - # Create the sessionkey (aka signing key, aka mackey) and encrypted session key - # Server will decide for key_size and key_exchange + resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(user, pass, self.challenge_key, domain, + default_name, default_domain, dns_host_name, + dns_domain_name, chall_MsvAvTimestamp , + self.spnopt, ntlm_options) enc_session_key = '' + self.sequence_counter = 0 if self.require_signing - - server_ntlmssp_flags = blob[cidx + 20, 4].unpack("V")[0] - # 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 & NTLM_CONST::NEGOTIATE_KEY_EXCH != 0 then - key_exchange = true - ntlmssp_flags |= NTLM_CONST::NEGOTIATE_KEY_EXCH - end - # Check 128bits - if server_ntlmssp_flags & NTLM_CONST::NEGOTIATE_128 != 0 then - key_size = 128 - ntlmssp_flags |= NTLM_CONST::NEGOTIATE_128 - ntlmssp_flags |= NTLM_CONST::NEGOTIATE_56 - # Check 56bits - else - if server_ntlmssp_flags & NTLM_CONST::NEGOTIATE_56 != 0 then - key_size = 56 - ntlmssp_flags |= NTLM_CONST::NEGOTIATE_56 - end - end - - # Generate the user session key - lanman_weak = false - - if self.send_ntlm # Should be default - if self.usentlm2_session - if self.use_ntlmv2 - if UTILS.is_pass_ntlm_hash?(pass) - user_session_key = NTLM_CRYPT::ntlmv2_user_session_key(user, - [ pass.upcase()[33,65] ].pack('H32'), - domain, - self.challenge_key, ntlm_cli_challenge, - {:pass_is_hash => true}) - else - user_session_key = NTLM_CRYPT::ntlmv2_user_session_key(user, pass, domain, - self.challenge_key, ntlm_cli_challenge) - end - else - if UTILS.is_pass_ntlm_hash?(pass) - user_session_key = NTLM_CRYPT::ntlm2_session_user_session_key([ pass.upcase()[33,65] ].pack('H32'), - self.challenge_key, - client_challenge, - {:pass_is_hash => true}) - else - user_session_key = NTLM_CRYPT::ntlm2_session_user_session_key(pass, self.challenge_key, - client_challenge) - end - end - else # lmv1/ntlmv1 - if self.send_lm - if self.use_lanman_key - if UTILS.is_pass_ntlm_hash?(pass) - user_session_key = NTLM_CRYPT::lanman_session_key([ pass.upcase()[0,32] ].pack('H32'), - self.challenge_key, - {:pass_is_hash => true}) - else - user_session_key = NTLM_CRYPT::lanman_session_key(pass, self.challenge_key) - end - lanman_weak = true - else - if UTILS.is_pass_ntlm_hash?(pass) - user_session_key = NTLM_CRYPT::ntlmv1_user_session_key([ pass.upcase()[33,65] ].pack('H32'), - {:pass_is_hash => true}) - else - user_session_key = NTLM_CRYPT::ntlmv1_user_session_key(pass) - end - - end - end - end - else - if self.usentlm2_session && self.use_ntlmv2 - if UTILS.is_pass_ntlm_hash?(pass) - user_session_key = NTLM_CRYPT::lmv2_user_session_key(user, [ pass.upcase()[33,65] ].pack('H32'), - domain, - self.challenge_key, client_challenge, - {:pass_is_hash => true}) - else - user_session_key = NTLM_CRYPT::lmv2_user_session_key(user, pass, domain, - self.challenge_key, client_challenge) - end - else - if UTILS.is_pass_ntlm_hash?(pass) - user_session_key = NTLM_CRYPT::lmv1_user_session_key([ pass.upcase()[0,32] ].pack('H32'), - {:pass_is_hash => true}) - else - user_session_key = NTLM_CRYPT::lmv1_user_session_key(pass) - end - end - end - - user_session_key = NTLM_CRYPT::make_weak_sessionkey(user_session_key,key_size, lanman_weak) - self.sequence_counter = 0 - # Sessionkey and encrypted session key - if key_exchange - self.signing_key = Rex::Text.rand_text(16) - enc_session_key = NTLM_CRYPT::encrypt_sessionkey(self.signing_key, user_session_key) - else - self.signing_key = user_session_key - end - + self.signing_key, enc_session_key = NTLM_UTILS.create_session_key(server_ntlmssp_flags, user, pass, domain, self.challenge_key, + client_challenge, ntlm_cli_challenge, ntlm_options) end + # Create the security blob data blob = NTLM_UTILS.make_ntlmssp_secblob_auth(domain, name, user, resp_lm, resp_ntlm, enc_session_key, ntlmssp_flags) pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct self.smb_defaults(pkt['Payload']['SMB']) @@ -2214,9 +1920,11 @@ # public read/write methods attr_accessor :native_os, :native_lm, :encrypt_passwords, :extended_security, :read_timeout, :evasion_opts attr_accessor :verify_signature, :use_ntlmv2, :usentlm2_session, :send_lm, :use_lanman_key, :send_ntlm attr_accessor :system_time, :system_zone + #misc + attr_accessor :spnopt # used for SPN # public read methods attr_reader :dialect, :session_id, :challenge_key, :peer_native_lm, :peer_native_os attr_reader :default_domain, :default_name, :auth_user, :auth_user_id attr_reader :multiplex_id, :last_tree_id, :last_file_id, :process_id, :last_search_id