Skip to content

Commit 7aef0f2

Browse files
committed
Per MS-2916, load Mettle extensions via new API.
1 parent 60111ad commit 7aef0f2

File tree

4 files changed

+77
-44
lines changed

4 files changed

+77
-44
lines changed

lib/rex/post/meterpreter/client_core.rb

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,10 @@ def set_transport_timeouts(opts={})
194194
# LibraryFilePath
195195
# The path to the library that is to be loaded
196196
#
197+
# LibraryFileImage
198+
# Binary object containing the library to be loaded
199+
# (can be used instead of LibraryFilePath)
200+
#
197201
# TargetFilePath
198202
# The target library path when uploading
199203
#
@@ -209,12 +213,13 @@ def set_transport_timeouts(opts={})
209213
#
210214
def load_library(opts)
211215
library_path = opts['LibraryFilePath']
216+
library_image = opts['LibraryFileImage']
212217
target_path = opts['TargetFilePath']
213218
load_flags = LOAD_LIBRARY_FLAG_LOCAL
214219

215220
# No library path, no cookie.
216-
if library_path.nil?
217-
raise ArgumentError, 'No library file path was supplied', caller
221+
if library_path.nil? && library_image.nil?
222+
raise ArgumentError, 'No library file path or image was supplied', caller
218223
end
219224

220225
# Set up the proper loading flags
@@ -233,14 +238,17 @@ def load_library(opts)
233238

234239
# If we must upload the library, do so now
235240
if (load_flags & LOAD_LIBRARY_FLAG_LOCAL) != LOAD_LIBRARY_FLAG_LOCAL
236-
image = ''
241+
if library_image.nil?
242+
# Caller did not provide the image, load it from the path
243+
library_image = ''
237244

238-
::File.open(library_path, 'rb') { |f|
239-
image = f.read
240-
}
245+
::File.open(library_path, 'rb') { |f|
246+
library_image = f.read
247+
}
248+
end
241249

242-
if image
243-
request.add_tlv(TLV_TYPE_DATA, image, false, client.capabilities[:zlib])
250+
if library_image
251+
request.add_tlv(TLV_TYPE_DATA, library_image, false, client.capabilities[:zlib])
244252
else
245253
raise RuntimeError, "Failed to serialize library #{library_path}.", caller
246254
end
@@ -328,22 +336,31 @@ def use(mod, opts = { })
328336
# if there are existing commands for the given extension, then we can use
329337
# what's already there
330338
unless commands.length > 0
331-
# Get us to the installation root and then into data/meterpreter, where
332-
# the file is expected to be
333-
modname = "ext_server_#{mod.downcase}"
334-
path = MetasploitPayloads.meterpreter_path(modname, suffix)
339+
image = nil
340+
path = nil
341+
# If client.sys isn't setup, it's a Windows meterpreter
342+
if client.respond_to?(:sys) && !client.sys.config.sysinfo['BuildTuple'].blank?
343+
# Query the payload gem directly for the extension image
344+
image = MetasploitPayloads::Mettle.load_extension(client.sys.config.sysinfo['BuildTuple'], mod.downcase, suffix)
345+
else
346+
# Get us to the installation root and then into data/meterpreter, where
347+
# the file is expected to be
348+
modname = "ext_server_#{mod.downcase}"
349+
path = MetasploitPayloads.meterpreter_path(modname, suffix)
335350

336-
if opts['ExtensionPath']
337-
path = ::File.expand_path(opts['ExtensionPath'])
351+
if opts['ExtensionPath']
352+
path = ::File.expand_path(opts['ExtensionPath'])
353+
end
338354
end
339355

340-
if path.nil?
356+
if path.nil? and image.nil?
341357
raise RuntimeError, "No module of the name #{modnameprovided} found", caller
342358
end
343359

344360
# Load the extension DLL
345361
commands = load_library(
346362
'LibraryFilePath' => path,
363+
'LibraryFileImage' => image,
347364
'UploadLibrary' => true,
348365
'Extension' => true,
349366
'SaveToDisk' => opts['LoadFromDisk'])

lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ def sysinfo(refresh: false)
122122
'Computer' => response.get_tlv_value(TLV_TYPE_COMPUTER_NAME),
123123
'OS' => response.get_tlv_value(TLV_TYPE_OS_NAME),
124124
'Architecture' => response.get_tlv_value(TLV_TYPE_ARCHITECTURE),
125+
'BuildTuple' => response.get_tlv_value(TLV_TYPE_BUILD_TUPLE),
125126
'System Language' => response.get_tlv_value(TLV_TYPE_LANG_SYSTEM),
126127
'Domain' => response.get_tlv_value(TLV_TYPE_DOMAIN),
127128
'Logged On Users' => response.get_tlv_value(TLV_TYPE_LOGGED_ON_USER_COUNT)

