Skip to content

Commit 9f144ce

Browse files
committed
Land rapid7#9151, mettle extension support + sniffer module
2 parents 3d76c36 + 2a94a44 commit 9f144ce

File tree

50 files changed

+215
-96
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+215
-96
lines changed

Gemfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ PATH
1919
metasploit-model
2020
metasploit-payloads (= 1.3.19)
2121
metasploit_data_models
22-
metasploit_payloads-mettle (= 0.2.8)
22+
metasploit_payloads-mettle (= 0.3.2)
2323
msgpack
2424
nessus_rest
2525
net-ssh
@@ -188,7 +188,7 @@ GEM
188188
postgres_ext
189189
railties (~> 4.2.6)
190190
recog (~> 2.0)
191-
metasploit_payloads-mettle (0.2.8)
191+
metasploit_payloads-mettle (0.3.2)
192192
method_source (0.9.0)
193193
mini_portile2 (2.3.0)
194194
minitest (5.10.3)

lib/msf/base/sessions/meterpreter.rb

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -635,24 +635,26 @@ def binary_suffix
635635
# Platform-agnostic archs go first
636636
case self.arch
637637
when 'java'
638-
'jar'
638+
['jar']
639639
when 'php'
640-
'php'
640+
['php']
641641
when 'python'
642-
'py'
642+
['py']
643643
else
644644
# otherwise we fall back to the platform
645645
case self.platform
646646
when 'windows'
647-
"#{self.arch}.dll"
647+
["#{self.arch}.dll"]
648648
when 'linux' , 'aix' , 'hpux' , 'irix' , 'unix'
649-
'lso'
649+
['bin', 'elf']
650+
when 'osx'
651+
['elf']
650652
when 'android', 'java'
651-
'jar'
653+
['jar']
652654
when 'php'
653-
'php'
655+
['php']
654656
when 'python'
655-
'py'
657+
['py']
656658
else
657659
nil
658660
end

lib/rex/post/meterpreter/client_core.rb

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ def set_transport_timeouts(opts={})
195195
# LibraryFilePath
196196
# The path to the library that is to be loaded
197197
#
198+
# LibraryFileImage
199+
# Binary object containing the library to be loaded
200+
# (can be used instead of LibraryFilePath)
201+
#
198202
# TargetFilePath
199203
# The target library path when uploading
200204
#
@@ -210,12 +214,13 @@ def set_transport_timeouts(opts={})
210214
#
211215
def load_library(opts)
212216
library_path = opts['LibraryFilePath']
217+
library_image = opts['LibraryFileImage']
213218
target_path = opts['TargetFilePath']
214219
load_flags = LOAD_LIBRARY_FLAG_LOCAL
215220

216221
# No library path, no cookie.
217-
if library_path.nil?
218-
raise ArgumentError, 'No library file path was supplied', caller
222+
if library_path.nil? && library_image.nil?
223+
raise ArgumentError, 'No library file path or image was supplied', caller
219224
end
220225

221226
# Set up the proper loading flags
@@ -234,14 +239,17 @@ def load_library(opts)
234239

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

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

243-
if image
244-
request.add_tlv(TLV_TYPE_DATA, image, false, client.capabilities[:zlib])
251+
if library_image
252+
request.add_tlv(TLV_TYPE_DATA, library_image, false, client.capabilities[:zlib])
245253
else
246254
raise RuntimeError, "Failed to serialize library #{library_path}.", caller
247255
end
@@ -250,8 +258,17 @@ def load_library(opts)
250258
# path of the local and target so that it gets loaded with a random
251259
# name
252260
if opts['Extension']
253-
library_path = "ext#{rand(1000000)}.#{client.binary_suffix}"
254-
target_path = library_path
261+
if client.binary_suffix and client.binary_suffix.size > 1
262+
m = /(.*)\.(.*)/.match(library_path)
263+
suffix = $2
264+
elsif client.binary_suffix.size == 1
265+
suffix = client.binary_suffix[0]
266+
else
267+
suffix = client.binary_suffix
268+
end
269+
270+
library_path = "ext#{rand(1000000)}.#{suffix}"
271+
target_path = "/tmp/#{library_path}"
255272
end
256273
end
257274

@@ -297,29 +314,54 @@ def use(mod, opts = { })
297314
raise RuntimeError, "No modules were specified", caller
298315
end
299316

