diff --git a/lib/msf/core/exploit/mssql.rb b/lib/msf/core/exploit/mssql.rb index c194219078..0cbb6ee16d 100644 --- a/lib/msf/core/exploit/mssql.rb +++ b/lib/msf/core/exploit/mssql.rb @@ -3,6 +3,7 @@ require 'msf/core/exploit/mssql_commands' require 'rex/proto/ntlm/crypt' require 'rex/proto/ntlm/constants' require 'rex/proto/ntlm/utils' +require 'rex/proto/ntlm/exceptions' module Msf @@ -17,16 +18,16 @@ module Exploit::Remote::MSSQL include Exploit::Remote::MSSQL_COMMANDS include Exploit::Remote::Udp include Exploit::Remote::Tcp + include Exploit::Remote::NTLM::Client # # Constants # - - # Ntlm NTLM_CRYPT = Rex::Proto::NTLM::Crypt NTLM_CONST = Rex::Proto::NTLM::Constants NTLM_UTILS = Rex::Proto::NTLM::Utils - + NTLM_XCEPT = Rex::Proto::NTLM::Exceptions + # Encryption ENCRYPT_OFF = 0x00 #Encryption is available but off. ENCRYPT_ON = 0x01 #Encryption is available and on. @@ -67,15 +68,16 @@ module Exploit::Remote::MSSQL Opt::RPORT(1433), OptString.new('USERNAME', [ false, 'The username to authenticate as', 'sa']), OptString.new('PASSWORD', [ false, 'The password for the specified username', '']), + OptBool.new('USE_WINDOWS_AUTHENT', [ true, 'Use windows authentification', false]), ], Msf::Exploit::Remote::MSSQL) register_advanced_options( [ - OptPath.new('HEX2BINARY', [ false, "The path to the hex2binary script on the disk", - File.join(Msf::Config.install_root, "data", "exploits", "mssql", "h2b") - ]) - ], Msf::Exploit::Remote::MSSQL) - + OptPath.new('HEX2BINARY', [ false, "The path to the hex2binary script on the disk", + File.join(Msf::Config.install_root, "data", "exploits", "mssql", "h2b") + ]), + OptString.new('DOMAIN', [ true, 'The domain to use for windows authentification', 'WORKSTATION']) + ], Msf::Exploit::Remote::MSSQL) register_autofilter_ports([ 1433, 1434, 1435, 14330, 2533, 9152, 2638 ]) register_autofilter_services(%W{ ms-sql-s ms-sql2000 sybase }) end @@ -415,9 +417,22 @@ module Exploit::Remote::MSSQL cname = Rex::Text.to_unicode( Rex::Text.rand_text_alpha(rand(8)+1) ) aname = Rex::Text.to_unicode( Rex::Text.rand_text_alpha(rand(8)+1) ) #application and library name sname = Rex::Text.to_unicode( rhost ) - dname = Rex::Text.to_unicode( db ) + dname = Rex::Text.to_unicode( db ) + + ntlm_options = { + :signing => false, + :usentlm2_session => datastore['NTLM::UseNTLM2_session'], + :use_ntlmv2 => datastore['NTLM::UseNTLMv2'], + :send_lm => datastore['NTLM::SendLM'], + :send_ntlm => 'NTLM::SendNTLM' + } + + ntlmssp_flags = NTLM_UTILS.make_ntlm_flags(ntlm_options) + workstation_name = Rex::Text.rand_text_alpha(rand(8)+1) + domain_name = datastore['DOMAIN'] + # use a default value for now - ntlmsspblob = NTLM_UTILS::make_ntlmssp_blob_init('WORKGROUP','WORKSTATION', 0xa2080205) + ntlmsspblob = NTLM_UTILS::make_ntlmssp_blob_init(domain_name, workstation_name, ntlmssp_flags) idx = pkt.size + 50 # lengths below @@ -470,38 +485,38 @@ module Exploit::Remote::MSSQL # is set to STATUS_NORMAL and not STATUS_END_OF_MESSAGE, then internally it waits for the ntlm_authentification resp = mssql_send_recv(pkt,15, false) - # Extract the NTLM challenge key the lazy way - cidx = resp.index("NTLMSSP\x00\x02\x00\x00\x00") - - #add exception and ... - #if (cidx == -1) - # raise XCEPT::NTLM2MissingChallenge - #end - #for the moment : - raise Error,"No challenge" if not cidx - - resp.slice!(0,cidx) - - challenge_key = resp[24, 8] - - #!!!TEMPORARY SOLUTION !!! (copy/paste from smb/client.rb)!!! - # Until ntlm proto will have a wrapper in lib/msf and will be integrated in smb and here - # we will use ntlm2_session response only and hope for the best ... (no ntlmv2 , no signing for now ...) - + # Get default data + begin + blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(resp) + # a domain.length < 3 will hit this + rescue NTLM_XCEPT::NTLMMissingChallenge + info = {:errors => []} + mssql_parse_reply(resp, info) + mssql_print_reply(info) + return false + end + challenge_key = blob_data[:challenge_key] + server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error + #netbios name + default_name = blob_data[:default_name] || '' + #netbios domain + default_domain = blob_data[:default_domain] || '' + #dns name + dns_host_name = blob_data[:dns_host_name] || '' + #dns domain + dns_domain_name = blob_data[:dns_domain_name] || '' + #Client time + chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || '' - client_challenge = Rex::Text.rand_text(8) - argntlm = { - :ntlm_hash => NTLM_CRYPT::ntlm_hash(pass), - :challenge => challenge_key - } - - 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) + spnopt = {:use_spn => datastore['NTLM::SendNTLM'], :name => self.rhost} + + resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(user, pass, challenge_key, + domain_name, default_name, default_domain, + dns_host_name, dns_domain_name, chall_MsvAvTimestamp, + spnopt, ntlm_options) + + ntlmssp = NTLM_UTILS.make_ntlmssp_blob_auth(domain_name, workstation_name, user, resp_lm, resp_ntlm, '', ntlmssp_flags) - ntlmssp = NTLM_UTILS.make_ntlmssp_blob_auth('WORKGROUP','WORKSTATION', user, resp_lm, resp_ntlm, - '', 0xa2080205) # Create an SSPIMessage idx = 0 pkt = '' diff --git a/lib/rex/proto/ntlm/exceptions.rb b/lib/rex/proto/ntlm/exceptions.rb index 0c1aeb8804..2298ecced2 100644 --- a/lib/rex/proto/ntlm/exceptions.rb +++ b/lib/rex/proto/ntlm/exceptions.rb @@ -2,6 +2,13 @@ module Rex module Proto module NTLM module Exceptions + +class NTLMMissingChallenge < ::RuntimeError + def to_s + "Unable to complete, no challenge key found" + end +end + end end end diff --git a/lib/rex/proto/ntlm/utils.rb b/lib/rex/proto/ntlm/utils.rb index eb17d1e7c6..91fc02ddfe 100644 --- a/lib/rex/proto/ntlm/utils.rb +++ b/lib/rex/proto/ntlm/utils.rb @@ -1,5 +1,6 @@ require 'rex/proto/ntlm/constants' require 'rex/proto/ntlm/crypt' +require 'rex/proto/ntlm/exceptions' module Rex module Proto @@ -8,6 +9,7 @@ class Utils 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 @@ -376,7 +378,7 @@ class Utils cidx = blob.index("NTLMSSP\x00\x02\x00\x00\x00") if not cidx - raise XCEPT::NTLM2MissingChallenge + raise XCEPT::NTLMMissingChallenge end data[:challenge_key] = blob[cidx + 24, 8]