173 lines
5.6 KiB
Ruby
173 lines
5.6 KiB
Ruby
###
|
|
#
|
|
# This mixn provides methods for interacting with a JDK installation to perform
|
|
# functions such as dynamic compilation and jar signing.
|
|
#
|
|
# Dependencies:
|
|
# - JDK6
|
|
# - rjb (rjb.rubyforge.org)
|
|
# - the $JAVA_HOME variable must point to the JDK
|
|
#
|
|
# Nathan Keltner <natron@metasploit.com>
|
|
#
|
|
###
|
|
|
|
require 'msf/core'
|
|
|
|
module Msf
|
|
module Exploit::Java
|
|
|
|
def initialize(info = {})
|
|
super
|
|
|
|
register_advanced_options(
|
|
[
|
|
OptString.new( 'JAVACACHE', [true, 'Java cache location',
|
|
File.join(Msf::Config.config_directory, "javacache")]),
|
|
OptString.new( 'ADDCLASSPATH', [false, 'Additional java classpath', nil]),
|
|
], self.class)
|
|
|
|
begin
|
|
require 'rjb'
|
|
@rjb_loaded = true
|
|
init_jvm
|
|
rescue ::Exception => e
|
|
@rjb_loaded = false
|
|
@jvm_init = false
|
|
@java_error = e
|
|
end
|
|
end
|
|
|
|
def init_jvm(jvmoptions=nil)
|
|
if (not ENV['JAVA_HOME'])
|
|
raise RuntimeError, 'Please set JAVA_HOME'
|
|
end
|
|
|
|
# Instantiate the JVM with a classpath pointing to the JDK tools.jar
|
|
# and our javatoolkit jar.
|
|
classpath = File.join(Msf::Config.install_root, "data", "exploits", "msfJavaToolkit.jar")
|
|
classpath += ":" + File.join(ENV['JAVA_HOME'], "lib", "tools.jar")
|
|
classpath += ":" + datastore['ADDCLASSPATH'] if datastore['ADDCLASSPATH']
|
|
|
|
Rjb::load(classpath, jvmargs=[])
|
|
|
|
@jvm_init = true
|
|
end
|
|
|
|
def query_jvm
|
|
return @jvmInit
|
|
end
|
|
|
|
def save_to_file(classnames, codez, location)
|
|
path = File.join( Msf::Config.install_root, "external", "source", location )
|
|
|
|
if not File.exists? path
|
|
Dir.mkdir(path)
|
|
end
|
|
|
|
i = 0
|
|
classnames.each { |fil|
|
|
file = File.join( path, fil + ".java")
|
|
fp = File.open( file, "w" )
|
|
print_status "Writing #{fil} to " + file
|
|
fp.puts codez[i]
|
|
i += 1
|
|
fp.close
|
|
}
|
|
end
|
|
|
|
def compile(classnames, codez, compile_options=nil)
|
|
if !@rjb_loaded or !@jvm_init
|
|
raise RuntimeError, "Could not load rjb and/or the JVM: " + @java_error.to_s
|
|
end
|
|
|
|
if compile_options.class.to_s != "Array" && compile_options
|
|
raise RuntimeError, "Compiler options must be of type Array."
|
|
end
|
|
|
|
compile_options = [] if compile_options.nil?
|
|
|
|
# Create the directory if it doesn't exist
|
|
Dir.mkdir(datastore['JAVACACHE']) if !File.exists? datastore['JAVACACHE']
|
|
|
|
# For compatibility, some exploits need to have the target and source version
|
|
# set to a previous JRE version.
|
|
std_compiler_opts = [ "-target", "1.3", "-source", "1.3", "-d", datastore['JAVACACHE'] ]
|
|
|
|
compile_options += std_compiler_opts
|
|
|
|
java_compiler_klass = Rjb::import('javaCompile.CompileSourceInMemory')
|
|
|
|
# If we were passed arrays
|
|
if classnames.class == [].class && codez.class == [].class
|
|
# default compile class
|
|
begin
|
|
# Sames as java_compiler_klass.CompileFromMemory( String[] classnames,
|
|
# String[] codez, String[] compilerOptions)
|
|
success = java_compiler_klass._invoke('CompileFromMemory',
|
|
# Signature explained: [ means array, Lpath.to.object; means object
|
|
# Thus, this reads as call the method with 3 String[] args.
|
|
'[Ljava.lang.String;[Ljava.lang.String;[Ljava.lang.String;',
|
|
classnames, codez, compile_options)
|
|
rescue Exception => e
|
|
print_error "Received unknown error: " + e
|
|
end
|
|
else
|
|
raise RuntimeError, "The Java mixin received unknown argument-type combinations and cannot continue."
|
|
end
|
|
if !success
|
|
raise RuntimeError, "Compile failed."
|
|
end
|
|
end
|
|
|
|
def build_jar(output_jar, in_files)
|
|
if output_jar.class != "".class || in_files.class != [].class
|
|
raise RuntimeError, "Building a jar requires an output_jar and an Array of in_files."
|
|
end
|
|
|
|
# Add paths
|
|
in_files = in_files.map { |file| File.join(datastore['JAVACACHE'], file) }
|
|
|
|
create_jar_klass = Rjb::import('javaCompile.CreateJarFile')
|
|
file_class = Rjb::import('java.io.File')
|
|
|
|
file_out_jar = file_class.new_with_sig('Ljava.lang.String;', File.join(datastore['JAVACACHE'], output_jar) )
|
|
files_in = Array.new
|
|
|
|
in_files.each { |file| files_in << file_class.new_with_sig('Ljava.lang.String;', file) }
|
|
create_jar_klass._invoke('createJarArchive', 'Ljava.io.File;[Ljava.io.File;', file_out_jar, files_in)
|
|
end
|
|
|
|
def sign_jar(cert_cn, unsiged_jar, signed_jar, cert_alias="signFiles", msf_keystore="msfkeystore",
|
|
msf_store_pass="msfstorepass", msf_key_pass="msfkeypass")
|
|
# Dependent on $JAVA_HOME/lib/tools.jar that comes with the JDK.
|
|
signer_klass = Rjb::import('javaCompile.SignJar')
|
|
|
|
# Check if the keystore exists from previous run. If it does, delete it.
|
|
msf_keystore = File.join(datastore['JAVACACHE'], msf_keystore)
|
|
File.delete msf_keystore if File.exists? msf_keystore
|
|
|
|
# http://www.defcon.org/images/defcon-17/dc-17-presentations/defcon-17-valsmith-metaphish.pdf
|
|
keytool_opts = ["-genkey", "-alias", cert_alias, "-keystore", msf_keystore,
|
|
"-storepass", msf_store_pass, "-dname", "CN=#{cert_cn}",
|
|
"-keypass", "msfkeypass"]
|
|
|
|
# Build the cert keystore
|
|
signer_klass._invoke('KeyToolMSF','[Ljava.lang.String;',keytool_opts)
|
|
|
|
jarsigner_opts = ["-keystore", msf_keystore, "-storepass", msf_store_pass,
|
|
"-keypass", msf_key_pass, "-signedJar",
|
|
File.join(datastore['JAVACACHE'], signed_jar), # Signed Jar
|
|
File.join(datastore['JAVACACHE'], unsiged_jar), # Input Jar we're signing
|
|
cert_alias] # The cert we're using
|
|
signer_klass._invoke('JarSignerMSF','[Ljava.lang.String;',jarsigner_opts)
|
|
|
|
# There are warnings in the source for KeyTool/JarSigner warning that security providers
|
|
# are not released, and if you are calling .main(foo) from another app, you need to release
|
|
# them manually. This is not done here, and should Rjb be used for anything in the future,
|
|
# this may need to be cleaned up.
|
|
end
|
|
|
|
end
|
|
end
|