CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb
Views: 1904
1
# -*- coding: binary -*-
2
3
require 'rex/post/dir'
4
require 'rex/post/meterpreter/extensions/stdapi/stdapi'
5
6
module Rex
7
module Post
8
module Meterpreter
9
module Extensions
10
module Stdapi
11
module Fs
12
13
###
14
#
15
# This class implements directory operations against the remote endpoint. It
16
# implements the Rex::Post::Dir interface.
17
#
18
###
19
class Dir < Rex::Post::Dir
20
21
class << self
22
attr_accessor :client
23
end
24
25
##
26
#
27
# Constructor
28
#
29
##
30
31
#
32
# Initializes the directory instance.
33
#
34
def initialize(path)
35
self.path = path
36
self.client = self.class.client
37
end
38
39
##
40
#
41
# Enumeration
42
#
43
##
44
45
#
46
# Enumerates all of the contents of the directory.
47
#
48
def each(&block)
49
client.fs.dir.foreach(self.path, &block)
50
end
51
52
#
53
# Enumerates all of the files/folders in a given directory.
54
#
55
def Dir.entries(name = getwd, glob = nil)
56
request = Packet.create_request(COMMAND_ID_STDAPI_FS_LS)
57
files = []
58
name = name + ::File::SEPARATOR + glob if glob
59
60
request.add_tlv(TLV_TYPE_DIRECTORY_PATH, client.unicode_filter_decode(name))
61
62
response = client.send_request(request)
63
64
response.each(TLV_TYPE_FILE_NAME) { |file_name|
65
files << client.unicode_filter_encode(file_name.value)
66
}
67
68
return files
69
end
70
71
#
72
# Enumerates files with a bit more information than the default entries.
73
#
74
def Dir.entries_with_info(name = getwd)
75
request = Packet.create_request(COMMAND_ID_STDAPI_FS_LS)
76
files = []
77
sbuf = nil
78
new_stat_buf = true
79
80
request.add_tlv(TLV_TYPE_DIRECTORY_PATH, client.unicode_filter_decode(name))
81
82
response = client.send_request(request)
83
84
fname = response.get_tlvs(TLV_TYPE_FILE_NAME)
85
fsname = response.get_tlvs(TLV_TYPE_FILE_SHORT_NAME)
86
fpath = response.get_tlvs(TLV_TYPE_FILE_PATH)
87
88
if response.has_tlv?(TLV_TYPE_STAT_BUF)
89
sbuf = response.get_tlvs(TLV_TYPE_STAT_BUF)
90
else
91
sbuf = response.get_tlvs(TLV_TYPE_STAT_BUF32)
92
new_stat_buf = false
93
end
94
95
if (!fname or !sbuf)
96
return []
97
end
98
99
fname.each_with_index { |file_name, idx|
100
st = nil
101
102
if sbuf[idx] && sbuf[idx].value.length > 0
103
st = ::Rex::Post::FileStat.new
104
if new_stat_buf
105
st.update(sbuf[idx].value)
106
else
107
st.update32(sbuf[idx].value)
108
end
109
end
110
111
files <<
112
{
113
'FileName' => client.unicode_filter_encode(file_name.value),
114
'FilePath' => client.unicode_filter_encode(fpath[idx].value),
115
'FileShortName' => fsname[idx] ? fsname[idx].value : nil,
116
'StatBuf' => st,
117
}
118
}
119
120
return files
121
end
122
123
#
124
# Enumerates all of the files and folders matched with name.
125
# When option match_dir is true, return matched folders.
126
#
127
def Dir.match(name, match_dir = false)
128
path = name + '*'
129
files = []
130
sbuf = nil
131
new_stat_buf = true
132
133
request = Packet.create_request(COMMAND_ID_STDAPI_FS_LS)
134
request.add_tlv(TLV_TYPE_DIRECTORY_PATH, client.unicode_filter_decode(path))
135
response = client.send_request(request)
136
137
fpath = response.get_tlvs(TLV_TYPE_FILE_PATH)
138
139
if response.has_tlv?(TLV_TYPE_STAT_BUF)
140
sbuf = response.get_tlvs(TLV_TYPE_STAT_BUF)
141
else
142
sbuf = response.get_tlvs(TLV_TYPE_STAT_BUF32)
143
new_stat_buf = false
144
end
145
146
unless fpath && sbuf
147
return []
148
end
149
150
fpath.each_with_index do |file_name, idx|
151
is_dir = false
152
if sbuf[idx]
153
st = ::Rex::Post::FileStat.new
154
if new_stat_buf
155
st.update(sbuf[idx].value)
156
else
157
st.update32(sbuf[idx].value)
158
end
159
is_dir = st.ftype == 'directory'
160
next if (match_dir && !is_dir) # if file_name isn't directory
161
end
162
163
if !file_name.value.end_with?('.', '\\', '/') # Exclude current and parent directory
164
name = client.unicode_filter_encode(file_name.value)
165
if is_dir
166
name += client.fs.file.separator
167
end
168
files << name
169
end
170
end
171
172
files
173
end
174
175
##
176
#
177
# General directory operations
178
#
179
##
180
181
#
182
# Changes the working directory of the remote process.
183
#
184
def Dir.chdir(path)
185
request = Packet.create_request(COMMAND_ID_STDAPI_FS_CHDIR)
186
187
request.add_tlv(TLV_TYPE_DIRECTORY_PATH, client.unicode_filter_decode( path ))
188
189
client.send_request(request)
190
191
getwd(refresh: true)
192
return 0
193
end
194
195
#
196
# Creates a directory.
197
#
198
def Dir.mkdir(path)
199
request = Packet.create_request(COMMAND_ID_STDAPI_FS_MKDIR)
200
201
request.add_tlv(TLV_TYPE_DIRECTORY_PATH, client.unicode_filter_decode( path ))
202
203
client.send_request(request)
204
205
return 0
206
end
207
208
#
209
# Returns the current working directory of the remote process.
210
#
211
def Dir.pwd(refresh: true)
212
if @working_directory.nil? || refresh
213
request = Packet.create_request(COMMAND_ID_STDAPI_FS_GETWD)
214
215
response = client.send_request(request)
216
217
@working_directory = client.unicode_filter_encode(response.get_tlv(TLV_TYPE_DIRECTORY_PATH).value)
218
end
219
@working_directory
220
end
221
222
#
223
# Synonym for pwd.
224
#
225
def Dir.getwd(refresh: true)
226
pwd(refresh: refresh)
227
end
228
229
#
230
# Removes the supplied directory if it's empty.
231
#
232
def Dir.delete(path)
233
request = Packet.create_request(COMMAND_ID_STDAPI_FS_DELETE_DIR)
234
235
request.add_tlv(TLV_TYPE_DIRECTORY_PATH, client.unicode_filter_decode( path ))
236
237
client.send_request(request)
238
239
return 0
240
end
241
242
#
243
# Synonyms for delete.
244
#
245
def Dir.rmdir(path)
246
delete(path)
247
end
248
249
#
250
# Synonyms for delete.
251
#
252
def Dir.unlink(path)
253
delete(path)
254
end
255
256
##
257
#
258
# Directory mirroring
259
#
260
##
261
262
#
263
# Downloads the contents of a remote directory a
264
# local directory, optionally in a recursive fashion.
265
#
266
def Dir.download(dst, src, opts = {}, force = true, glob = nil, &stat)
267
src.force_encoding('UTF-8')
268
dst.force_encoding('UTF-8')
269
tries_cnt = 0
270
271
continue = opts["continue"]
272
recursive = opts["recursive"]
273
timestamp = opts["timestamp"]
274
tries_no = opts["tries_no"] || 0
275
tries = opts["tries"]
276
277
begin
278
dir_files = self.entries(src, glob)
279
rescue Rex::TimeoutError
280
if (tries && (tries_no == 0 || tries_cnt < tries_no))
281
tries_cnt += 1
282
stat.call('error listing - retry #', tries_cnt, src) if (stat)
283
retry
284
else
285
stat.call('error listing directory - giving up', src, dst) if (stat)
286
raise
287
end
288
end
289
290
dir_files.each { |src_sub|
291
src_sub.force_encoding('UTF-8')
292
dst_sub = src_sub.dup
293
dst_sub.gsub!(::File::SEPARATOR, '_') # '/' on all systems
294
dst_sub.gsub!(::File::ALT_SEPARATOR, '_') if ::File::ALT_SEPARATOR # nil on Linux, '\' on Windows
295
296
dst_item = ::File.join(dst, client.unicode_filter_encode(dst_sub))
297
src_item = src + client.fs.file.separator + client.unicode_filter_encode(src_sub)
298
299
if (src_sub == '.' or src_sub == '..')
300
next
301
end
302
303
tries_cnt = 0
304
begin
305
src_stat = client.fs.filestat.new(src_item)
306
rescue Rex::TimeoutError
307
if (tries && (tries_no == 0 || tries_cnt < tries_no))
308
tries_cnt += 1
309
stat.call('error opening file - retry #', tries_cnt, src_item) if (stat)
310
retry
311
else
312
stat.call('error opening file - giving up', tries_cnt, src_item) if (stat)
313
raise
314
end
315
end
316
317
if (src_stat.file?)
318
if timestamp
319
dst_item << timestamp
320
end
321
322
stat.call('downloading', src_item, dst_item) if (stat)
323
324
begin
325
if (continue || tries) # allow to file.download to log messages
326
result = client.fs.file.download_file(dst_item, src_item, opts, &stat)
327
else
328
result = client.fs.file.download_file(dst_item, src_item, opts)
329
end
330
stat.call(result, src_item, dst_item) if (stat)
331
rescue ::Rex::Post::Meterpreter::RequestError => e
332
if force
333
stat.call('failed', src_item, dst_item) if (stat)
334
else
335
raise e
336
end
337
end
338
339
elsif (src_stat.directory?)
340
if (recursive == false)
341
next
342
end
343
344
begin
345
::Dir.mkdir(dst_item)
346
rescue
347
end
348
349
stat.call('mirroring', src_item, dst_item) if (stat)
350
download(dst_item, src_item, opts, force, glob, &stat)
351
stat.call('mirrored', src_item, dst_item) if (stat)
352
end
353
} # entries
354
end
355
356
#
357
# Uploads the contents of a local directory to a remote
358
# directory, optionally in a recursive fashion.
359
#
360
def Dir.upload(dst, src, recursive = false, &stat)
361
::Dir.entries(src).each { |src_sub|
362
dst_item = dst + client.fs.file.separator + client.unicode_filter_encode(src_sub)
363
src_item = src + ::File::SEPARATOR + client.unicode_filter_encode(src_sub)
364
365
if (src_sub == '.' or src_sub == '..')
366
next
367
end
368
369
src_stat = ::File.stat(src_item)
370
371
if (src_stat.file?)
372
stat.call('uploading', src_item, dst_item) if (stat)
373
client.fs.file.upload(dst_item, src_item)
374
stat.call('uploaded', src_item, dst_item) if (stat)
375
elsif (src_stat.directory?)
376
if (recursive == false)
377
next
378
end
379
380
begin
381
self.mkdir(dst_item)
382
rescue
383
end
384
385
stat.call('mirroring', src_item, dst_item) if (stat)
386
upload(dst_item, src_item, recursive, &stat)
387
stat.call('mirrored', src_item, dst_item) if (stat)
388
end
389
}
390
end
391
392
#
393
# The path of the directory that was opened.
394
#
395
attr_reader :path
396
protected
397
attr_accessor :client # :nodoc:
398
attr_writer :path # :nodoc:
399
400
end
401
402
end; end; end; end; end; end
403
404
405