|
@@ -37,36 +37,10 @@ public class SSH implements Protocol {
|
|
|
NONE, SERVER_VERSION, CLIENT_VERSION, KEX_INIT, NEW_KEYS, USERAUTH, CONNECTION, CHANNEL, TERMINAL_CMD, TERMINAL_ENTER, CLOSED
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Converts a byte[] to int
|
|
|
- *
|
|
|
- * @param bytes
|
|
|
- * that are converted
|
|
|
- * @return converted byte[] as int
|
|
|
- */
|
|
|
- private int byteToInt(byte[] bytes) {
|
|
|
- int convertedInteger = 0;
|
|
|
- for (int i = 0; i < bytes.length; i++) {
|
|
|
- convertedInteger <<= 8;
|
|
|
- convertedInteger |= bytes[i] & 0xFF;
|
|
|
- }
|
|
|
- return convertedInteger;
|
|
|
- }
|
|
|
-
|
|
|
- private String initSshType() {
|
|
|
- SecureRandom rnd = new SecureRandom();
|
|
|
- int majorVersion = rnd.nextInt(possibleSshTypes.length);
|
|
|
- return "OpenSSH_"
|
|
|
- + possibleSshTypes[majorVersion][0][0]
|
|
|
- + possibleSshTypes[majorVersion][1][rnd
|
|
|
- .nextInt(possibleSshTypes[majorVersion][1].length)];
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* Denotes in which state the protocol is right now.
|
|
|
*/
|
|
|
private STATE state = STATE.NONE;
|
|
|
-
|
|
|
private boolean useEncryption = false;
|
|
|
|
|
|
// version stuff
|
|
@@ -76,70 +50,60 @@ public class SSH implements Protocol {
|
|
|
{ { "5." }, { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" } },
|
|
|
{ { "6." }, { "0", "1", "2", "3", "4" } } };
|
|
|
|
|
|
- // server infos
|
|
|
- private String serverVersion = "SSH-2.0-";
|
|
|
+ private String initSshType() {
|
|
|
+ SecureRandom rnd = new SecureRandom();
|
|
|
+ int majorVersion = rnd.nextInt(possibleSshTypes.length);
|
|
|
+ return "OpenSSH_"
|
|
|
+ + possibleSshTypes[majorVersion][0][0]
|
|
|
+ + possibleSshTypes[majorVersion][1][rnd
|
|
|
+ .nextInt(possibleSshTypes[majorVersion][1].length)];
|
|
|
+ }
|
|
|
|
|
|
+ // server infos
|
|
|
+ private static final String serverVersion = "SSH-2.0-";
|
|
|
private String serverType = initSshType();
|
|
|
-
|
|
|
private String serverName = HelperUtils.getRandomString(16, false);
|
|
|
-
|
|
|
private int packetNumber = 0;
|
|
|
-
|
|
|
private int recipientChannel;
|
|
|
-
|
|
|
private String userName;
|
|
|
-
|
|
|
private String terminalPrefix;
|
|
|
-
|
|
|
private StringBuffer command = new StringBuffer();
|
|
|
-
|
|
|
private SecureRandom random = new SecureRandom();
|
|
|
|
|
|
// SSH Parameters for Kex etc.
|
|
|
private byte[] V_S = (serverVersion + serverType).getBytes();
|
|
|
-
|
|
|
private byte[] V_C;
|
|
|
-
|
|
|
private byte[] I_S;
|
|
|
-
|
|
|
private byte[] I_C;
|
|
|
-
|
|
|
private byte[] e;
|
|
|
-
|
|
|
private BigInteger f;
|
|
|
-
|
|
|
private byte[] h;
|
|
|
-
|
|
|
private BigInteger k;
|
|
|
-
|
|
|
private byte[] K_S;
|
|
|
-
|
|
|
private byte[] signature;
|
|
|
|
|
|
// allowed algorithms for kexinit
|
|
|
private static final String KEX_ALG = "diffie-hellman-group1-sha1";
|
|
|
-
|
|
|
private static final String SERVER_ALG = "ssh-dss";
|
|
|
-
|
|
|
private static final String ENCRYPT_ALG_C = "3des-cbc";
|
|
|
-
|
|
|
private static final String ENCRYPT_ALG_S = "3des-cbc";
|
|
|
-
|
|
|
private static final String MAC_ALG_C = "hmac-sha1";
|
|
|
private static final String MAC_ALG_S = "hmac-sha1";
|
|
|
private static final String COMP_ALG_C = "none";
|
|
|
private static final String COMP_ALG_S = "none";
|
|
|
+
|
|
|
private int cipherBlockSize = 16;
|
|
|
+
|
|
|
// for en- and decryption
|
|
|
private DESede desEncryption;
|
|
|
private DESede desDecryption;
|
|
|
private CBCMode cbcEncryption;
|
|
|
private CBCMode cbcDecryption;
|
|
|
-
|
|
|
private MAC macEncryption;
|
|
|
// private MAC macDec;
|
|
|
+
|
|
|
// dsa private key
|
|
|
- private final char[] dsaPem = ("-----BEGIN DSA PRIVATE KEY-----\n"
|
|
|
+ private static final char[] dsaPem = ("-----BEGIN DSA PRIVATE KEY-----\n"
|
|
|
+ "MIIBugIBAAKBgQCDZ9R2vfCPwjv5vKF1igIv9drrZ7G0dhMkGT9AZTjgI34Qm4w0\n"
|
|
|
+ "0iWeCqO7SmqiaMIjbRIm91MeDed4ObAq4sAkqRE/2P4mTbzFx5KhEczRRiDoqQBX\n"
|
|
|
+ "xYa0yWKJpeZ94SGM6DEPuBTxKo0T4uMjbq2FzHL2FXT1/WoNCmRU6gFSiwIVAMK4\n"
|
|
@@ -158,13 +122,8 @@ public class SSH implements Protocol {
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public boolean isClosed() {
|
|
|
- return (state == STATE.CLOSED);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public boolean isSecure() {
|
|
|
- return false;
|
|
|
+ public TALK_FIRST whoTalksFirst() {
|
|
|
+ return TALK_FIRST.SERVER;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -179,19 +138,18 @@ public class SSH implements Protocol {
|
|
|
}
|
|
|
switch (state) {
|
|
|
case NONE:
|
|
|
- responsePackets
|
|
|
- .add(new Packet(serverVersion + serverType + "\r\n", toString()));
|
|
|
- responsePackets.add(kexInit());
|
|
|
+ responsePackets.add(new Packet(serverVersion + serverType + "\r\n",
|
|
|
+ toString()));
|
|
|
state = STATE.SERVER_VERSION;
|
|
|
break;
|
|
|
case SERVER_VERSION:
|
|
|
extractType(request);
|
|
|
extractPayload(request);
|
|
|
- extractPubKey(request);
|
|
|
- responsePackets.add(dhKexReply());
|
|
|
- state = STATE.KEX_INIT;
|
|
|
+ responsePackets.add(kexInit());
|
|
|
+ state = STATE.CLIENT_VERSION;
|
|
|
break;
|
|
|
case CLIENT_VERSION:
|
|
|
+ extractPubKey(request);
|
|
|
responsePackets.add(dhKexReply());
|
|
|
state = STATE.KEX_INIT;
|
|
|
break;
|
|
@@ -231,87 +189,84 @@ public class SSH implements Protocol {
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public String toString() {
|
|
|
- return "SSH";
|
|
|
+ public boolean isClosed() {
|
|
|
+ return (state == STATE.CLOSED);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public TALK_FIRST whoTalksFirst() {
|
|
|
- return TALK_FIRST.CLIENT;
|
|
|
+ public boolean isSecure() {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String toString() {
|
|
|
+ return "SSH";
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Channel Open Reply.
|
|
|
+ * Wraps the packets with packet length and padding.
|
|
|
*
|
|
|
- * @param request
|
|
|
- * from client.
|
|
|
- * @return Channel Open Reply.
|
|
|
+ * @param response
|
|
|
+ * content that is wrapped.
|
|
|
+ * @return wrapped packet.
|
|
|
*/
|
|
|
- private Packet channelOpenReply(byte[] request) {
|
|
|
- if (!(HelperUtils.byteToStr(request).contains("session"))) {
|
|
|
- return disconnectReply(2); // if contains "session" ok else disc
|
|
|
- }
|
|
|
- TypesReader tr = new TypesReader(request, 6);
|
|
|
- TypesWriter tw = new TypesWriter();
|
|
|
- try {
|
|
|
- tr.readString();
|
|
|
- recipientChannel = tr.readUINT32();
|
|
|
- int senderChannel = recipientChannel;
|
|
|
- int initialWindowSize = tr.readUINT32();
|
|
|
- int maximumPacketSize = tr.readUINT32();
|
|
|
+ private Packet wrapPacket(byte[] response) {
|
|
|
+ // 4 byte packet length, 1 byte padding length, payload length
|
|
|
+ int packetLength = 5 + response.length;
|
|
|
+ int paddingLengthCBS = cipherBlockSize
|
|
|
+ - (packetLength % cipherBlockSize);
|
|
|
+ int paddingLength8 = 8 - (packetLength % 8);
|
|
|
+ int paddingLength = paddingLengthCBS > paddingLength8 ? paddingLengthCBS
|
|
|
+ : paddingLength8;
|
|
|
+ if (paddingLength < 4)
|
|
|
+ paddingLength += cipherBlockSize;
|
|
|
+ // add padding string length to packet length
|
|
|
+ packetLength = packetLength + paddingLength - 4;
|
|
|
|
|
|
- tw.writeByte(0x5b); // msgcode
|
|
|
- tw.writeUINT32(recipientChannel);
|
|
|
- tw.writeUINT32(senderChannel);
|
|
|
- tw.writeUINT32(initialWindowSize);
|
|
|
- tw.writeUINT32(maximumPacketSize);
|
|
|
- } catch (IOException e) {
|
|
|
- e.printStackTrace();
|
|
|
+ byte[] packetLen = ByteBuffer.allocate(4).putInt(packetLength).array();
|
|
|
+ byte[] paddingLen = { (byte) paddingLength };
|
|
|
+ byte[] paddingString = HelperUtils.randomBytes(paddingLength);
|
|
|
+ byte[] wrappedResponse = HelperUtils.concat(packetLen, paddingLen,
|
|
|
+ response, paddingString);
|
|
|
+ if (useEncryption) {
|
|
|
+ byte[] mac = createMac(wrappedResponse);
|
|
|
+ byte[] responseEnc = encryptBytes(wrappedResponse);
|
|
|
+ wrappedResponse = HelperUtils.concat(responseEnc, mac);
|
|
|
}
|
|
|
+ packetNumber++;
|
|
|
|
|
|
- return wrapPacket(tw.getBytes());
|
|
|
+ return new Packet(wrappedResponse, toString());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Channel Success Reply.
|
|
|
+ * Encrypts a request with triple DES.
|
|
|
*
|
|
|
* @param request
|
|
|
- * from client.
|
|
|
- * @return Channel Success Reply.
|
|
|
+ * that is encrypted.
|
|
|
+ * @return encrypted request.
|
|
|
*/
|
|
|
- private Packet channelSuccessReply(byte[] request) {
|
|
|
- if (!(HelperUtils.byteToStr(request)).contains("pty-req")) {
|
|
|
- return disconnectReply(2);
|
|
|
+ private byte[] encryptBytes(byte[] bytes) {
|
|
|
+ byte[] responseEncrypted = new byte[bytes.length];
|
|
|
+ for (int i = 0; i < bytes.length; i += 8) {
|
|
|
+ cbcEncryption.transformBlock(bytes, i, responseEncrypted, i);
|
|
|
}
|
|
|
- TypesWriter tw = new TypesWriter();
|
|
|
- tw.writeByte(0x63); // msgcode
|
|
|
- tw.writeUINT32(recipientChannel);
|
|
|
-
|
|
|
- return wrapPacket(tw.getBytes());
|
|
|
+ return responseEncrypted;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Userauth ssh-connection reply.
|
|
|
+ * Decrypts a request with triple DES.
|
|
|
*
|
|
|
* @param request
|
|
|
- * from the client.
|
|
|
- * @return ssh-connection reply.
|
|
|
+ * that is decrypted.
|
|
|
+ * @return decrypted request.
|
|
|
*/
|
|
|
- private Packet connectionReply(byte[] request) {
|
|
|
- if (request[5] != 0x32
|
|
|
- && !(HelperUtils.byteToStr(request).contains("ssh-connection"))) {
|
|
|
- return disconnectReply(14);// disconnect because its not
|
|
|
- // servicerequest ssh-connect
|
|
|
- }
|
|
|
- try {
|
|
|
- TypesReader tr = new TypesReader(request, 6);
|
|
|
- userName = tr.readString();
|
|
|
- terminalPrefix = "[" + userName + "@" + serverName + " ~]$ ";
|
|
|
- } catch (IOException e) {
|
|
|
- e.printStackTrace();
|
|
|
+ private byte[] decryptBytes(byte[] request) {
|
|
|
+ byte[] decryptedRequest = new byte[request.length
|
|
|
+ - ((request.length % 8 == 0) ? 0 : 20)];
|
|
|
+ for (int i = 0; i < decryptedRequest.length; i += 8) {
|
|
|
+ cbcDecryption.transformBlock(request, i, decryptedRequest, i);
|
|
|
}
|
|
|
- byte[] msgcode = { 0x34 };
|
|
|
- return wrapPacket(msgcode);
|
|
|
+ return decryptedRequest;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -330,19 +285,36 @@ public class SSH implements Protocol {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Decrypts a request with triple DES.
|
|
|
+ * Builds the Kex Init packet that contains all the allowed algorithms by
|
|
|
+ * the server.
|
|
|
*
|
|
|
- * @param request
|
|
|
- * that is decrypted.
|
|
|
- * @return decrypted request.
|
|
|
+ * @return Kex Init packet.
|
|
|
*/
|
|
|
- private byte[] decryptBytes(byte[] request) {
|
|
|
- byte[] decryptedRequest = new byte[request.length
|
|
|
- - ((request.length % 8 == 0) ? 0 : 20)];
|
|
|
- for (int i = 0; i < decryptedRequest.length; i += 8) { // -12 wegen MAC
|
|
|
- cbcDecryption.transformBlock(request, i, decryptedRequest, i);
|
|
|
- }
|
|
|
- return decryptedRequest;
|
|
|
+ private Packet kexInit() {
|
|
|
+ TypesWriter tw = new TypesWriter();
|
|
|
+ tw.writeByte(0x14);
|
|
|
+ // cookie
|
|
|
+ tw.writeBytes(HelperUtils.randomBytes(16));
|
|
|
+ tw.writeString(KEX_ALG);
|
|
|
+ tw.writeString(SERVER_ALG);
|
|
|
+ tw.writeString(ENCRYPT_ALG_C);
|
|
|
+ tw.writeString(ENCRYPT_ALG_S);
|
|
|
+ tw.writeString(MAC_ALG_C);
|
|
|
+ tw.writeString(MAC_ALG_S);
|
|
|
+ tw.writeString(COMP_ALG_C);
|
|
|
+ tw.writeString(COMP_ALG_S);
|
|
|
+ // language client to server
|
|
|
+ tw.writeBytes(new byte[] { 0x00, 0x00, 0x00, 0x00 });
|
|
|
+ // language server to client
|
|
|
+ tw.writeBytes(new byte[] { 0x00, 0x00, 0x00, 0x00 });
|
|
|
+ // no guess from server
|
|
|
+ tw.writeByte(0x00);
|
|
|
+ // reserved
|
|
|
+ tw.writeBytes(new byte[] { 0x00, 0x00, 0x00, 0x00 });
|
|
|
+ byte[] response = tw.getBytes();
|
|
|
+ I_S = response;
|
|
|
+
|
|
|
+ return wrapPacket(response);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -372,9 +344,10 @@ public class SSH implements Protocol {
|
|
|
response = tw.getBytes();
|
|
|
|
|
|
// init for decryption and encryption
|
|
|
+ // KeyMaterial: alg, h, k, keylength, blocklength, maclength,
|
|
|
+ // keylength, blocklength, maclength
|
|
|
KeyMaterial km = KeyMaterial.create("SHA1", h, k, h, 24, 8, 20, 24,
|
|
|
- 8, 20); // alg, h, k, keylength, blocklength, maclength,
|
|
|
- // keylength, blocklength, maclength
|
|
|
+ 8, 20);
|
|
|
desEncryption = new DESede();
|
|
|
desDecryption = new DESede();
|
|
|
desEncryption.init(true, km.enc_key_server_to_client);
|
|
@@ -388,181 +361,121 @@ public class SSH implements Protocol {
|
|
|
} catch (Exception e) {
|
|
|
e.printStackTrace();
|
|
|
}
|
|
|
- return wrapPacket(response);
|
|
|
- }
|
|
|
|
|
|
- /**
|
|
|
- * Disconnect Reply using the given number as reason code.
|
|
|
- *
|
|
|
- * @param reasonCode
|
|
|
- * for disconnect reply. Must be between 1 and 15, default is 2.
|
|
|
- * @return Disconnect Reply.
|
|
|
- */
|
|
|
- private Packet disconnectReply(int reasonCode) {
|
|
|
- TypesWriter tw = new TypesWriter();
|
|
|
- tw.writeByte(0x01);
|
|
|
- switch (reasonCode) {
|
|
|
- case 1:
|
|
|
- tw.writeUINT32(1);
|
|
|
- tw.writeString("SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT");
|
|
|
- break;
|
|
|
- case 7:
|
|
|
- tw.writeUINT32(7);
|
|
|
- tw.writeString("SSH_DISCONNECT_SERVICE_NOT_AVAILABLE");
|
|
|
- break;
|
|
|
- case 14:
|
|
|
- tw.writeUINT32(14);
|
|
|
- tw.writeString("SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE");
|
|
|
- break;
|
|
|
- default:
|
|
|
- tw.writeUINT32(2);
|
|
|
- tw.writeString("SSH_DISCONNECT_PROTOCOL_ERROR");
|
|
|
- break;
|
|
|
- }
|
|
|
- return wrapPacket(tw.getBytes());
|
|
|
+ return wrapPacket(response);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Encrypts a request with triple DES.
|
|
|
+ * New Keys response.
|
|
|
*
|
|
|
- * @param request
|
|
|
- * that is encrypted.
|
|
|
- * @return encrypted request.
|
|
|
+ * @return New Keys response.
|
|
|
*/
|
|
|
- private byte[] encryptBytes(byte[] bytes) {
|
|
|
- byte[] responseEncrypted = new byte[bytes.length];
|
|
|
- for (int i = 0; i < bytes.length; i += 8) {
|
|
|
- cbcEncryption.transformBlock(bytes, i, responseEncrypted, i);
|
|
|
- }
|
|
|
- return responseEncrypted;
|
|
|
+ private Packet newKeys() {
|
|
|
+ byte[] msgCode = { 0x15 };
|
|
|
+ return wrapPacket(msgCode);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Extracts the payload of a packet and writes it in I_C.
|
|
|
+ * Service ssh-userauth reply.
|
|
|
*
|
|
|
* @param request
|
|
|
- * packet of which the payload is extracted.
|
|
|
+ * from the client.
|
|
|
+ * @return Service reply.
|
|
|
*/
|
|
|
- private void extractPayload(byte[] request) {
|
|
|
- int position = 0;
|
|
|
- if (request[5] != 0x14) {
|
|
|
- position = 1;
|
|
|
- for (int i = 0; i < request.length; i++, position++) {
|
|
|
- if (request[i] == 0x0a)
|
|
|
- break;
|
|
|
- }
|
|
|
+ private Packet serviceReply(byte[] request) {
|
|
|
+ byte[] message;
|
|
|
+ // if newkeys request is included in the same packet remove it
|
|
|
+ if (request[5] == 0x15) {
|
|
|
+ message = new byte[request.length - 16];
|
|
|
+ System.arraycopy(request, 16, message, 0, request.length - 16);
|
|
|
+ } else {
|
|
|
+ message = request;
|
|
|
}
|
|
|
- int packetLength = byteToInt(new byte[] { request[position],
|
|
|
- request[1 + position], request[2 + position],
|
|
|
- request[3 + position] });
|
|
|
- int paddingLength = byteToInt(new byte[] { request[4 + position] });
|
|
|
- byte[] payload = new byte[packetLength - paddingLength - 1];
|
|
|
- for (int i = 6; i < packetLength - paddingLength - 1; i++) {
|
|
|
- payload[i - 6] = request[i + position];
|
|
|
+ if (message[5] != 0x05
|
|
|
+ && !(HelperUtils.byteToStr(message).contains("ssh-userauth"))) {
|
|
|
+ // disconnect because its not servicerequest ssh-userauth
|
|
|
+ return disconnectReply(7);
|
|
|
}
|
|
|
- I_C = payload;
|
|
|
- System.out.println(HelperUtils.bytesToHexString(I_C));
|
|
|
+ TypesWriter tw = new TypesWriter();
|
|
|
+ tw.writeByte(0x06);
|
|
|
+ tw.writeString("ssh-userauth");
|
|
|
+ return wrapPacket(tw.getBytes());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Extracts the public key from the DH Kex Request
|
|
|
+ * Userauth ssh-connection reply.
|
|
|
*
|
|
|
* @param request
|
|
|
- * containing the clients public key
|
|
|
+ * from the client.
|
|
|
+ * @return ssh-connection reply.
|
|
|
*/
|
|
|
- private void extractPubKey(byte[] request) {
|
|
|
- int packetLength = byteToInt(new byte[] { request[0],
|
|
|
- request[1], request[2],
|
|
|
- request[3] });
|
|
|
- int paddingLength = byteToInt(new byte[] { request[4] });
|
|
|
- byte[] len = new byte[] { request[2+packetLength + paddingLength], request[3+ packetLength + paddingLength], request[4 + paddingLength + packetLength],
|
|
|
- request[5 + packetLength + paddingLength] };
|
|
|
- e = new byte[byteToInt(len)];
|
|
|
- for (int i = 0; i < e.length; i++) {
|
|
|
- e[i] = request[i+packetLength + paddingLength+6];
|
|
|
+ private Packet connectionReply(byte[] request) {
|
|
|
+ if (request[5] != 0x32
|
|
|
+ && !(HelperUtils.byteToStr(request).contains("ssh-connection"))) {
|
|
|
+ // disconnect because its not servicerequest ssh-connect
|
|
|
+ return disconnectReply(14);
|
|
|
}
|
|
|
+ try {
|
|
|
+ TypesReader tr = new TypesReader(request, 6);
|
|
|
+ userName = tr.readString();
|
|
|
+ terminalPrefix = "[" + userName + "@" + serverName + " ~]$ ";
|
|
|
+ } catch (IOException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ byte[] msgcode = { 0x34 };
|
|
|
+ return wrapPacket(msgcode);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Extracts the type of the client
|
|
|
+ * Channel Open Reply.
|
|
|
*
|
|
|
* @param request
|
|
|
- * containing the clients type
|
|
|
+ * from client.
|
|
|
+ * @return Channel Open Reply.
|
|
|
*/
|
|
|
- private void extractType(byte[] request) {
|
|
|
- int length = 0;
|
|
|
- for (int i = 0; i < request.length; i++, length++) {
|
|
|
- if (request[i] == 0x0d)
|
|
|
- break; // find the end of the type: '\r'
|
|
|
+ private Packet channelOpenReply(byte[] request) {
|
|
|
+ if (!(HelperUtils.byteToStr(request).contains("session"))) {
|
|
|
+ // if contains "session" ok else disc
|
|
|
+ return disconnectReply(2);
|
|
|
}
|
|
|
- V_C = new byte[length];
|
|
|
- System.arraycopy(request, 0, V_C, 0, length);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Builds the Kex Init packet that contains all the allowed algorithms by
|
|
|
- * the server.
|
|
|
- *
|
|
|
- * @return Kex Init packet.
|
|
|
- */
|
|
|
- private Packet kexInit() {
|
|
|
+ TypesReader tr = new TypesReader(request, 6);
|
|
|
TypesWriter tw = new TypesWriter();
|
|
|
- tw.writeByte(0x14);
|
|
|
- tw.writeBytes(HelperUtils.randomBytes(16)); // cookie
|
|
|
- tw.writeString(KEX_ALG);
|
|
|
- tw.writeString(SERVER_ALG);
|
|
|
- tw.writeString(ENCRYPT_ALG_C);
|
|
|
- tw.writeString(ENCRYPT_ALG_S);
|
|
|
- tw.writeString(MAC_ALG_C);
|
|
|
- tw.writeString(MAC_ALG_S);
|
|
|
- tw.writeString(COMP_ALG_C);
|
|
|
- tw.writeString(COMP_ALG_S);
|
|
|
- tw.writeBytes(new byte[] { 0x00, 0x00, 0x00, 0x00 }); // language client
|
|
|
- // to server
|
|
|
- tw.writeBytes(new byte[] { 0x00, 0x00, 0x00, 0x00 }); // language server
|
|
|
- // to client
|
|
|
- tw.writeByte(0x00); // no guess from server
|
|
|
- tw.writeBytes(new byte[] { 0x00, 0x00, 0x00, 0x00 }); // reserved
|
|
|
- byte[] response = tw.getBytes();
|
|
|
- I_S = response;
|
|
|
+ try {
|
|
|
+ tr.readString();
|
|
|
+ recipientChannel = tr.readUINT32();
|
|
|
+ int senderChannel = recipientChannel;
|
|
|
+ int initialWindowSize = tr.readUINT32();
|
|
|
+ int maximumPacketSize = tr.readUINT32();
|
|
|
|
|
|
- return wrapPacket(response);
|
|
|
- }
|
|
|
+ // msgcode
|
|
|
+ tw.writeByte(0x5b);
|
|
|
+ tw.writeUINT32(recipientChannel);
|
|
|
+ tw.writeUINT32(senderChannel);
|
|
|
+ tw.writeUINT32(initialWindowSize);
|
|
|
+ tw.writeUINT32(maximumPacketSize);
|
|
|
+ } catch (IOException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * New Keys response.
|
|
|
- *
|
|
|
- * @return New Keys response.
|
|
|
- */
|
|
|
- private Packet newKeys() {
|
|
|
- byte[] msgCode = { 0x15 };
|
|
|
- return wrapPacket(msgCode);
|
|
|
+ return wrapPacket(tw.getBytes());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Service ssh-userauth reply.
|
|
|
+ * Channel Success Reply.
|
|
|
*
|
|
|
* @param request
|
|
|
- * from the client.
|
|
|
- * @return Service reply.
|
|
|
+ * from client.
|
|
|
+ * @return Channel Success Reply.
|
|
|
*/
|
|
|
- private Packet serviceReply(byte[] request) {
|
|
|
- byte[] message;
|
|
|
- if (request[5] == 0x15) { // if newkeys request is included in the same
|
|
|
- // packet
|
|
|
- message = new byte[request.length - 16]; // remove it
|
|
|
- System.arraycopy(request, 16, message, 0, request.length - 16);
|
|
|
- } else {
|
|
|
- message = request;
|
|
|
- }
|
|
|
- if (message[5] != 0x05
|
|
|
- && !(HelperUtils.byteToStr(message).contains("ssh-userauth"))) {
|
|
|
- return disconnectReply(7); // disconnect because its not
|
|
|
- // servicerequest ssh-userauth
|
|
|
+ private Packet channelSuccessReply(byte[] request) {
|
|
|
+ if (!(HelperUtils.byteToStr(request)).contains("pty-req")) {
|
|
|
+ return disconnectReply(2);
|
|
|
}
|
|
|
TypesWriter tw = new TypesWriter();
|
|
|
- tw.writeByte(0x06);
|
|
|
- tw.writeString("ssh-userauth");
|
|
|
+ // msgcode
|
|
|
+ tw.writeByte(0x63);
|
|
|
+ tw.writeUINT32(recipientChannel);
|
|
|
+
|
|
|
return wrapPacket(tw.getBytes());
|
|
|
}
|
|
|
|
|
@@ -595,7 +508,7 @@ public class SSH implements Protocol {
|
|
|
message = tr.readString();
|
|
|
if (message.contains("\r")) {
|
|
|
if (command.toString().contains("exit")) {
|
|
|
- state = STATE.CLOSED; // ugly style
|
|
|
+ state = STATE.CLOSED;
|
|
|
return disconnectReply(2);
|
|
|
}
|
|
|
message = "\r\nbash: " + command + " :command not found\r\n"
|
|
@@ -612,43 +525,115 @@ public class SSH implements Protocol {
|
|
|
e.printStackTrace();
|
|
|
}
|
|
|
TypesWriter tw = new TypesWriter();
|
|
|
- tw.writeByte(0x5e); // msgcode
|
|
|
+ // msgcode
|
|
|
+ tw.writeByte(0x5e);
|
|
|
tw.writeUINT32(recipientChannel);
|
|
|
tw.writeString(message);
|
|
|
return wrapPacket(tw.getBytes());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Wraps the packets with packet length and padding.
|
|
|
+ * Disconnect Reply using the given number as reason code.
|
|
|
*
|
|
|
- * @param response
|
|
|
- * content that is wrapped.
|
|
|
- * @return wrapped packet.
|
|
|
+ * @param reasonCode
|
|
|
+ * for disconnect reply. Must be between 1 and 15, default is 2.
|
|
|
+ * @return Disconnect Reply.
|
|
|
*/
|
|
|
- private Packet wrapPacket(byte[] response) {
|
|
|
- // 4 byte packet length, 1 byte padding length, payload length
|
|
|
- int packetLength = 5 + response.length;
|
|
|
- int paddingLengthCBS = cipherBlockSize
|
|
|
- - (packetLength % cipherBlockSize);
|
|
|
- int paddingLength8 = 8 - (packetLength % 8);
|
|
|
- int paddingLength = paddingLengthCBS > paddingLength8 ? paddingLengthCBS
|
|
|
- : paddingLength8;
|
|
|
- if (paddingLength < 4)
|
|
|
- paddingLength += cipherBlockSize;
|
|
|
- // add padding string length to packet length
|
|
|
- packetLength = packetLength + paddingLength - 4;
|
|
|
- byte[] packetLen = ByteBuffer.allocate(4).putInt(packetLength).array();
|
|
|
- byte[] paddingLen = { (byte) paddingLength };
|
|
|
- byte[] paddingString = HelperUtils.randomBytes(paddingLength);
|
|
|
- byte[] wrappedResponse = HelperUtils.concat(packetLen, paddingLen,
|
|
|
- response, paddingString);
|
|
|
- if (useEncryption) {
|
|
|
- byte[] mac = createMac(wrappedResponse);
|
|
|
- byte[] responseEnc = encryptBytes(wrappedResponse);
|
|
|
- wrappedResponse = HelperUtils.concat(responseEnc, mac);
|
|
|
+ private Packet disconnectReply(int reasonCode) {
|
|
|
+ TypesWriter tw = new TypesWriter();
|
|
|
+ tw.writeByte(0x01);
|
|
|
+ switch (reasonCode) {
|
|
|
+ case 1:
|
|
|
+ tw.writeUINT32(1);
|
|
|
+ tw.writeString("SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT");
|
|
|
+ break;
|
|
|
+ case 7:
|
|
|
+ tw.writeUINT32(7);
|
|
|
+ tw.writeString("SSH_DISCONNECT_SERVICE_NOT_AVAILABLE");
|
|
|
+ break;
|
|
|
+ case 14:
|
|
|
+ tw.writeUINT32(14);
|
|
|
+ tw.writeString("SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE");
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ tw.writeUINT32(2);
|
|
|
+ tw.writeString("SSH_DISCONNECT_PROTOCOL_ERROR");
|
|
|
+ break;
|
|
|
}
|
|
|
- packetNumber++;
|
|
|
+ return wrapPacket(tw.getBytes());
|
|
|
+ }
|
|
|
|
|
|
- return new Packet(wrappedResponse, toString());
|
|
|
+ /**
|
|
|
+ * Extracts the type of the client
|
|
|
+ *
|
|
|
+ * @param request
|
|
|
+ * containing the clients type
|
|
|
+ */
|
|
|
+ private void extractType(byte[] request) {
|
|
|
+ int length = 0;
|
|
|
+ for (int i = 0; i < request.length; i++, length++) {
|
|
|
+ // find the end of the type: '\r'
|
|
|
+ if (request[i] == 0x0d)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ V_C = new byte[length];
|
|
|
+ System.arraycopy(request, 0, V_C, 0, length);
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Extracts the payload of a packet and writes it in I_C.
|
|
|
+ *
|
|
|
+ * @param request
|
|
|
+ * packet of which the payload is extracted.
|
|
|
+ */
|
|
|
+ private void extractPayload(byte[] request) {
|
|
|
+ int position = 0;
|
|
|
+ if (request[5] != 0x14) {
|
|
|
+ position = 1;
|
|
|
+ for (int i = 0; i < request.length; i++, position++) {
|
|
|
+ if (request[i] == 0x0a)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ int packetLength = byteToInt(new byte[] { request[position],
|
|
|
+ request[1 + position], request[2 + position],
|
|
|
+ request[3 + position] });
|
|
|
+ int paddingLength = byteToInt(new byte[] { request[4 + position] });
|
|
|
+ byte[] payload = new byte[packetLength - paddingLength - 1];
|
|
|
+ for (int i = 5; i < packetLength - paddingLength - 1; i++) {
|
|
|
+ payload[i - 5] = request[i + position];
|
|
|
+ }
|
|
|
+ I_C = payload;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Extracts the public key from the DH Kex Request
|
|
|
+ *
|
|
|
+ * @param request
|
|
|
+ * containing the clients public key
|
|
|
+ */
|
|
|
+ private void extractPubKey(byte[] request) {
|
|
|
+ e = new byte[byteToInt(new byte[] { request[6], request[7], request[8],
|
|
|
+ request[9] })];
|
|
|
+ for (int i = 0; i < e.length; i++) {
|
|
|
+ e[i] = request[i + 10];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Converts a byte[] to int
|
|
|
+ *
|
|
|
+ * @param bytes
|
|
|
+ * that are converted
|
|
|
+ * @return converted byte[] as int
|
|
|
+ */
|
|
|
+ private static int byteToInt(byte[] bytes) {
|
|
|
+ int convertedInteger = 0;
|
|
|
+ for (int i = 0; i < bytes.length; i++) {
|
|
|
+ convertedInteger <<= 8;
|
|
|
+ convertedInteger |= bytes[i] & 0xFF;
|
|
|
+ }
|
|
|
+ return convertedInteger;
|
|
|
+ }
|
|
|
+
|
|
|
}
|