Class: Rex::Post::Meterpreter::Extensions::Stdapi::Fs::Dir

Inherits:
Dir
  • Object
show all
Defined in:
lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb

Overview

This class implements directory operations against the remote endpoint. It implements the Rex::Post::Dir interface.

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Dir

foreach

Constructor Details

#initialize(path) ⇒ Dir

Initializes the directory instance.



34
35
36
37
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 34

def initialize(path)
  self.path   = path
  self.client = self.class.client
end

Class Attribute Details

.clientObject

Returns the value of attribute client.



22
23
24
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 22

def client
  @client
end

Instance Attribute Details

#clientObject (protected)

:nodoc:



397
398
399
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 397

def client
  @client
end

#pathObject

The path of the directory that was opened.



395
396
397
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 395

def path
  @path
end

Class Method Details

.chdir(path) ⇒ Object

Changes the working directory of the remote process.



184
185
186
187
188
189
190
191
192
193
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 184

def Dir.chdir(path)
  request = Packet.create_request(COMMAND_ID_STDAPI_FS_CHDIR)

  request.add_tlv(TLV_TYPE_DIRECTORY_PATH, client.unicode_filter_decode( path ))

  client.send_request(request)

  getwd(refresh: true)
  return 0
end

.delete(path) ⇒ Object

Removes the supplied directory if it’s empty.



232
233
234
235
236
237
238
239
240
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 232

def Dir.delete(path)
  request = Packet.create_request(COMMAND_ID_STDAPI_FS_DELETE_DIR)

  request.add_tlv(TLV_TYPE_DIRECTORY_PATH, client.unicode_filter_decode( path ))

  client.send_request(request)

  return 0
end

.download(dst, src, opts = {}, force = true, glob = nil, &stat) ⇒ Object

Downloads the contents of a remote directory a local directory, optionally in a recursive fashion.



266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 266

def Dir.download(dst, src, opts = {}, force = true, glob = nil, &stat)
  src.force_encoding('UTF-8')
  dst.force_encoding('UTF-8')
  tries_cnt = 0

  continue =  opts["continue"]
  recursive = opts["recursive"]
  timestamp = opts["timestamp"]
  tries_no = opts["tries_no"] || 0
  tries = opts["tries"]

  begin
    dir_files = self.entries(src, glob)
  rescue Rex::TimeoutError
    if (tries && (tries_no == 0 || tries_cnt < tries_no))
      tries_cnt += 1
      stat.call('error listing  - retry #', tries_cnt, src) if (stat)
      retry
    else
      stat.call('error listing directory - giving up', src, dst) if (stat)
      raise
    end
  end

  dir_files.each { |src_sub|
    src_sub.force_encoding('UTF-8')
    dst_sub = src_sub.dup
    dst_sub.gsub!(::File::SEPARATOR, '_')                                   # '/' on all systems
    dst_sub.gsub!(::File::ALT_SEPARATOR, '_') if ::File::ALT_SEPARATOR      # nil on Linux, '\' on Windows

    dst_item = ::File.join(dst, client.unicode_filter_encode(dst_sub))
    src_item = src + client.fs.file.separator + client.unicode_filter_encode(src_sub)

    if (src_sub == '.' or src_sub == '..')
      next
    end

    tries_cnt = 0
    begin
      src_stat = client.fs.filestat.new(src_item)
    rescue Rex::TimeoutError
      if (tries && (tries_no == 0 || tries_cnt < tries_no))
        tries_cnt += 1
        stat.call('error opening file - retry #', tries_cnt, src_item) if (stat)
        retry
      else
        stat.call('error opening file - giving up', tries_cnt, src_item) if (stat)
        raise
      end
    end

    if (src_stat.file?)
      if timestamp
        dst_item << timestamp
      end

      stat.call('downloading', src_item, dst_item) if (stat)

      begin
        if (continue || tries)  # allow to file.download to log messages
          result = client.fs.file.download_file(dst_item, src_item, opts, &stat)
        else
          result = client.fs.file.download_file(dst_item, src_item, opts)
        end
        stat.call(result, src_item, dst_item) if (stat)
      rescue ::Rex::Post::Meterpreter::RequestError => e
        if force
          stat.call('failed', src_item, dst_item) if (stat)
        else
          raise e
        end
      end

    elsif (src_stat.directory?)
      if (recursive == false)
        next
      end

      begin
        ::Dir.mkdir(dst_item)
      rescue
      end

      stat.call('mirroring', src_item, dst_item) if (stat)
      download(dst_item, src_item, opts, force, glob, &stat)
      stat.call('mirrored', src_item, dst_item) if (stat)
    end
  } # entries
end

.entries(name = getwd, glob = nil) ⇒ Object

Enumerates all of the files/folders in a given directory.



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 55

def Dir.entries(name = getwd, glob = nil)
  request = Packet.create_request(COMMAND_ID_STDAPI_FS_LS)
  files   = []
  name = name + ::File::SEPARATOR + glob if glob

  request.add_tlv(TLV_TYPE_DIRECTORY_PATH, client.unicode_filter_decode(name))

  response = client.send_request(request)

  response.each(TLV_TYPE_FILE_NAME) { |file_name|
    files << client.unicode_filter_encode(file_name.value)
  }

  return files
end

.entries_with_info(name = getwd) ⇒ Object

Enumerates files with a bit more information than the default entries.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 74