lib/rex/post/meterpreter/extensions/stdapi/tlv.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ module Stdapi
130130
TLV_TYPE_DOMAIN = TLV_META_TYPE_STRING | 1046
131131
TLV_TYPE_LOGGED_ON_USER_COUNT = TLV_META_TYPE_UINT | 1047
132132
TLV_TYPE_LOCAL_DATETIME = TLV_META_TYPE_STRING | 1048
133+
TLV_TYPE_BUILD_TUPLE = TLV_META_TYPE_STRING | 1049
133134

134135
# Environment
135136
TLV_TYPE_ENV_VARIABLE = TLV_META_TYPE_STRING | 1100

lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,22 +1147,27 @@ def cmd_load(*args)
11471147
case opt
11481148
when '-l'
11491149
exts = SortedSet.new
1150-
msf_path = MetasploitPayloads.msf_meterpreter_dir
1151-
gem_path = MetasploitPayloads.local_meterpreter_dir
1152-
[msf_path, gem_path].each do |path|
1153-
::Dir.entries(path).each { |f|
1154-
if (::File.file?(::File.join(path, f)))
1155-
client.binary_suffix.each { |s|
1156-
if (f =~ /ext_server_(.*)\.#{s}/ )
1157-
if (client.binary_suffix.size > 1)
1158-
exts.add($1 + ".#{s}")
1159-
else
1160-
exts.add($1)
1150+
if !client.sys.config.sysinfo['BuildTuple'].blank?
1151+
# Use API to get list of extensions from the gem
1152+
exts.merge(MetasploitPayloads::Mettle.available_extensions(client.sys.config.sysinfo['BuildTuple']))
1153+
else
1154+
msf_path = MetasploitPayloads.msf_meterpreter_dir
1155+
gem_path = MetasploitPayloads.local_meterpreter_dir
1156+
[msf_path, gem_path].each do |path|
1157+
::Dir.entries(path).each { |f|
1158+
if (::File.file?(::File.join(path, f)))
1159+
client.binary_suffix.each { |s|
1160+
if (f =~ /ext_server_(.*)\.#{s}/ )
1161+
if (client.binary_suffix.size > 1)
1162+
exts.add($1 + ".#{s}")
1163+
else
1164+
exts.add($1)
1165+
end
11611166
end
1162-
end
1163-
}
1164-
end
1165-
}
1167+
}
1168+
end
1169+
}
1170+
end
11661171
end
11671172
print(exts.to_a.join("\n") + "\n")
11681173

@@ -1212,22 +1217,31 @@ def cmd_load(*args)
12121217

12131218
def cmd_load_tabs(str, words)
12141219
tabs = SortedSet.new
1215-
msf_path = MetasploitPayloads.msf_meterpreter_dir
1216-
gem_path = MetasploitPayloads.local_meterpreter_dir
1217-
[msf_path, gem_path].each do |path|
1218-
::Dir.entries(path).each { |f|
1219-
if (::File.file?(::File.join(path, f)))
1220-
client.binary_suffix.each { |s|
1221-
if (f =~ /ext_server_(.*)\.#{s}/ )
1222-
if (client.binary_suffix.size > 1 && !extensions.include?($1 + ".#{s}"))
1223-
tabs.add($1 + ".#{s}")
1224-
elsif (!extensions.include?($1))
1225-
tabs.add($1)
1220+
if !client.sys.config.sysinfo['BuildTuple'].blank?
1221+
# Use API to get list of extensions from the gem
1222+
MetasploitPayloads::Mettle.available_extensions(client.sys.config.sysinfo['BuildTuple']).each { |f|
1223+
if !extensions.include?(f.split('.').first)
1224+
tabs.add(f)
1225+
end
1226+
}
1227+
else
1228+
msf_path = MetasploitPayloads.msf_meterpreter_dir
1229+
gem_path = MetasploitPayloads.local_meterpreter_dir
1230+
[msf_path, gem_path].each do |path|
1231+
::Dir.entries(path).each { |f|
1232+
if (::File.file?(::File.join(path, f)))
1233+
client.binary_suffix.each { |s|
1234+
if (f =~ /ext_server_(.*)\.#{s}/ )
1235+
if (client.binary_suffix.size > 1 && !extensions.include?($1 + ".#{s}"))
1236+
tabs.add($1 + ".#{s}")
1237+
elsif (!extensions.include?($1))
1238+
tabs.add($1)
1239+
end
12261240
end
1227-
end
1228-
}
1241+
}
1242+
end
1243+
}
12291244
end
1230-
}
12311245
end
12321246
return tabs.to_a
12331247
end

0 commit comments

Comments
 (0)