Class: Rex::Post::Meterpreter::Ui::Console::CommandDispatcher::Stdapi::Fs

Inherits:
Object
  • Object
show all
Includes:
Rex::Post::Meterpreter::Ui::Console::CommandDispatcher
Defined in:
lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb

Overview

The file system portion of the standard API extension.

Constant Summary collapse

Klass =
Console::CommandDispatcher::Stdapi::Fs
@@download_opts =

Options for the download command.

Rex::Parser::Arguments.new(
"-h" => [ false, "Help banner." ],
"-r" => [ false, "Download recursively." ])
@@upload_opts =

Options for the upload command.

Rex::Parser::Arguments.new(
"-h" => [ false, "Help banner." ],
"-r" => [ false, "Upload recursively." ])

Instance Attribute Summary

Attributes included from Ui::Text::DispatcherShell::CommandDispatcher

#shell, #tab_complete_items

Instance Method Summary collapse

Methods included from Rex::Post::Meterpreter::Ui::Console::CommandDispatcher

check_hash, #client, #initialize, #log_error, #msf_loaded?, set_hash

Methods included from Ui::Text::DispatcherShell::CommandDispatcher

#cmd_help, #cmd_help_help, #cmd_help_tabs, #deprecated_cmd, #deprecated_commands, #deprecated_help, #help_to_s, #initialize, #print, #print_error, #print_good, #print_line, #print_status, #print_warning, #tab_complete_filenames, #update_prompt

Instance Method Details

#cmd_cat(*args) ⇒ Object

Reads the contents of a file and prints them to the screen.


156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 156

def cmd_cat(*args)
  if (args.length == 0)
    print_line("Usage: cat file")
    return true
  end

  if (client.fs.file.stat(args[0]).directory?)
    print_error("#{args[0]} is a directory")
  else
    fd = client.fs.file.new(args[0], "rb")
    begin
      until fd.eof?
        print(fd.read)
      end
    # EOFError is raised if file is empty, do nothing, just catch
    rescue EOFError
    end
    fd.close
  end

  true
end

#cmd_cd(*args) ⇒ Object

Change the working directory.


182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 182

def cmd_cd(*args)
  if (args.length == 0)
    print_line("Usage: cd directory")
    return true
  end
  if args[0] =~ /\%(\w*)\%/
    client.fs.dir.chdir(client.fs.file.expand_path(args[0].upcase))
  else
    client.fs.dir.chdir(args[0])
  end

  return true
end

#cmd_download(*args) ⇒ Object

Downloads a file or directory from the remote machine to the local machine.


255
256
257
258
259
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
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 255

def cmd_download(*args)
  if (args.empty? or args.include? "-h")
    cmd_download_help
    return true
  end

  recursive = false
  src_items = []
  last      = nil
  dest      = nil

  @@download_opts.parse(args) { |opt, idx, val|
    case opt
    when "-r"
      recursive = true
    when nil
      src_items << last if (last)
      last = val
    end
  }

  # No files given, nothing to do
  if not last
    cmd_download_help
    return true
  end

  # Source and destination will be the same
  if src_items.empty?
    src_items << last
    # Use the basename of the remote filename so we don't end up with
    # a file named c:\\boot.ini in linux
    dest = ::Rex::Post::Meterpreter::Extensions::Stdapi::Fs::File.basename(last)
  else
    dest = last
  end

  # Go through each source item and download them
  src_items.each { |src|
    stat = client.fs.file.stat(src)

    if (stat.directory?)
      client.fs.dir.download(dest, src, recursive, true) { |step, src, dst|
        print_status("#{step.ljust(11)}: #{src} -> #{dst}")
        client.framework.events.on_session_download(client, src, dest) if msf_loaded?
      }
    elsif (stat.file?)
      client.fs.file.download(dest, src) { |step, src, dst|
        print_status("#{step.ljust(11)}: #{src} -> #{dst}")
        client.framework.events.on_session_download(client, src, dest) if msf_loaded?
      }
    end
  }

  return true
end

#cmd_download_helpObject


244
245
246
247
248
249
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 244

def cmd_download_help
  print_line "Usage: download [options] src1 src2 src3 ... destination"
  print_line
  print_line "Downloads remote files and directories to the local machine."
  print_line @@download_opts.usage
end

#cmd_edit(*args) ⇒ Object

Downloads a file to a temporary file, spawns and editor, and then uploads the contents to the remote machine after completion.


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
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 316

