module MetasploitPayloads

This module dispenses Metasploit payload binary files

Constants

EXTENSION_PREFIX
METERPRETER_SUBFOLDER
USER_DATA_SUBFOLDER
VERSION

Public Class Methods

data_directory() click to toggle source

Full path to the local gem folder containing the base data

# File lib/metasploit-payloads.rb, line 183
def self.data_directory
  ::File.realpath(::File.join(::File.dirname(__FILE__), '..', 'data'))
end
expand(root_dir, file_name) click to toggle source

Expand the given root path and file name into a full file location.

# File lib/metasploit-payloads.rb, line 258
def self.expand(root_dir, file_name)
  ::File.expand_path(::File.join(root_dir, file_name))
end
list_meterpreter_extension_suffixes(extension_name=nil) click to toggle source

List all the available suffixes, optionally filtered by the given extension name. This is mostly useful for determining support for a specific extension.

@param [String] extension_name An optional extension name to use for filtering results. If omitted, all suffixes

will be returned.

@return [Array<String>] Returns an array of binary suffixes.

# File lib/metasploit-payloads.rb, line 176
def self.list_meterpreter_extension_suffixes(extension_name=nil)
  list_meterpreter_dirs { |dir| meterpreter_enum_ext_suffixes(dir, extension_name) }
end
list_meterpreter_extensions(binary_suffix=nil) click to toggle source

List all the available extensions, optionally filtered by the given suffix.

@param [String] binary_suffix An optional suffix to use for filtering results. If omitted, all extensions will be

returned.

@return [Array<String>] Returns an array of extensions.

# File lib/metasploit-payloads.rb, line 165
def self.list_meterpreter_extensions(binary_suffix=nil)
  list_meterpreter_dirs { |dir| meterpreter_enum_ext(dir, binary_suffix) }
end
local_meterpreter_dir() click to toggle source

Full path to the local gem folder which contains the meterpreter binaries.

# File lib/metasploit-payloads.rb, line 204
def self.local_meterpreter_dir
  ::File.join(data_directory, METERPRETER_SUBFOLDER)
end
manifest_errors() click to toggle source

@return [Array<Hash<String, Symbol>>] An array of filenames with warnings. Provides a file name and error.

Empty if all needed Meterpreter files exist and have the correct hash.
# File lib/metasploit-payloads.rb, line 18
def self.manifest_errors
  manifest_errors = []

  begin
    manifest_contents = ::File.binread(manifest_path)
  rescue => e
    return [{ path: manifest_path, error: e }]
  end

  begin
    manifest_uuid_contents = ::File.binread(manifest_uuid_path)
  rescue => e
    manifest_errors.append({ path: manifest_uuid_path, error: e })
  end

  # Check if the hash of the manifest file is correct.
  if manifest_uuid_contents
    manifest_digest = ::OpenSSL::Digest.new('SHA3-256', manifest_contents)
    uuid_matches = (manifest_uuid_contents.chomp == manifest_digest.to_s)
    unless uuid_matches
      e = ::MetasploitPayloads::HashMismatchError.new(manifest_path)
      manifest_errors.append({ path: manifest_path, error: e })
    end
  end

  manifest_contents.each_line do |line|
    filename, hash_type, hash = line.chomp.split(':')
    begin
      filename = filename.sub('./data/', '')
      # self.path prepends the gem data directory, which is already present in the manifest file.
      out_path = self.path(filename)
      # self.path can return a path to the gem data, or user's local data.
      bundled_file = out_path.start_with?(data_directory)
      if bundled_file
        file_hash_match = (::OpenSSL::Digest.new(hash_type, ::File.binread(out_path)).to_s == hash)
        unless file_hash_match
          e = ::MetasploitPayloads::HashMismatchError.new(out_path)
          manifest_errors.append({ path: e.path, error: e })
        end
      end
    rescue ::MetasploitPayloads::NotFoundError, ::MetasploitPayloads::NotReadableError => e
      manifest_errors.append({ path: e.path, error: e })
    end
  end

  manifest_errors
end
metasploit_installed?() click to toggle source

Determine if MSF has been installed and is being used.

# File lib/metasploit-payloads.rb, line 251
def self.metasploit_installed?
  defined? Msf::Config
