From c2a063c8aee00d599cc5d9ec21476e745530ec9a Mon Sep 17 00:00:00 2001 From: James Lee Date: Fri, 13 May 2016 19:00:52 -0500 Subject: [PATCH] Start using rubyntlm for ssp auth --- Gemfile.lock | 1 + lib/rex/proto/smb/client.rb | 74 ++++++++++++++---------------------- metasploit-framework.gemspec | 4 +- 3 files changed, 32 insertions(+), 47 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 34c2a31378..7adf0db2b7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -30,6 +30,7 @@ PATH redcarpet robots rubyzip + rubyntlm sqlite3 tzinfo tzinfo-data diff --git a/lib/rex/proto/smb/client.rb b/lib/rex/proto/smb/client.rb index f3a81c5c60..a587dd24b4 100644 --- a/lib/rex/proto/smb/client.rb +++ b/lib/rex/proto/smb/client.rb @@ -4,6 +4,8 @@ module Proto module SMB class Client + require 'net/ntlm' + require 'rex/text' require 'rex/struct2' require 'rex/proto/smb/constants' @@ -166,14 +168,14 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils # Scan the packet receive cache for a matching response def smb_recv_cache_find_match(expected_type) - + clean = [] found = nil @smb_recv_cache.each do |cent| pkt, data, tstamp = cent - # Return matching packets and mark for removal + # Return matching packets and mark for removal if pkt['Payload']['SMB'].v['Command'] == expected_type found = [pkt,data] clean << cent @@ -623,16 +625,15 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils # Authenticate and establish a session - def session_setup(*args) - + def session_setup(user='', pass='', domain='', do_recv=true) if (self.dialect =~ /^(NT LANMAN 1.0|NT LM 0.12)$/) if (self.challenge_key) - return self.session_setup_no_ntlmssp(*args) + return self.session_setup_no_ntlmssp(user, pass, domain, do_recv) end if ( self.extended_security ) - return self.session_setup_with_ntlmssp(*args) + return self.session_setup_with_ntlmssp(user, pass, domain, nil, do_recv) end end @@ -831,7 +832,16 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils name = Rex::Text.rand_text_alphanumeric(16) end - blob = NTLM_UTILS.make_ntlmssp_secblob_init(domain, name, ntlmssp_flags) + @ntlm_client = Net::NTLM::Client.new( + user, + pass, + workstation: name, + domain: domain, + flags: ntlmssp_flags + ) + + + blob = @ntlm_client.init_context.serialize native_data = '' native_data << self.native_os + "\x00" @@ -892,37 +902,9 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils # Save the temporary UserID for use in the next request temp_user_id = ack['Payload']['SMB'].v['UserID'] - # 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] || '' - - - 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 - self.signing_key, enc_session_key, ntlmssp_flags = NTLM_UTILS.create_session_key(ntlmssp_flags, 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) + type3 = @ntlm_client.init_context([blob].pack('m')) + type3_blob = type3.serialize + self.signing_key = @ntlm_client.session_key pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct self.smb_defaults(pkt['Payload']['SMB']) @@ -944,8 +926,8 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils pkt['Payload'].v['VCNum'] = 1 pkt['Payload'].v['Capabilities'] = 0x8000d05c pkt['Payload'].v['SessionKey'] = self.session_id - pkt['Payload'].v['SecurityBlobLen'] = blob.length - pkt['Payload'].v['Payload'] = blob + native_data + pkt['Payload'].v['SecurityBlobLen'] = type3_blob.length + pkt['Payload'].v['Payload'] = type3_blob + native_data # NOTE: if do_recv is set to false, we cant reach here... self.smb_send(pkt.to_s) @@ -1771,7 +1753,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils # Remove the NetBIOS header resp_rpkt.slice!(0, 4) - resp_parm = resp_rpkt[poff, pcnt] + _resp_parm = resp_rpkt[poff, pcnt] resp_data = resp_rpkt[doff, dcnt] return resp_data @@ -1797,7 +1779,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils # Remove the NetBIOS header resp_rpkt.slice!(0, 4) - resp_parm = resp_rpkt[poff, pcnt] + _resp_parm = resp_rpkt[poff, pcnt] resp_data = resp_rpkt[doff, dcnt] return resp_data @@ -1958,7 +1940,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils resp = find_next(last_search_id, last_offset, last_filename) # Flip bit so response params will parse correctly - search_next = 1 + search_next = 1 end files @@ -1973,7 +1955,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils 260, # Level of interest resume_key, # Resume key from previous (Last name offset) 6, # Close search if end of search - ].pack('vvvVv') + + ].pack('vvvVv') + last_filename.to_s + # Last filename returned from find_first or find_next "\x00" # Terminate the file name @@ -2006,7 +1988,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils search_path = "#{current_path}#{fname}\\" file_search(search_path, regex, depth).each {|fn| files << fn } rescue Rex::Proto::SMB::Exceptions::ErrorCode => e - + # Ignore common errors related to permissions and non-files if %W{ STATUS_ACCESS_DENIED @@ -2030,9 +2012,9 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils # Creates a new directory on the mounted tree def create_directory(name) - files = { } parm = [0].pack('V') + name + "\x00" resp = trans2(CONST::TRANS2_CREATE_DIRECTORY, parm, '') + resp end # public read/write methods diff --git a/metasploit-framework.gemspec b/metasploit-framework.gemspec index 8394491151..e17e10a8ea 100644 --- a/metasploit-framework.gemspec +++ b/metasploit-framework.gemspec @@ -75,6 +75,8 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'msgpack' # get list of network interfaces, like eth* from OS. spec.add_runtime_dependency 'network_interface' + # NTLM authentication + spec.add_runtime_dependency 'rubyntlm' # Needed by anemone crawler spec.add_runtime_dependency 'nokogiri' # Needed by db.rb and Msf::Exploit::Capture @@ -96,7 +98,7 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'patch_finder' # TimeZone info spec.add_runtime_dependency 'tzinfo-data' - + # rb-readline doesn't work with Ruby Installer due to error with Fiddle: # NoMethodError undefined method `dlopen' for Fiddle:Module unless Gem.win_platform?