Module: MetasploitPayloads

Defined in:
lib/metasploit-payloads.rb,
lib/metasploit-payloads/error.rb,
lib/metasploit-payloads/crypto.rb,
lib/metasploit-payloads/version.rb

Overview

This module dispenses Metasploit payload binary files

Defined Under Namespace

Modules: Crypto Classes: Error, HashMismatchError, NotFoundError, NotReadableError

Constant Summary collapse

EXTENSION_PREFIX =
'ext_server_'
METERPRETER_SUBFOLDER =
'meterpreter'
USER_DATA_SUBFOLDER =
'payloads'
VERSION =
'2.0.188'

Class Method Summary collapse

Class Method Details

.data_directoryObject

Full path to the local gem folder containing the base data



184
185
186
# File 'lib/metasploit-payloads.rb', line 184

def self.data_directory
  ::File.realpath(::File.join(::File.dirname(__FILE__), '..', 'data'))
end

.list_meterpreter_extension_suffixes(extension_name = nil) ⇒ Array<String>

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

Parameters:

  • extension_name (String) (defaults to: nil)

    An optional extension name to use for filtering results. If omitted, all suffixes will be returned.

Returns:

  • (Array<String>)

    Returns an array of binary suffixes.



177
178
179
# File 'lib/metasploit-payloads.rb', line 177

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) ⇒ Array<String>

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

Parameters:

  • binary_suffix (String) (defaults to: nil)

    An optional suffix to use for filtering results. If omitted, all extensions will be returned.

Returns:

  • (Array<String>)

    Returns an array of extensions.



166
167
168
# File 'lib/metasploit-payloads.rb', line 166

def self.list_meterpreter_extensions(binary_suffix=nil)
  list_meterpreter_dirs { |dir| meterpreter_enum_ext(dir, binary_suffix) }
end

.local_meterpreter_dirObject

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



205
206
207
# File 'lib/metasploit-payloads.rb', line 205

def self.local_meterpreter_dir
  ::File.join(data_directory, METERPRETER_SUBFOLDER)
end

.manifest_errorsArray<Hash<String, Symbol>>

Returns An array of filenames with warnings. Provides a file name and error. Empty if all needed Meterpreter files exist and have the correct hash.

Returns:

  • (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.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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
# File 'lib/metasploit-payloads.rb', line 19

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

.meterpreter_enum_ext(root_dir, binary_suffix = nil) ⇒ Array<String>

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

Parameters:

  • root_dir (String)

    The path to the directory from which to enumerate extensions.

  • binary_suffix (String) (defaults to: nil)

    An optional suffix to use for filtering results. If omitted, all extensions will be returned.

Returns:

  • (Array<String>)

    Returns an array of extensions.



216
217
218
219
220
221
222
223
224
225
226
# File 'lib/metasploit-payloads.rb', line 216

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) ⇒ Array<String>

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

Parameters:

  • root_dir (String)

    The path to the directory from which to enumerate extension suffixes.

  • extension_name (String) (defaults to: nil)

    An optional extension name to use for filtering results. If omitted, all suffixes will be returned.

Returns:

  • (Array<String>)

    Returns an array of binary suffixes.



235
236
237
238
239
240
241
242
243
244
245
# File 'lib/metasploit-payloads.rb', line 235

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) ⇒ Object

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



70
71
72
# File 'lib/metasploit-payloads.rb', line 70

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) ⇒ Object

Get the path to a meterpreter binary by full name.

Parameters:

  • name (String)

    The name of the requested binary without any file extensions

  • binary_suffix (String)

    The binary extension, without the leading '.' char (e.g. 'php`, `jar`)

  • debug (Boolean) (defaults to: false)

    Request a debug version of the binary. This adds a leading '.debug' to the extension if looking for a DLL file.



112
113
114
115
# File 'lib/metasploit-payloads.rb', line 112

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_dirObject

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



191
192
193
# File 'lib/metasploit-payloads.rb', line 191

def self.msf_meterpreter_dir
  ::File.join(Msf::Config.data_directory, METERPRETER_SUBFOLDER)
end

.path(*path_parts) ⇒ String?

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

Parameters:

  • path_parts (Array<String>)

    requested path parts that will be joined

Returns:

  • (String, nil)

    A path or nil

Raises:

  • (NotFoundError)

    if the requested path/file does not exist

  • (NotReadableError)

    if the requested file exists but the user doesn't have read permissions



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/metasploit-payloads.rb', line 124

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) ⇒ Object

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.



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/metasploit-payloads.rb', line 144

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) ⇒ String?

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`.

Parameters:

  • gem_path (String)

    a path to the gem

  • extra_paths (Array<String>)

    a path to any extra paths that should be evaluated for local files before 'gem_path`

Returns:

  • (String, nil)

    A readable path or nil

Raises:

  • (NotReadableError)

    if the user doesn't have read permissions for the currently-evaluated path



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/metasploit-payloads.rb', line 82

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_dirObject

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



198
199
200
# File 'lib/metasploit-payloads.rb', line 198

def self.user_meterpreter_dir
  ::File.join(Msf::Config.config_directory, USER_DATA_SUBFOLDER, METERPRETER_SUBFOLDER)
end

.versionObject



5
6
7
# File 'lib/metasploit-payloads/version.rb', line 5

def self.version
  VERSION
end