Skip to content

Commit 10d1f53

Browse files
authored
Merge pull request #19975 from dledda-r7/feat/split-stdapi
Split Stdapi
2 parents 3ed3b39 + 05894cc commit 10d1f53

34 files changed

+1199
-26
lines changed

Gemfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ PATH
4545
metasploit-concern
4646
metasploit-credential
4747
metasploit-model
48-
metasploit-payloads (= 2.0.221)
48+
metasploit-payloads (= 2.0.234)
4949
metasploit_data_models (>= 6.0.7)
5050
metasploit_payloads-mettle (= 1.0.45)
5151
mqtt
@@ -348,7 +348,7 @@ GEM
348348
drb
349349
mutex_m
350350
railties (~> 7.0)
351-
metasploit-payloads (2.0.221)
351+
metasploit-payloads (2.0.234)
352352
metasploit_data_models (6.0.9)
353353
activerecord (~> 7.0)
354354
activesupport (~> 7.0)

LICENSE_GEMS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ metasploit-concern, 5.0.5, "New BSD"
9898
metasploit-credential, 6.0.16, "New BSD"
9999
metasploit-framework, 6.4.92, "New BSD"
100100
metasploit-model, 5.0.4, "New BSD"
101-
metasploit-payloads, 2.0.221, "3-clause (or ""modified"") BSD"
101+
metasploit-payloads, 2.0.234, "3-clause (or ""modified"") BSD"
102102
metasploit_data_models, 6.0.9, "New BSD"
103103
metasploit_payloads-mettle, 1.0.45, "3-clause (or ""modified"") BSD"
104104
method_source, 1.1.0, MIT

lib/msf/base/sessions/meterpreter.rb

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,26 @@ def shell_init
137137

138138
end
139139

140+
def load_embedded_extensions
141+
142+
# First of all, let's see if we have stdapi.
143+
commands = self.core.get_loaded_extension_commands('stdapi')
144+
console.run_single("load stdapi") if commands.length > 0
145+
146+
return if self.platform != 'windows'
147+
148+
exts = Set.new
149+
exts.merge(binary_suffix.map { |suffix| MetasploitPayloads.list_meterpreter_extensions(suffix) }.flatten)
150+
exts = exts.sort.uniq
151+
152+
exts.each { |e|
153+
commands = self.core.get_loaded_extension_commands(e.downcase)
154+
if commands.length > 0 && !e.downcase.starts_with?('stdapi')
155+
console.run_single("load #{e.downcase}")
156+
end
157+
}
158+
end
159+
140160
def bootstrap(datastore = {}, handler = nil)
141161
session = self
142162

@@ -180,6 +200,12 @@ def bootstrap(datastore = {}, handler = nil)
180200
print_warning('Meterpreter start up operations have been aborted. Use the session at your own risk.')
181201
return nil
182202
end
203+
204+
original = console.disable_output
205+
console.disable_output = true
206+
207+
load_embedded_extensions
208+
183209
extensions = datastore['AutoLoadExtensions']&.delete(' ').split(',') || []
184210

185211
# BEGIN: This should be removed on MSF 7
@@ -192,8 +218,6 @@ def bootstrap(datastore = {}, handler = nil)
192218
extensions.push('android') if session.platform == 'android'
193219
extensions = extensions.uniq
194220
# END
195-
original = console.disable_output
196-
console.disable_output = true
197221
# TODO: abstract this a little, perhaps a "post load" function that removes
198222
# platform-specific stuff?
199223
extensions.each do |extension|

lib/rex/payloads/meterpreter/config.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,18 @@ def config_block
173173
file_extension = 'x86.dll'
174174
file_extension = 'x64.dll' unless is_x86?
175175

176+
@opts[:extensions] = [] if @opts[:extensions].blank?
177+
prev_length = @opts[:extensions].length
178+
@opts[:extensions] = @opts[:extensions].select {|ext_name| ext_name.index('stdapi_') != 0}
179+
if prev_length != @opts[:extensions].length
180+
raise Msf::OptionValidateError.new(
181+
{
182+
'EXTENSIONS' => 'The extensions starting with stdapi_* are currently not supported.'
183+
},
184+
message: "The extensions starting with stdapi_* are currently not supported."
185+
)
186+
end
187+
176188
(@opts[:extensions] || []).each do |e|
177189
config << extension_block(e, file_extension, debug_build: @opts[:debug_build])
178190
end