def cmd_edit(*args)
  if (args.length == 0)
    print_line("Usage: edit file")
    return true
  end

  # Get a temporary file path
  meterp_temp = Tempfile.new('meterp')
  meterp_temp.binmode
  temp_path = meterp_temp.path

  begin
    # Download the remote file to the temporary file
    client.fs.file.download_file(temp_path, args[0])
  rescue RequestError => re
    # If the file doesn't exist, then it's okay.  Otherwise, throw the
    # error.
    if re.result != 2
      raise $!
    end
  end

  # Spawn the editor (default to vi)
  editor = Rex::Compat.getenv('EDITOR') || 'vi'

  # If it succeeds, upload it to the remote side.
  if (system("#{editor} #{temp_path}") == true)
    client.fs.file.upload_file(args[0], temp_path)
  end

  # Get rid of that pesky temporary file
  ::File.delete(temp_path) rescue nil
end

#cmd_lcd(*args) ⇒ Object

Change the local working directory.


199
200
201
202
203
204
205
206
207
208
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 199

def cmd_lcd(*args)
  if (args.length == 0)
    print_line("Usage: lcd directory")
    return true
  end

  ::Dir.chdir(args[0])

  return true
end

#cmd_lpwd(*args) ⇒ Object Also known as: cmd_getlwd

Display the local working directory.


353
354
355
356
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 353

def cmd_lpwd(*args)
  print_line(::Dir.pwd)
  return true
end

#cmd_ls(*args) ⇒ Object

Lists files

TODO: make this more useful


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
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 365

def cmd_ls(*args)
  path = args[0] || client.fs.dir.getwd
  tbl  = Rex::Ui::Text::Table.new(
    'Header'  => "Listing: #{path}",
    'SortIndex' => 4,
    'Columns' =>
      [
        'Mode',
        'Size',
        'Type',
        'Last modified',
        'Name',
      ])

  items = 0
  stat = client.fs.file.stat(path)
  if stat.directory?
    # Enumerate each item...
    # No need to sort as Table will do it for us
    client.fs.dir.entries_with_info(path).each { |p|

      tbl <<
        [
          p['StatBuf'] ? p['StatBuf'].prettymode : '',
          p['StatBuf'] ? p['StatBuf'].size       : '',
          p['StatBuf'] ? p['StatBuf'].ftype[0,3] : '',
          p['StatBuf'] ? p['StatBuf'].mtime      : '',
          p['FileName'] || 'unknown'
        ]

      items += 1
    }

    if (items > 0)
      print("\n" + tbl.to_s + "\n")
    else
      print_line("No entries exist in #{path}")
    end
  else
    print_line("#{stat.prettymode}  #{stat.size}  #{stat.ftype[0,3]}  #{stat.mtime}  #{path}")
  end

  return true
end

#cmd_mkdir(*args) ⇒ Object

Make one or more directory.


413
414
415
416
417
418
419
420
421
422
423
424
425
426
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 413

def cmd_mkdir(*args)
  if (args.length == 0)
    print_line("Usage: mkdir dir1 dir2 dir3 ...")
    return true
  end

  args.each { |dir|
    print_line("Creating directory: #{dir}")

    client.fs.dir.mkdir(dir)
  }

  return true
end

#cmd_mv(*args) ⇒ Object Also known as: cmd_move, cmd_rename

Move source to destination


229
230
231
232
233
234
235
236
237
238
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 229

def cmd_mv(*args)
        if (args.length < 2)
                print_line("Usage: mv oldfile newfile")
                return true
        end 

        client.fs.file.mv(args[0],args[1])

        return true
end

#cmd_pwd(*args) ⇒ Object Also known as: cmd_getwd

Display the working directory.


431
432
433
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 431

def cmd_pwd(*args)
  print_line(client.fs.dir.getwd)
end

#cmd_rm(*args) ⇒ Object Also known as: cmd_del

Delete the specified file.


213
214
215
216
217
218
219
220
221
222
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 213

def cmd_rm(*args)
  if (args.length == 0)
    print_line("Usage: rm file")
    return true
  end

  client.fs.file.rm(args[0])

  return true
end

#cmd_rmdir(*args) ⇒ Object

Removes one or more directory if it's empty.


440
441
442
443
444
445
446
447
448
449
450
451
452
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 440

def cmd_rmdir(*args)
  if (args.length == 0 or args.include?("-h"))
    print_line("Usage: rmdir dir1 dir2 dir3 ...")
    return true
  end

  args.each { |dir|
    print_line("Removing directory: #{dir}")
    client.fs.dir.rmdir(dir)
  }

  return true
end

#cmd_search(*args) ⇒ Object

Search for files.


102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 102

