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:


388
389
390
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 388

def client
  @client
end

#pathObject

The path of the directory that was opened.


386
387
388
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 386

def path
  @path
end

Class Method Details

.chdir(path) ⇒ Object

Changes the working directory of the remote process.


178
179
180
181
182
183
184
185
186
187
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 178

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.


226
227
228
229
230
231
232
233
234
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 226

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.


260
261
262
263
264
265
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
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 260

def Dir.download(dst, src, opts = {}, force = true, glob = nil, &stat)
  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|
    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])
      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.


219
220
221
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 219

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

.match(name, dir = false) ⇒ Object

Enumerates all of the files and folders matched with name. When option 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
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 127

def Dir.match(name, 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|
    if dir && sbuf[idx]
      st = ::Rex::Post::FileStat.new
      if new_stat_buf
        st.update(sbuf[idx].value)
      else
        st.update32(sbuf[idx].value)
      end
      next if st.ftype != 'directory' # if file_name isn't directory
    end

    if !file_name.value.end_with?('.', '\\', '/') # Exclude current and parent directory
      files << client.unicode_filter_encode(file_name.value)
    end
  end

  files
end

.mkdir(path) ⇒ Object

Creates a directory.


192
193
194
195
196
197
198
199
200
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 192

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.


205
206
207
208
209
210
211
212
213
214
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 205

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.


239
240
241
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 239

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

Synonyms for delete.


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

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.


351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
# File 'lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb', line 351

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