Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/msf/core/exploit/java.rb
Views: 11784
# -*- coding: binary -*-1###2#3# This mixin provides methods for interacting with a JDK installation to perform4# functions such as dynamic compilation and jar signing.5#6# Dependencies:7# - JDK68# - rjb (rjb.rubyforge.org)9# - the $JAVA_HOME variable must point to the JDK10#11# Nathan Keltner <[email protected]>12#13###1415module Msf16module Exploit::Java1718def initialize(info = {})19super2021register_advanced_options(22[23OptString.new( 'JavaCache', [true, 'Java cache location',24File.join(Msf::Config.config_directory, "javacache")]),25OptString.new( 'AddClassPath', [false, 'Additional java classpath', nil]),26], self.class)2728begin29require 'rjb'30@rjb_loaded = true31init_jvm32rescue ::Exception => e33@rjb_loaded = false34@jvm_init = false35@java_error = e36end37end3839def init_jvm(jvmoptions = nil)40if (not ENV['JAVA_HOME'])41raise RuntimeError, 'JAVA_HOME is not set'42end4344toolsjar = File.join(ENV['JAVA_HOME'], "lib", "tools.jar")45if (not File.exist? toolsjar)46raise RuntimeError, 'JAVA_HOME does not point to a valid JDK installation.'47end4849# Instantiate the JVM with a classpath pointing to the JDK tools.jar50# and our javatoolkit jar.51classpath = File.join(Msf::Config.data_directory, "exploits", "msfJavaToolkit.jar")52classpath += ":" + toolsjar53classpath += ":" + datastore['ADDCLASSPATH'] if datastore['ADDCLASSPATH']5455Rjb::load(classpath, jvmargs=[])5657@jvm_init = true58end5960def query_jvm61return @jvmInit62end6364def save_to_file(classnames, codez, location)65path = File.join( Msf::Config.install_root, "external", "source", location )6667if not File.exist? path68Dir.mkdir(path)69end7071i = 072classnames.each { |fil|73file = File.join( path, fil + ".java")74fp = File.open( file, "wb" )75print_status "Writing #{fil} to " + file76fp.puts codez[i]77i += 178fp.close79}80end8182def compile(classnames, codez, compile_options=nil)83if !@rjb_loaded or !@jvm_init84raise RuntimeError, "Could not load rjb and/or the JVM: " + @java_error.to_s85end8687if !compile_options.is_a?(Array) && compile_options88raise RuntimeError, "Compiler options must be of type Array."89end9091compile_options = [] if compile_options.nil?9293# Create the directory if it doesn't exist94Dir.mkdir(datastore['JavaCache']) if !File.exist? datastore['JavaCache']9596# For compatibility, some exploits need to have the target and source version97# set to a previous JRE version.98std_compiler_opts = [ "-target", "1.3", "-source", "1.3", "-d", datastore['JavaCache'] ]99100compile_options += std_compiler_opts101102java_compiler_klass = Rjb::import('javaCompile.CompileSourceInMemory')103104# If we were passed arrays105if classnames.class == [].class && codez.class == [].class106# default compile class107begin108# Same as java_compiler_klass.CompileFromMemory( String[] classnames,109# String[] codez, String[] compilerOptions)110success = java_compiler_klass._invoke('CompileFromMemory',111# Signature explained: [ means array, Lpath.to.object; means object112# Thus, this reads as call the method with 3 String[] args.113'[Ljava.lang.String;[Ljava.lang.String;[Ljava.lang.String;',114classnames, codez, compile_options)115rescue Exception => e116print_error "Received unknown error: " + e117end118else119raise RuntimeError, "The Java mixin received unknown argument-type combinations and cannot continue."120end121if !success122raise RuntimeError, "Compile failed."123end124end125126def build_jar(output_jar, in_files)127if output_jar.class != "".class || in_files.class != [].class128raise RuntimeError, "Building a jar requires an output_jar and an Array of in_files."129end130131# Add paths132in_files = in_files.map { |file| File.join(datastore['JavaCache'], file) }133134create_jar_klass = Rjb::import('javaCompile.CreateJarFile')135file_class = Rjb::import('java.io.File')136137file_out_jar = file_class.new_with_sig('Ljava.lang.String;', File.join(datastore['JavaCache'], output_jar) )138files_in = Array.new139140in_files.each { |file| files_in << file_class.new_with_sig('Ljava.lang.String;', file) }141create_jar_klass._invoke('createJarArchive', 'Ljava.io.File;[Ljava.io.File;', file_out_jar, files_in)142end143144#145# http://www.defcon.org/images/defcon-17/dc-17-presentations/defcon-17-valsmith-metaphish.pdf146#147def sign_jar(cert_cn, unsiged_jar, signed_jar, cert_alias="signFiles", msf_keystore="msfkeystore",148msf_store_pass="msfstorepass", msf_key_pass="msfkeypass")149150# Dependent on $JAVA_HOME/lib/tools.jar that comes with the JDK.151signer_klass = Rjb::import('javaCompile.SignJar')152153# Check if the keystore exists from previous run. If it does, delete it.154msf_keystore = File.join(datastore['JavaCache'], msf_keystore)155File.delete msf_keystore if File.exist? msf_keystore156157# Rjb pukes on a CN with a comma in it so bad that it crashes to shell158# and turns input echoing off. Simple fix for this ugly bug is159# just to get rid of commas which kinda sucks but whatever. See #1543.160keytool_opts = [161"-genkey", "-alias", cert_alias, "-keystore", msf_keystore,162"-storepass", msf_store_pass, "-dname", "CN=#{cert_cn.gsub(",",'')}",163"-keypass", "msfkeypass"164]165166# Build the cert keystore167signer_klass._invoke('KeyToolMSF','[Ljava.lang.String;',keytool_opts)168169jarsigner_opts = [170"-keystore", msf_keystore, "-storepass", msf_store_pass,171"-keypass", msf_key_pass, "-signedJar",172File.join(datastore['JavaCache'], signed_jar), # Signed Jar173File.join(datastore['JavaCache'], unsiged_jar), # Input Jar we're signing174cert_alias # The cert we're using175]176signer_klass._invoke('JarSignerMSF','[Ljava.lang.String;',jarsigner_opts)177178# There are warnings in the source for KeyTool/JarSigner warning that security providers179# are not released, and if you are calling .main(foo) from another app, you need to release180# them manually. This is not done here, and should Rjb be used for anything in the future,181# this may need to be cleaned up.182end183184#185# Create a Java-natively-serialized object for use in Ruby186#187# @param jar [String] Buffer containing JAR data from which to extract the class188# @param ser_class [String] The class name to be serialized189#190# @return [String] Marshalled serialized byteArray191def serialized_class_from_jar(jar, ser_class)192file_name = Rex::Text.rand_text_alpha_lower(8)193file_path = datastore['JavaCache'] + "/#{file_name}.jar"194::File.open(file_path, 'wb+') {|f| f.write(jar)}195::Rjb::add_jar(file_path)196::File.unlink(file_path)197payClass = ::Rjb::import(ser_class)198byteArrayClass = ::Rjb::import("java.io.ByteArrayOutputStream")199outputClass = ::Rjb::import("java.io.ObjectOutputStream")200payInst = payClass.new()201byteArrayInst = byteArrayClass.new()202outputInst = outputClass.new(byteArrayInst)203begin204serResult = outputInst.writeObject(payInst)205rescue => e206# Rjb exceptions are pretty broken - try to inform the user of where we keeled207print_error("Failed to Rjb-serialize the #{ser_class} class due to #{e}")208raise e209end210byteArrayInst.toByteArray()211end212end213end214215216