end
meterpreter_enum_ext(root_dir, binary_suffix=nil) click to toggle source

Enumerate extensions in the given root folder based on an optional suffix.

@param [String] root_dir The path to the directory from which to enumerate extensions. @param [String] binary_suffix An optional suffix to use for filtering results. If omitted, all extensions will be

returned.

@return [Array<String>] Returns an array of extensions.

# File lib/metasploit-payloads.rb, line 215
def self.meterpreter_enum_ext(root_dir, binary_suffix=nil)
  exts = []
  binary_suffix ||= '.*'
  ::Dir.entries(root_dir).each do |f|
    if ::File.readable?(::File.join(root_dir, f)) && \
       f =~ /#{EXTENSION_PREFIX}(\w+)\.#{binary_suffix}/
      exts.push($1)
    end
  end
  exts
end
meterpreter_enum_ext_suffixes(root_dir, extension_name=nil) click to toggle source

Enumerate binary suffixes in the given root folder based on an optional extension name.

@param [String] root_dir The path to the directory from which to enumerate extension suffixes. @param [String] extension_name An optional extension name to use for filtering results. If omitted, all suffixes will

be returned.

@return [Array<String>] Returns an array of binary suffixes.

# File lib/metasploit-payloads.rb, line 234
def self.meterpreter_enum_ext_suffixes(root_dir, extension_name=nil)
  suffixes = []
  extension_name ||= '\w+'
  ::Dir.entries(root_dir).each do |f|
    if ::File.readable?(::File.join(root_dir, f)) && \
       f =~ /#{EXTENSION_PREFIX}#{extension_name}\.(\w+(\.\w+)*)/
      suffixes.push($1)
    end
  end
  suffixes
end
meterpreter_ext_path(ext_name, binary_suffix) click to toggle source

Get the path to an extension based on its name (no prefix).

# File lib/metasploit-payloads.rb, line 69
def self.meterpreter_ext_path(ext_name, binary_suffix)
  path(METERPRETER_SUBFOLDER, "#{EXTENSION_PREFIX}#{ext_name}.#{binary_suffix}")
end
meterpreter_path(name, binary_suffix, debug: false) click to toggle source

Get the path to a meterpreter binary by full name.