def Dir.entries_with_info(name = getwd)
  request = Packet.create_request(COMMAND_ID_STDAPI_FS_LS)
  files = []
  sbuf = nil
  new_stat_buf = true

  request.add_tlv(TLV_TYPE_DIRECTORY_PATH, client.unicode_filter_decode(name))

  response = client.send_request(request)

  fname = response.get_tlvs(TLV_TYPE_FILE_NAME)
  fsname = response.get_tlvs(TLV_TYPE_FILE_SHORT_NAME)
  fpath = response.get_tlvs(TLV_TYPE_FILE_PATH)

  if response.has_tlv?(TLV_TYPE_STAT_BUF)
    sbuf = response.get_tlvs(TLV_TYPE_STAT_BUF)
  else
    sbuf = response.get_tlvs(TLV_TYPE_STAT_BUF32)
    new_stat_buf = false
  end

  if (!fname or !sbuf)
    return []
  end

  fname.each_with_index { |file_name, idx|
    st = nil

    if sbuf[idx] && sbuf[idx].value.length > 0
      st = ::Rex::Post::FileStat.new
      if new_stat_buf
        st.update(sbuf[idx].value)
      else
        st.update32(sbuf[idx].value)
      end
    end

    files <<
      {
        'FileName' => client.unicode_filter_encode(file_name.value),
        'FilePath' => client.unicode_filter_encode(fpath[idx].value),
        'FileShortName' => fsname[idx] ? fsname[idx].value : nil,
        'StatBuf'  => st,
      }
  }

  return files
end

.getwd(refresh: true) ⇒ Object

Synonym for pwd.



225
226
227
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 225

def Dir.getwd(refresh: true)
  pwd(refresh: refresh)
end

.match(name, match_dir = false) ⇒ Object

Enumerates all of the files and folders matched with name. When option match_dir is true, return matched folders.



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 127

def Dir.match(name, match_dir = false)
  path  = name + '*'
  files = []
  sbuf = nil
  new_stat_buf = true

  request = Packet.create_request(COMMAND_ID_STDAPI_FS_LS)
  request.add_tlv(TLV_TYPE_DIRECTORY_PATH, client.unicode_filter_decode(path))
  response = client.send_request(request)

  fpath = response.get_tlvs(TLV_TYPE_FILE_PATH)

  if response.has_tlv?(TLV_TYPE_STAT_BUF)
    sbuf = response.get_tlvs(TLV_TYPE_STAT_BUF)
  else
    sbuf = response.get_tlvs(TLV_TYPE_STAT_BUF32)
    new_stat_buf = false
  end

  unless fpath && sbuf
    return []
  end

  fpath.each_with_index do |file_name, idx|
    is_dir = false
    if sbuf[idx]
      st = ::Rex::Post::FileStat.new
      if new_stat_buf
        st.update(sbuf[idx].value)
      else
        st.update32(sbuf[idx].value)
      end
      is_dir = st.ftype == 'directory'
      next if (match_dir && !is_dir) # if file_name isn't directory
    end

    if !file_name.value.end_with?('.', '\\', '/') # Exclude current and parent directory
      name = client.unicode_filter_encode(file_name.value)
      if is_dir
        name += client.fs.file.separator
      end
      files << name
    end
  end

  files
end

.mkdir(path) ⇒ Object

Creates a directory.



198
199
200
201
202
203
204
205
206
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 198

def Dir.mkdir(path)
  request = Packet.create_request(COMMAND_ID_STDAPI_FS_MKDIR)

  request.add_tlv(TLV_TYPE_DIRECTORY_PATH, client.unicode_filter_decode( path ))

  client.send_request(request)

  return 0
end

.pwd(refresh: true) ⇒ Object

Returns the current working directory of the remote process.



211
212
213
214
215
216
217
218
219
220
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 211

def Dir.pwd(refresh: true)
  if @working_directory.nil? || refresh
    request = Packet.create_request(COMMAND_ID_STDAPI_FS_GETWD)

    response = client.send_request(request)

    @working_directory = client.unicode_filter_encode(response.get_tlv(TLV_TYPE_DIRECTORY_PATH).value)
  end
  @working_directory
end

.rmdir(path) ⇒ Object

Synonyms for delete.



245
246
247
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 245

def Dir.rmdir(path)
  delete(path)
end

Synonyms for delete.



252
253
254
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 252

def Dir.unlink(path)
  delete(path)
end

.upload(dst, src, recursive = false, &stat) ⇒ Object

Uploads the contents of a local directory to a remote directory, optionally in a recursive fashion.



360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 360

def Dir.upload(dst, src, recursive = false, &stat)
  ::Dir.entries(src).each { |src_sub|
    dst_item = dst + client.fs.file.separator + client.unicode_filter_encode(src_sub)
    src_item = src + ::File::SEPARATOR + client.unicode_filter_encode(src_sub)

    if (src_sub == '.' or src_sub == '..')
      next
    end

    src_stat = ::File.stat(src_item)

    if (src_stat.file?)
      stat.call('uploading', src_item, dst_item) if (stat)
      client.fs.file.upload(dst_item, src_item)
      stat.call('uploaded', src_item, dst_item) if (stat)
    elsif (src_stat.directory?)
      if (recursive == false)
        next
      end

      begin
        self.mkdir(dst_item)
      rescue
      end

      stat.call('mirroring', src_item, dst_item) if (stat)
      upload(dst_item, src_item, recursive, &stat)
      stat.call('mirrored', src_item, dst_item) if (stat)
    end
  }
end

Instance Method Details

#each(&block) ⇒ Object

Enumerates all of the contents of the directory.



48
49
50
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 48

def each(&block)
  client.fs.dir.foreach(self.path, &block)
end