317+
modnameprovided = mod
318+
suffix = nil
319+
if not client.binary_suffix
320+
suffix = ''
321+
elsif client.binary_suffix.size > 1
322+
client.binary_suffix.each { |s|
323+
if (mod =~ /(.*)\.#{s}/ )
324+
mod = $1
325+
suffix = s
326+
break
327+
end
328+
}
329+
else
330+
suffix = client.binary_suffix.first
331+
end
332+
300333
# Query the remote instance to see if commands for the extension are
301334
# already loaded
302335
commands = get_loaded_extension_commands(mod.downcase)
303336

304337
# if there are existing commands for the given extension, then we can use
305338
# what's already there
306339
unless commands.length > 0
307-
# Get us to the installation root and then into data/meterpreter, where
308-
# the file is expected to be
309-
modname = "ext_server_#{mod.downcase}"
310-
path = MetasploitPayloads.meterpreter_path(modname, client.binary_suffix)
340+
image = nil
341+
path = nil
342+
# If client.sys isn't setup, it's a Windows meterpreter
343+
if client.respond_to?(:sys) && !client.sys.config.sysinfo['BuildTuple'].blank?
344+
# Query the payload gem directly for the extension image
345+
image = MetasploitPayloads::Mettle.load_extension(client.sys.config.sysinfo['BuildTuple'], mod.downcase, suffix)
346+
else
347+
# Get us to the installation root and then into data/meterpreter, where
348+
# the file is expected to be
349+
modname = "ext_server_#{mod.downcase}"
350+
path = MetasploitPayloads.meterpreter_path(modname, suffix)
311351

312-
if opts['ExtensionPath']
313-
path = ::File.expand_path(opts['ExtensionPath'])
352+
if opts['ExtensionPath']
353+
path = ::File.expand_path(opts['ExtensionPath'])
354+
end
314355
end
315356

316-
if path.nil?
317-
raise RuntimeError, "No module of the name #{modname}.#{client.binary_suffix} found", caller
357+
if path.nil? and image.nil?
358+
raise RuntimeError, "No module of the name #{modnameprovided} found", caller
318359
end
319360

320361
# Load the extension DLL
321362
commands = load_library(
322363
'LibraryFilePath' => path,
364+
'LibraryFileImage' => image,
323365
'UploadLibrary' => true,
324366
'Extension' => true,
325367
'SaveToDisk' => opts['LoadFromDisk'])

lib/rex/post/meterpreter/extensions/priv/priv.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,19 @@ def getsystem( technique=0 )
4545

4646
elevator_name = Rex::Text.rand_text_alpha_lower( 6 )
4747

48-
elevator_path = MetasploitPayloads.meterpreter_path('elevator', client.binary_suffix)
48+
elevator_path = nil
49+
client.binary_suffix.each { |s|
50+
elevator_path = MetasploitPayloads.meterpreter_path('elevator', s)
51+
if !elevator_path.nil?
52+
break
53+
end
54+
}
4955
if elevator_path.nil?
50-
raise RuntimeError, "elevator.#{binary_suffix} not found", caller
56+
elevators = ""
57+
client.binary_suffix.each { |s|
58+
elevators << "elevator.#{s}, "
59+
}
60+
raise RuntimeError, "#{elevators.chomp(', ')} not found", caller
5161
end
5262

5363
elevator_data = ""

lib/rex/post/meterpreter/extensions/sniffer/sniffer.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,13 @@ def interfaces()
3939
response.each(TLV_TYPE_SNIFFER_INTERFACES) { |p|
4040
vals = p.tlvs.map{|x| x.value }
4141
iface = { }
42-
ikeys = %W{idx name description type mtu wireless usable dhcp}
42+
if vals.length == 8
43+
# Windows
44+
ikeys = %W{idx name description type mtu wireless usable dhcp}
45+
else
46+
# Mettle
47+
ikeys = %W{idx name description usable}
48+
end
4349
ikeys.each_index { |i| iface[ikeys[i]] = vals[i] }
4450
ifaces << iface
4551
}

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: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,14 +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)) && f =~ /ext_server_(.*)\.#{client.binary_suffix}/ )
1155-
exts.add($1)
1156-
end
1157-
}
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
1166+
end
1167+
}
1168+
end
1169+
}
1170+
end
11581171
end
11591172
print(exts.to_a.join("\n") + "\n")
11601173

@@ -1168,7 +1181,16 @@ def cmd_load(*args)
11681181
# Load each of the modules
11691182
args.each { |m|
11701183
md = m.downcase
1184+
modulenameprovided = md
11711185

1186+
if client.binary_suffix and client.binary_suffix.size > 1
1187+
client.binary_suffix.each { |s|
1188+
if (md =~ /(.*)\.#{s}/ )
1189+
md = $1
1190+
break
1191+
end
1192+
}
1193+
end
11721194
if (extensions.include?(md))
11731195
print_error("The '#{md}' extension has already been loaded.")
11741196
next
@@ -1178,7 +1200,7 @@ def cmd_load(*args)
11781200

11791201
begin
11801202
# Use the remote side, then load the client-side
1181-
if (client.core.use(md) == true)
1203+
if (client.core.use(modulenameprovided) == true)
11821204
add_extension_client(md)
11831205
end
11841206
rescue
@@ -1195,16 +1217,31 @@ def cmd_load(*args)
11951217

11961218
def cmd_load_tabs(str, words)
11971219
tabs = SortedSet.new
1198-
msf_path = MetasploitPayloads.msf_meterpreter_dir
1199-
gem_path = MetasploitPayloads.local_meterpreter_dir
1200-
[msf_path, gem_path].each do |path|
1201-
::Dir.entries(path).each { |f|
1202-
if (::File.file?(::File.join(path, f)) && f =~ /ext_server_(.*)\.#{client.binary_suffix}/ )
1203-
if (not extensions.include?($1))
1204-
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)
12051225
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
1240+
end
1241+
}
1242+
end
1243+
}
12061244
end
1207-
}
12081245
end
12091246
return tabs.to_a
12101247
end

0 commit comments

Comments
 (0)