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

The file system portion of the standard API extension.

Constants

Klass

Public Instance Methods

cmd_cat(*args) click to toggle source

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

# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 216
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) click to toggle source

Change the working directory.

# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 242
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_copy(*args)
Alias for: cmd_cp
cmd_cp(*args) click to toggle source

Move source to destination

# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 304
def cmd_cp(*args)
  if (args.length < 2)
    print_line("Usage: cp oldfile newfile")
    return true
  end
  client.fs.file.cp(args[0],args[1])
  return true
end
Also aliased as: cmd_copy
cmd_del(*args)
Alias for: cmd_rm
cmd_dir(*args)

Alias the ls command to dir, for those of us who have windows muscle-memory

Alias for: cmd_ls
cmd_download(*args) click to toggle source

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

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

  # Download to a directory, not a pattern
  if client.fs.file.is_glob?(dest)
    dest = ::File.dirname(dest)
  end

  # Go through each source item and download them
  src_items.each { |src|
    glob = nil
    if client.fs.file.is_glob?(src)
      glob = ::File.basename(src)
      src = ::File.dirname(src)
    end

    # Use search if possible for recursive pattern matching. It will work
    # more intuitively since it will not try to match on intermediate
    # directories, only file names.
    if glob && recursive && client.commands.include?('stdapi_fs_search')

      files = client.fs.file.search(src, glob, recursive)
      if !files.empty?
        print_line("Downloading #{files.length} file#{files.length > 1 ? 's' : ''}...")

        files.each do |file|
          src_separator = client.fs.file.separator
          src_path = file['path'] + client.fs.file.separator + file['name']
          dest_path = src_path.tr(src_separator, ::File::SEPARATOR)

          client.fs.file.download(dest_path, src_path) do |step, src, dst|
            puts step
            print_status("#{step.ljust(11)}: #{src} -> #{dst}")
            client.framework.events.on_session_download(client, src, dest) if msf_loaded?
          end
        end

      else
        print_status("No matching files found for download")
      end

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

  true
end
cmd_download_help() click to toggle source
# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 316
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) click to toggle source

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

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

  # Try to download the file, but don't worry if it doesn't exist
  client.fs.file.download_file(temp_path, args[0]) rescue nil

  # 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_getlwd(*args)
Alias for: cmd_lpwd
cmd_getwd(*args)
Alias for: cmd_pwd
cmd_lcd(*args) click to toggle source

Change the local working directory.

# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 259
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) click to toggle source

Display the local working directory.

# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 455
def cmd_lpwd(*args)
  print_line(::Dir.pwd)
  return true
end
Also aliased as: cmd_getlwd
cmd_ls(*args) click to toggle source

Lists files

# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 533
def cmd_ls(*args)
  # Set defaults
  path = client.fs.dir.getwd
  search_term = nil
  sort = 'Name'
  short = nil
  order = :forward
  recursive = nil

  # Parse the args
  @@ls_opts.parse(args) { |opt, idx, val|
    case opt
    # Sort options
    when '-s'
      sort = 'Size'
    when '-t'
      sort = 'Last modified'
    # Output options
    when '-x'
      short = true
    when '-l'
      short = nil
    when '-r'
      order = :reverse
    when '-R'
      recursive = true
    # Search
    when '-S'
      search_term = val
      if search_term.nil?
        print_error("Enter a search term")
        return true
      else
        search_term = /#{search_term}/nmi
      end
    # Help and path
    when "-h"
      cmd_ls_help
      return 0
    when nil
      path = val
    end
  }

  columns = [ 'Mode', 'Size', 'Type', 'Last modified', 'Name' ]
  columns.insert(4, 'Short Name') if short

  stat_path = path

  # Check session capabilities
  is_glob = client.fs.file.is_glob?(path)
  if is_glob
    if !client.commands.include?('stdapi_fs_search')
      print_line('File globbing not supported with this session')
      return
    end
    stat_path = ::File.dirname(path)
  end

  stat = client.fs.file.stat(stat_path)
  if stat.directory?
    list_path(path, columns, sort, order, short, recursive, 0, search_term)
  else
    print_line("#{stat.prettymode}  #{stat.size}  #{stat.ftype[0,3]}  #{stat.mtime}  #{path}")
  end

  return true
end
Also aliased as: cmd_dir
cmd_ls_help() click to toggle source
# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 463
def cmd_ls_help
  print_line "Usage: ls [options]"
  print_line
  print_line "Lists contents of directory or file info, searchable"
  print_line @@ls_opts.usage
end
cmd_mkdir(*args) click to toggle source

Make one or more directory.

# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 611
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_move(*args)
Alias for: cmd_mv
cmd_mv(*args) click to toggle source

Move source to destination

# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 289
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
Also aliased as: cmd_move, cmd_rename
cmd_pwd(*args) click to toggle source

Display the working directory.

# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 629
def cmd_pwd(*args)
  print_line(client.fs.dir.getwd)
end
Also aliased as: cmd_getwd
cmd_rename(*args)
Alias for: cmd_mv
cmd_rm(*args) click to toggle source

Delete the specified file.

# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 273
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
Also aliased as: cmd_del
cmd_rmdir(*args) click to toggle source

Removes one or more directory if it's empty.

# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 638
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_show_mount(*args) click to toggle source

Show all the mount points/logical drives (currently geared towards the Windows Meterpreter).

# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 177
def cmd_show_mount(*args)
  if args.include?('-h')
    print_line('Usage: show_mount')
    return true
  end

  mounts = client.fs.mount.show_mount

  table = Rex::Ui::Text::Table.new(
    'Header'    => 'Mounts / Drives',
    'Indent'    => 0,
    'SortIndex' => 0,
    'Columns'   => [
      'Name', 'Type', 'Size (Total)', 'Size (Free)', 'Mapped to'
    ]
  )

  mounts.each do |d|
    ts = ::Filesize.from("#{d[:total_space]} B").pretty.split(' ')
    fs = ::Filesize.from("#{d[:free_space]} B").pretty.split(' ')
    table << [
      d[:name],
      d[:type],
      "#{ts[0].rjust(6)} #{ts[1].ljust(3)}",
      "#{fs[0].rjust(6)} #{fs[1].ljust(3)}",
      d[:unc]
    ]
  end

  print_line
  print_line(table.to_s)
  print_line
  print_line("Total mounts/drives: #{mounts.length}")
  print_line
end
cmd_upload(*args) click to toggle source

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

# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 663
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?)
      if client.fs.file.exists?(dest) and client.fs.file.stat(dest).directory?
        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?
        }
      else
        client.fs.file.upload_file(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
    end
  }

  return true
end
cmd_upload_help() click to toggle source
# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 652
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) click to toggle source
# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 721
def cmd_upload_tabs(str, words)
  return [] if words.length > 1

  tab_complete_filenames(str, words)
end
commands() click to toggle source

List of supported commands.

# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 49
def commands
  all = {
    'cat'        => 'Read the contents of a file to the screen',
    'cd'         => 'Change directory',
    'del'        => 'Delete the specified file',
    'dir'        => 'List files (alias for ls)',
    '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',
    'show_mount' => 'List all mount points/logical drives',
  }

  reqs = {
    'cat'        => [],
    'cd'         => ['stdapi_fs_chdir'],
    'del'        => ['stdapi_fs_rm'],
    'dir'        => ['stdapi_fs_stat', 'stdapi_fs_ls'],
    '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'     => [],
    'show_mount' => ['stdapi_fs_mount_show'],
  }

  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
list_path(path, columns, sort, order, short, recursive = false, depth = 0, search_term = nil) click to toggle source
# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 470
def list_path(path, columns, sort, order, short, recursive = false, depth = 0, search_term = nil)

  # avoid infinite recursion
  if depth > 100
    return
  end

  tbl = Rex::Ui::Text::Table.new(
    'Header'  => "Listing: #{path}",
    'SortIndex' => columns.index(sort),
    'SortOrder' => order,
    'Columns' => columns,
    'SearchTerm' => search_term)

  items = 0

  # Enumerate each item...
  # No need to sort as Table will do it for us
  client.fs.dir.entries_with_info(path).each do |p|

    ffstat = p['StatBuf']
    fname = p['FileName'] || 'unknown'

    row = [
        ffstat ? ffstat.prettymode : '',
        ffstat ? ffstat.size       : '',
        ffstat ? ffstat.ftype[0,3] : '',
        ffstat ? ffstat.mtime      : '',
        fname
      ]
    row.insert(4, p['FileShortName'] || '') if short

    if fname != '.' && fname != '..'
      if row.join(' ') =~ /#{search_term}/
        tbl << row
        items += 1
      end

      if recursive && ffstat && ffstat.directory?
        if client.fs.file.is_glob?(path)
          child_path = ::File.dirname(path) + ::File::SEPARATOR + fname
          child_path += ::File::SEPARATOR + ::File.basename(path)
        else
          child_path = path + ::File::SEPARATOR + fname
        end
        begin
          list_path(child_path, columns, sort, order, short, recursive, depth + 1, search_term)
        rescue RequestError
        end
      end
    end
  end

  if items > 0
    print_line(tbl.to_s)
  else
    print_line("No entries exist in #{path}")
  end
end
name() click to toggle source

Name for this dispatcher.

# File lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb, line 111
def name
  "Stdapi: File system"
end