@param [String] name The name of the requested binary without any file extensions @param [String] binary_suffix The binary extension, without the leading ‘.’ char (e.g. ‘php`, `jar`) @param [Boolean] debug Request a debug version of the binary. This adds a

leading '.debug' to the extension if looking for a DLL file.
# File lib/metasploit-payloads.rb, line 111
def self.meterpreter_path(name, binary_suffix, debug: false)
  binary_suffix = binary_suffix&.gsub(/dll$/, 'debug.dll') if debug
  path(METERPRETER_SUBFOLDER, "#{name}.#{binary_suffix}".downcase)
end
msf_meterpreter_dir() click to toggle source

Full path to the MSF data folder which contains the meterpreter binaries.

# File lib/metasploit-payloads.rb, line 190
def self.msf_meterpreter_dir
  ::File.join(Msf::Config.data_directory, METERPRETER_SUBFOLDER)
end
path(*path_parts) click to toggle source

Get the full path to any file packaged in this gem or other Metasploit Framework directories by local path and name.

@param [Array<String>] path_parts requested path parts that will be joined @raise [NotFoundError] if the requested path/file does not exist @raise [NotReadableError] if the requested file exists but the user doesn’t have read permissions @return [String,nil] A path or nil

# File lib/metasploit-payloads.rb, line 123
def self.path(*path_parts)
  gem_path = expand(data_directory, ::File.join(path_parts))
  if metasploit_installed?
    user_path = expand(Msf::Config.config_directory, ::File.join(USER_DATA_SUBFOLDER, path_parts))
    msf_path = expand(Msf::Config.data_directory, ::File.join(path_parts))
    out_path = readable_path(gem_path, user_path, msf_path)
  else
    out_path = readable_path(gem_path)
  end

  return out_path unless out_path.nil?
  raise ::MetasploitPayloads::NotFoundError, ::File.join(gem_path), caller unless ::File.exist?(gem_path)

  nil
end
read(*path_parts) click to toggle source

Get the contents of any file packaged in this gem by local path and name. If the file is encrypted using ChaCha20, automatically decrypt it and return the file contents.

# File lib/metasploit-payloads.rb, line 143
def self.read(*path_parts)
  file_path = self.path(path_parts)

  begin
    file_contents = ::File.binread(file_path)
  rescue ::Errno::ENOENT => _e
    raise ::MetasploitPayloads::NotFoundError, file_path, caller
  rescue ::Errno::EACCES => _e
    raise ::MetasploitPayloads::NotReadableError, file_path, caller
  rescue ::StandardError => e
    raise e
  end

  Crypto.decrypt(ciphertext: file_contents)
end
readable_path(gem_path, *extra_paths) click to toggle source

Get the path for the first readable path in the provided arguments. Start with the provided ‘extra_paths` then fall back to the `gem_path`.

@param [String] gem_path a path to the gem @param [Array<String>] extra_paths a path to any extra paths that should be evaluated for local files before ‘gem_path` @raise [NotReadableError] if the user doesn’t have read permissions for the currently-evaluated path @return [String,nil] A readable path or nil

# File lib/metasploit-payloads.rb, line 81
def self.readable_path(gem_path, *extra_paths)
  # Try the MSF path first to see if the file exists, allowing the MSF data
  # folder to override what is in the gem. This is very helpful for
  # testing/development without having to move the binaries to the gem folder
  # each time. We only do this is MSF is installed.
  extra_paths.each do |extra_path|
    if ::File.readable? extra_path
      warn_local_path(extra_path)
      return extra_path
    else
      # Raise rather than falling back;
      # If there is a local file present, let's assume that the user wants to use it (e.g. local dev. changes)
      # rather than having MSF Console falling back to the files in the gem
      raise ::MetasploitPayloads::NotReadableError, extra_path, caller if ::File.exist?(extra_path)
    end
  end

  return gem_path if ::File.readable? gem_path
  raise ::MetasploitPayloads::NotReadableError, gem_path, caller if ::File.exist?(gem_path)

  nil
end
user_meterpreter_dir() click to toggle source

Full path to the user’s MSF data folder which contains the meterpreter binaries.

# File lib/metasploit-payloads.rb, line 197
def self.user_meterpreter_dir
  ::File.join(Msf::Config.config_directory, USER_DATA_SUBFOLDER, METERPRETER_SUBFOLDER)
end
version() click to toggle source
# File lib/metasploit-payloads/version.rb, line 4
def self.version
  VERSION
end
warn_local_path(path) click to toggle source
# File lib/metasploit-payloads.rb, line 264
def self.warn_local_path(path)
  STDERR.puts("WARNING: Local file #{path} is being used")
  STDERR.puts('WARNING: Local files may be incompatible with the Metasploit Framework') if @local_paths.empty?
  @local_paths << path
  @local_paths.uniq!
end

Private Class Methods

list_meterpreter_dirs() { |dir).each do |e| push| ... } click to toggle source
# File lib/metasploit-payloads.rb, line 273
def list_meterpreter_dirs(&block)
  things = [] # *things* is whatever is being enumerated (extension names, suffixes, etc.) as determined by the block
  root_dirs = [local_meterpreter_dir]

  # Find the valid extensions in the data folder first, if MSF
  # is installed.
  if metasploit_installed?
    root_dirs.unshift(msf_meterpreter_dir)
    root_dirs.unshift(user_meterpreter_dir)
  end

  root_dirs.each do |dir|
    next unless ::File.directory?(dir)

    # Merge in any that don't already exist in the collection.
    (yield dir).each do |e|
      things.push(e) unless things.include?(e)
    end
  end

Private Instance Methods

manifest_path() click to toggle source
# File lib/metasploit-payloads.rb, line 296
def manifest_path
  ::File.realpath(::File.join(::File.dirname(__FILE__), '..', 'manifest'))
end
manifest_uuid_path() click to toggle source
# File lib/metasploit-payloads.rb, line 300
def manifest_uuid_path
  ::File.realpath(::File.join(::File.dirname(__FILE__), '..', 'manifest.uuid'))
end