CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/core/exploit/java.rb
Views: 11784
1
# -*- coding: binary -*-
2
###
3
#
4
# This mixin provides methods for interacting with a JDK installation to perform
5
# functions such as dynamic compilation and jar signing.
6
#
7
# Dependencies:
8
# - JDK6
9
# - rjb (rjb.rubyforge.org)
10
# - the $JAVA_HOME variable must point to the JDK
11
#
12
# Nathan Keltner <[email protected]>
13
#
14
###
15
16
module Msf
17
module Exploit::Java
18
19
def initialize(info = {})
20
super
21
22
register_advanced_options(
23
[
24
OptString.new( 'JavaCache', [true, 'Java cache location',
25
File.join(Msf::Config.config_directory, "javacache")]),
26
OptString.new( 'AddClassPath', [false, 'Additional java classpath', nil]),
27
], self.class)
28
29
begin
30
require 'rjb'
31
@rjb_loaded = true
32
init_jvm
33
rescue ::Exception => e
34
@rjb_loaded = false
35
@jvm_init = false
36
@java_error = e
37
end
38
end
39
40
def init_jvm(jvmoptions = nil)
41
if (not ENV['JAVA_HOME'])
42
raise RuntimeError, 'JAVA_HOME is not set'
43
end
44
45
toolsjar = File.join(ENV['JAVA_HOME'], "lib", "tools.jar")
46
if (not File.exist? toolsjar)
47
raise RuntimeError, 'JAVA_HOME does not point to a valid JDK installation.'
48
end
49
50
# Instantiate the JVM with a classpath pointing to the JDK tools.jar
51
# and our javatoolkit jar.
52
classpath = File.join(Msf::Config.data_directory, "exploits", "msfJavaToolkit.jar")
53
classpath += ":" + toolsjar
54
classpath += ":" + datastore['ADDCLASSPATH'] if datastore['ADDCLASSPATH']
55
56
Rjb::load(classpath, jvmargs=[])
57
58
@jvm_init = true
59
end
60
61
def query_jvm
62
return @jvmInit
63
end
64
65
def save_to_file(classnames, codez, location)
66
path = File.join( Msf::Config.install_root, "external", "source", location )
67
68
if not File.exist? path
69
Dir.mkdir(path)
70
end
71
72
i = 0
73
classnames.each { |fil|
74
file = File.join( path, fil + ".java")
75
fp = File.open( file, "wb" )
76
print_status "Writing #{fil} to " + file
77
fp.puts codez[i]
78
i += 1
79
fp.close
80
}
81
end
82
83
def compile(classnames, codez, compile_options=nil)
84
if !@rjb_loaded or !@jvm_init
85
raise RuntimeError, "Could not load rjb and/or the JVM: " + @java_error.to_s
86
end
87
88
if !compile_options.is_a?(Array) && compile_options
89
raise RuntimeError, "Compiler options must be of type Array."
90
end
91
92
compile_options = [] if compile_options.nil?
93
94
# Create the directory if it doesn't exist
95
Dir.mkdir(datastore['JavaCache']) if !File.exist? datastore['JavaCache']
96
97
# For compatibility, some exploits need to have the target and source version
98
# set to a previous JRE version.
99
std_compiler_opts = [ "-target", "1.3", "-source", "1.3", "-d", datastore['JavaCache'] ]
100
101
compile_options += std_compiler_opts
102
103
java_compiler_klass = Rjb::import('javaCompile.CompileSourceInMemory')
104
105
# If we were passed arrays
106
if classnames.class == [].class && codez.class == [].class
107
# default compile class
108
begin
109
# Same as java_compiler_klass.CompileFromMemory( String[] classnames,
110
# String[] codez, String[] compilerOptions)
111
success = java_compiler_klass._invoke('CompileFromMemory',
112
# Signature explained: [ means array, Lpath.to.object; means object
113
# Thus, this reads as call the method with 3 String[] args.
114
'[Ljava.lang.String;[Ljava.lang.String;[Ljava.lang.String;',
115
classnames, codez, compile_options)
116
rescue Exception => e
117
print_error "Received unknown error: " + e
118
end
119
else
120
raise RuntimeError, "The Java mixin received unknown argument-type combinations and cannot continue."
121
end
122
if !success
123
raise RuntimeError, "Compile failed."
124
end
125
end
126
127
def build_jar(output_jar, in_files)
128
if output_jar.class != "".class || in_files.class != [].class
129
raise RuntimeError, "Building a jar requires an output_jar and an Array of in_files."
130
end
131
132
# Add paths
133
in_files = in_files.map { |file| File.join(datastore['JavaCache'], file) }
134
135
create_jar_klass = Rjb::import('javaCompile.CreateJarFile')
136
file_class = Rjb::import('java.io.File')
137
138
file_out_jar = file_class.new_with_sig('Ljava.lang.String;', File.join(datastore['JavaCache'], output_jar) )
139
files_in = Array.new
140
141
in_files.each { |file| files_in << file_class.new_with_sig('Ljava.lang.String;', file) }
142
create_jar_klass._invoke('createJarArchive', 'Ljava.io.File;[Ljava.io.File;', file_out_jar, files_in)
143
end
144
145
#
146
# http://www.defcon.org/images/defcon-17/dc-17-presentations/defcon-17-valsmith-metaphish.pdf
147
#
148
def sign_jar(cert_cn, unsiged_jar, signed_jar, cert_alias="signFiles", msf_keystore="msfkeystore",
149
msf_store_pass="msfstorepass", msf_key_pass="msfkeypass")
150
151
# Dependent on $JAVA_HOME/lib/tools.jar that comes with the JDK.
152
signer_klass = Rjb::import('javaCompile.SignJar')
153
154
# Check if the keystore exists from previous run. If it does, delete it.
155
msf_keystore = File.join(datastore['JavaCache'], msf_keystore)
156
File.delete msf_keystore if File.exist? msf_keystore
157
158
# Rjb pukes on a CN with a comma in it so bad that it crashes to shell
159
# and turns input echoing off. Simple fix for this ugly bug is
160
# just to get rid of commas which kinda sucks but whatever. See #1543.
161
keytool_opts = [
162
"-genkey", "-alias", cert_alias, "-keystore", msf_keystore,
163
"-storepass", msf_store_pass, "-dname", "CN=#{cert_cn.gsub(",",'')}",
164
"-keypass", "msfkeypass"
165
]
166
167
# Build the cert keystore
168
signer_klass._invoke('KeyToolMSF','[Ljava.lang.String;',keytool_opts)
169
170
jarsigner_opts = [
171
"-keystore", msf_keystore, "-storepass", msf_store_pass,
172
"-keypass", msf_key_pass, "-signedJar",
173
File.join(datastore['JavaCache'], signed_jar), # Signed Jar
174
File.join(datastore['JavaCache'], unsiged_jar), # Input Jar we're signing
175
cert_alias # The cert we're using
176
]
177
signer_klass._invoke('JarSignerMSF','[Ljava.lang.String;',jarsigner_opts)
178
179
# There are warnings in the source for KeyTool/JarSigner warning that security providers
180
# are not released, and if you are calling .main(foo) from another app, you need to release
181
# them manually. This is not done here, and should Rjb be used for anything in the future,
182
# this may need to be cleaned up.
183
end
184
185
#
186
# Create a Java-natively-serialized object for use in Ruby
187
#
188
# @param jar [String] Buffer containing JAR data from which to extract the class
189
# @param ser_class [String] The class name to be serialized
190
#
191
# @return [String] Marshalled serialized byteArray
192
def serialized_class_from_jar(jar, ser_class)
193
file_name = Rex::Text.rand_text_alpha_lower(8)
194
file_path = datastore['JavaCache'] + "/#{file_name}.jar"
195
::File.open(file_path, 'wb+') {|f| f.write(jar)}
196
::Rjb::add_jar(file_path)
197
::File.unlink(file_path)
198
payClass = ::Rjb::import(ser_class)
199
byteArrayClass = ::Rjb::import("java.io.ByteArrayOutputStream")
200
outputClass = ::Rjb::import("java.io.ObjectOutputStream")
201
payInst = payClass.new()
202
byteArrayInst = byteArrayClass.new()
203
outputInst = outputClass.new(byteArrayInst)
204
begin
205
serResult = outputInst.writeObject(payInst)
206
rescue => e
207
# Rjb exceptions are pretty broken - try to inform the user of where we keeled
208
print_error("Failed to Rjb-serialize the #{ser_class} class due to #{e}")
209
raise e
210
end
211
byteArrayInst.toByteArray()
212
end
213
end
214
end
215
216