def cmd_search( *args )

  root    = nil
  glob    = nil
  recurse = true

  opts = Rex::Parser::Arguments.new(
    "-h" => [ false, "Help Banner." ],
    "-d" => [ true,  "The directory/drive to begin searching from. Leave empty to search all drives. (Default: #{root})" ],
    "-f" => [ true,  "The file pattern glob to search for. (e.g. *secret*.doc?)" ],
    "-r" => [ true,  "Recursivly search sub directories. (Default: #{recurse})" ]
  )

  opts.parse(args) { | opt, idx, val |
    case opt
      when "-h"
        print_line( "Usage: search [-d dir] [-r recurse] -f pattern" )
        print_line( "Search for files." )
        print_line( opts.usage )
        return
      when "-d"
        root = val
      when "-f"
        glob = val
      when "-r"
        recurse = false if( val =~ /^(f|n|0)/i )
    end
  }

  if( not glob )
    print_error( "You must specify a valid file glob to search for, e.g. >search -f *.doc" )
    return
  end

  files = client.fs.file.search( root, glob, recurse )

  if( not files.empty? )
    print_line( "Found #{files.length} result#{ files.length > 1 ? 's' : '' }..." )
    files.each do | file |
      if( file['size'] > 0 )
        print( "    #{file['path']}#{ file['path'].empty? ? '' : '\\' }#{file['name']} (#{file['size']} bytes)\n" )
      else
        print( "    #{file['path']}#{ file['path'].empty? ? '' : '\\' }#{file['name']}\n" )
      end
    end
  else
    print_line( "No files matching your search were found." )
  end

end

#cmd_upload(*args) ⇒ Object

Uploads a file or directory to the remote machine from the local machine.


465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 465

def cmd_upload(*args)
  if (args.empty? or args.include?("-h"))
    cmd_upload_help
    return true
  end

  recursive = false
  src_items = []
  last      = nil
  dest      = nil

  @@upload_opts.parse(args) { |opt, idx, val|
    case opt
      when "-r"
        recursive = true
      when nil
        if (last)
          src_items << last
        end

        last = val
    end
  }

  return true if not last

  # Source and destination will be the same
  src_items << last if src_items.empty?

  dest = last

  # Go through each source item and upload them
  src_items.each { |src|
    stat = ::File.stat(src)

    if (stat.directory?)
      client.fs.dir.upload(dest, src, recursive) { |step, src, dst|
        print_status("#{step.ljust(11)}: #{src} -> #{dst}")
        client.framework.events.on_session_upload(client, src, dest) if msf_loaded?
      }
    elsif (stat.file?)
      client.fs.file.upload(dest, src) { |step, src, dst|
        print_status("#{step.ljust(11)}: #{src} -> #{dst}")
        client.framework.events.on_session_upload(client, src, dest) if msf_loaded?
      }
    end
  }

  return true
end

#cmd_upload_helpObject


454
455
456
457
458
459
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 454

def cmd_upload_help
  print_line "Usage: upload [options] src1 src2 src3 ... destination"
  print_line
  print_line "Uploads local files and directories to the remote machine."
  print_line @@upload_opts.usage
end

#cmd_upload_tabs(str, words) ⇒ Object


516
517
518
519
520
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 516

def cmd_upload_tabs(str, words)
  return [] if words.length > 1

  tab_complete_filenames(str, words)
end

#commandsObject

List of supported commands.


37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 37

def commands
  all = {
    "cat"      => "Read the contents of a file to the screen",
    "cd"       => "Change directory",
    "del"      => "Delete the specified file",
    "download" => "Download a file or directory",
    "edit"     => "Edit a file",
    "getlwd"   => "Print local working directory",
    "getwd"    => "Print working directory",
    "lcd"      => "Change local working directory",
    "lpwd"     => "Print local working directory",
    "ls"       => "List files",
    "mkdir"    => "Make directory",
    "pwd"      => "Print working directory",
    "rm"       => "Delete the specified file",
    "mv"	   => "Move source to destination",
    "rmdir"    => "Remove directory",
    "search"   => "Search for files",
    "upload"   => "Upload a file or directory",
  }

  reqs = {
    "cat"      => [ ],
    "cd"       => [ "stdapi_fs_chdir" ],
    "del"      => [ "stdapi_fs_rm" ],
    "download" => [ ],
    "edit"     => [ ],
    "getlwd"   => [ ],
    "getwd"    => [ "stdapi_fs_getwd" ],
    "lcd"      => [ ],
    "lpwd"     => [ ],
    "ls"       => [ "stdapi_fs_stat", "stdapi_fs_ls" ],
    "mkdir"    => [ "stdapi_fs_mkdir" ],
    "pwd"      => [ "stdapi_fs_getwd" ],
    "rmdir"    => [ "stdapi_fs_delete_dir" ],
    "rm"       => [ "stdapi_fs_delete_file" ],
    "mv"       => [ "stdapi_fs_file_move" ],
    "search"   => [ "stdapi_fs_search" ],
    "upload"   => [ ],
  }

  all.delete_if do |cmd, desc|
    del = false
    reqs[cmd].each do |req|
      next if client.commands.include? req
      del = true
      break
    end

    del
  end

  all
end

#nameObject

Name for this dispatcher.


95
96
97
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb', line 95

def name
  "Stdapi: File system"
end