lib/rex/post/meterpreter/client_core.rb

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -346,9 +346,19 @@ def use(mod, opts = { })
346346
# already loaded
347347
commands = get_loaded_extension_commands(mod.downcase)
348348

349-
# if there are existing commands for the given extension, then we can use
350-
# what's already there
351-
unless commands.length > 0
349+
# This check is important to keep compatibility with Mettle:
350+
# Mettle is the only meterpreter that has stdapi functions embedded in the metsrv.
351+
# Double-loading of extensions is prevented by the lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb line: 1195
352+
# However we need to add more flexibility here to allow users loading the split of stdapi in Windows Meterpreter.
353+
# reference: https://github.com/rapid7/metasploit-framework/pull/19975
354+
# So here we are actively preventing loading of stdapi if some commands for that extension are already loaded.
355+
# So we will not load stdapi if:
356+
# - we are running mettle (which has by default those command registered)
357+
# - we are running windows meterpreter with one of the stdapi namespace loaded (stdapi_net, stdapi_fs.... etc).
358+
# partial loading of the other namespace is still possible, we can load stdapi_net and stdapi_fs later.
359+
360+
skip_loading = commands.length > 0 && !mod.downcase.start_with?('stdapi_')
361+
unless skip_loading
352362
image = nil
353363
path = nil
354364
# If client.sys isn't setup, it's a Windows meterpreter
@@ -393,7 +403,6 @@ def use(mod, opts = { })
393403
'Extension' => true,
394404
'SaveToDisk' => opts['LoadFromDisk'])
395405
end
396-
397406
# wire the commands into the client
398407
client.add_extension(mod, commands)
399408

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# -*- coding: binary -*-
2+
3+
require 'rex/post/meterpreter/object_aliases'
4+
require 'rex/post/meterpreter/extension'
5+
require 'rex/post/meterpreter/extensions/stdapi/constants'
6+
require 'rex/post/meterpreter/extensions/stdapi/tlv'
7+
require 'rex/post/meterpreter/extensions/stdapi/command_ids'
8+
require 'rex/post/meterpreter/extensions/stdapi/mic/mic'
9+
require 'rex/post/meterpreter/extensions/stdapi/audio_output/audio_output'
10+
11+
module Rex
12+
module Post
13+
module Meterpreter
14+
module Extensions
15+
module Stdapi_Audio
16+
module AudioOutput
17+
include Rex::Post::Meterpreter::Extensions::Stdapi::AudioOutput
18+
end
19+
module Mic
20+
include Rex::Post::Meterpreter::Extensions::Stdapi::Mic
21+
end
22+
include Rex::Post::Meterpreter::Extensions::Stdapi
23+
24+
class Stdapi_Audio < Extension
25+
26+
def self.extension_id
27+
Rex::Post::Meterpreter::Extensions::Stdapi::EXTENSION_ID_STDAPI
28+
end
29+
30+
#
31+
# Initializes an instance of the Standard API (Audio Namespace) extension.
32+
#
33+
def initialize(client)
34+
super(client, 'stdapi_audio')
35+
36+
# Alias the following things on the client object so that they
37+
# can be directly referenced
38+
client.register_extension_aliases(
39+
[
40+
{
41+
'name' => 'audio_output',
42+
'ext' => Rex::Post::Meterpreter::Extensions::Stdapi_Audio::AudioOutput::AudioOutput.new(client)
43+
},
44+
{
45+
'name' => 'mic',
46+
'ext' => Rex::Post::Meterpreter::Extensions::Stdapi_Audio::Mic::Mic.new(client)
47+
},
48+
])
49+
end
50+
51+
#
52+
# Sets the client instance on a duplicated copy of the supplied class.
53+
#
54+
def brand(klass)
55+
klass = klass.dup
56+
klass.client = self.client
57+
return klass
58+
end
59+
end
60+
end
61+
end
62+
end
63+
end
64+
end
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# -*- coding: binary -*-
2+
3+
require 'rex/post/meterpreter/object_aliases'
4+
require 'rex/post/meterpreter/extension'
5+
require 'rex/post/meterpreter/extensions/stdapi/constants'
6+
require 'rex/post/meterpreter/extensions/stdapi/stdapi'
7+
require 'rex/post/meterpreter/extensions/stdapi/tlv'
8+
require 'rex/post/meterpreter/extensions/stdapi/command_ids'
9+
require 'rex/post/meterpreter/extensions/stdapi/fs/dir'
10+
require 'rex/post/meterpreter/extensions/stdapi/fs/file'
11+
require 'rex/post/meterpreter/extensions/stdapi/fs/file_stat'
12+
require 'rex/post/meterpreter/extensions/stdapi/fs/mount'
13+
14+
module Rex
15+
module Post
16+
module Meterpreter
17+
module Extensions
18+
module Stdapi_Fs
19+
module Fs
20+
include Rex::Post::Meterpreter::Extensions::Stdapi::Fs
21+
end
22+
include Rex::Post::Meterpreter::Extensions::Stdapi
23+
24+
class Stdapi_Fs < Extension
25+
26+
def self.extension_id
27+
Rex::Post::Meterpreter::Extensions::Stdapi::EXTENSION_ID_STDAPI
28+
end
29+
30+
#
31+
# Initializes an instance of the Standard API (Fs Namespace) extension.
32+
#
33+
def initialize(client)
34+
super(client, 'stdapi_fs')
35+
36+
# Alias the following things on the client object so that they
37+
# can be directly referenced
38+
client.register_extension_aliases(
39+
[
40+
{
41+
'name' => 'fs',
42+
'ext' => ObjectAliases.new(
43+
{
44+
'dir' => self.dir,
45+
'file' => self.file,
46+
'filestat' => self.filestat,
47+
'mount' => Fs::Mount.new(client)
48+
})
49+
},
50+
])
51+
end
52+
53+
#
54+
# Sets the client instance on a duplicated copy of the supplied class.
55+
#
56+
def brand(klass)
57+
klass = klass.dup
58+
klass.client = self.client
59+
return klass
60+
end
61+
62+
#
63+
# Returns a copy of the Dir class.
64+
#
65+
def dir
66+
brand(Rex::Post::Meterpreter::Extensions::Stdapi_Fs::Fs::Dir)
67+
end
68+
69+
#
70+
# Returns a copy of the File class.
71+
#
72+
def file
73+
brand(Rex::Post::Meterpreter::Extensions::Stdapi_Fs::Fs::File)
74+
end
75+
76+
#
77+
# Returns a copy of the FileStat class.
78+
#
79+
def filestat
80+
brand(Rex::Post::Meterpreter::Extensions::Stdapi_Fs::Fs::FileStat)
81+
end
82+
end
83+
end
84+
end
85+
end
86+
end
87+
end
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# -*- coding: binary -*-
2+
3+
require 'rex/post/meterpreter/object_aliases'
4+
require 'rex/post/meterpreter/extension'
5+
require 'rex/post/meterpreter/extensions/stdapi/constants'
6+
require 'rex/post/meterpreter/extensions/stdapi/tlv'
7+
require 'rex/post/meterpreter/extensions/stdapi/command_ids'
8+
require 'rex/post/meterpreter/extensions/stdapi/net/resolve'
9+
require 'rex/post/meterpreter/extensions/stdapi/net/config'
10+
require 'rex/post/meterpreter/extensions/stdapi/net/socket'
11+
module Rex
12+
module Post
13+
module Meterpreter
14+
module Extensions
15+
module Stdapi_Net
16+
module Net
17+
include Rex::Post::Meterpreter::Extensions::Stdapi::Net
18+
end
19+
include Rex::Post::Meterpreter::Extensions::Stdapi
20+
class Stdapi_Net < Extension
21+
22+
def self.extension_id
23+
Rex::Post::Meterpreter::Extensions::Stdapi::EXTENSION_ID_STDAPI
24+
end
25+
26+
#
27+
# Initializes an instance of the Standard API (Net Namespace) extension.
28+
#
29+
def initialize(client)
30+
super(client, 'stdapi_net')
31+
32+
# Alias the following things on the client object so that they
33+
# can be directly referenced
34+
client.register_extension_aliases(
35+
[
36+
{
37+
'name' => 'net',
38+
'ext' => ObjectAliases.new(
39+
{
40+
'config' => Rex::Post::Meterpreter::Extensions::Stdapi_Net::Net::Config.new(client),
41+
'socket' => Rex::Post::Meterpreter::Extensions::Stdapi_Net::Net::Socket.new(client),
42+
'resolve' => Rex::Post::Meterpreter::Extensions::Stdapi_Net::Net::Resolve.new(client)
43+
})
44+
}
45+
])
46+
end
47+
48+
#
49+
# Sets the client instance on a duplicated copy of the supplied class.
50+
#
51+
def brand(klass)
52+
klass = klass.dup
53+
klass.client = self.client
54+
return klass
55+
end
56+
end
57+
end
58+
end
59+
end
60+
end
61+
end

0 commit comments

Comments
 (0)