Browse Source

SSH: completed. connection setup and terminal session working.
Reduced SLEEPTIME for byte streams to 1 ms.
Minor changes.

Wulf Pfeiffer 10 years ago
parent
commit
971d2c8fbd

+ 1 - 1
.classpath

@@ -5,6 +5,6 @@
 	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
 	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
 	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
-	<classpathentry kind="lib" path="libs/sshlib-v0.1.jar"/>
+	<classpathentry kind="lib" path="libs/sshlib-v1.jar"/>
 	<classpathentry kind="output" path="bin/classes"/>
 </classpath>

+ 0 - 0
libs/sshlib-v0.1.jar → libs/sshlib-v1.jar


+ 1 - 1
src/de/tudarmstadt/informatik/hostage/HoneyHandler.java

@@ -71,7 +71,7 @@ public class HoneyHandler implements Runnable {
 		this.thread = new Thread(this);
 		SharedPreferences pref = PreferenceManager
 				.getDefaultSharedPreferences(service);
-		SLEEPTIME = pref.getInt("sleeptime", 500);
+		SLEEPTIME = pref.getInt("sleeptime", 1); // 1 ms already removes ressource leak
 		TIMEOUT = pref.getInt("timeout", 30) * 1000;
 		// TODO ThreadSicher?
 		attack_id = getAndIncrementAttackID(pref);

+ 2 - 2
src/de/tudarmstadt/informatik/hostage/commons/HelperUtils.java

@@ -304,7 +304,7 @@ public final class HelperUtils {
 	}
 
 	/**
-	 * Produces a random String. The String can be of random length with a
+	 * Produces a random String. The String can be of random length (minimum 1) with a
 	 * maximum length, or it can be forced to have the length that was given.
 	 * 
 	 * @param length
@@ -316,7 +316,7 @@ public final class HelperUtils {
 	 */
 	public static String getRandomString(int length, boolean forceLength) {
 		SecureRandom rndm = new SecureRandom();
-		char[] c = new char[forceLength ? length : rndm.nextInt(length)];
+		char[] c = new char[forceLength ? length : rndm.nextInt(length-1)+1];
 		for (int i = 0; i < c.length; i++) {
 			c[i] = (char) (rndm.nextInt(95) + 32);
 		}

+ 13 - 0
src/de/tudarmstadt/informatik/hostage/protocol/ProtocolSettings.java

@@ -51,6 +51,7 @@ public class ProtocolSettings {
 	//SSH
 	private static String sshVersion	= initSshVersion();
 	private static String sshType		= initSshType();
+	private static String sshName		= initSshName();
 	
 	//~~~ Initialize methods ~~~//
 	
@@ -96,6 +97,10 @@ public class ProtocolSettings {
 		int majorVersion = rndm.nextInt(possibleSshTypes.length);
 		return "OpenSSH_" + possibleSshTypes[majorVersion][0][0] + possibleSshTypes[majorVersion][1][rndm.nextInt(possibleSshTypes[majorVersion][1].length)];
 	}
+	
+	private static String initSshName() {
+		return HelperUtils.getRandomString(16, false);
+	}
 
 	//~~~ Getters and Setters ~~//
 	
@@ -179,4 +184,12 @@ public class ProtocolSettings {
 		ProtocolSettings.sshType = sshType;
 	}
 	
+	public static String getSshName() {
+		return sshName;
+	}
+
+	public static void setSsh(String sshName) {
+		ProtocolSettings.sshName = sshName;
+	}
+	
 }

+ 236 - 115
src/de/tudarmstadt/informatik/hostage/protocol/SSH.java

@@ -1,5 +1,6 @@
 package de.tudarmstadt.informatik.hostage.protocol;
 
+import java.io.IOException;
 import java.math.BigInteger;
 import java.nio.ByteBuffer;
 import java.security.SecureRandom;
@@ -16,6 +17,7 @@ import de.tudarmstadt.informatik.hostage.ssh.crypto.digest.MAC;
 import de.tudarmstadt.informatik.hostage.ssh.signature.DSAPrivateKey;
 import de.tudarmstadt.informatik.hostage.ssh.signature.DSASHA1Verify;
 import de.tudarmstadt.informatik.hostage.ssh.signature.DSASignature;
+import de.tudarmstadt.informatik.hostage.ssh.util.TypesReader;
 import de.tudarmstadt.informatik.hostage.ssh.util.TypesWriter;
 import de.tudarmstadt.informatik.hostage.wrapper.Packet;
 
@@ -32,10 +34,12 @@ public class SSH implements Protocol {
 		SERVER_VERSION,
 		CLIENT_VERSION,
 		KEX_INIT,
+		NEW_KEYS,
 		USERAUTH,
 		CONNECTION,
 		CHANNEL,
-		LOOP, // TODO for debugging
+		TERMINAL_CMD,
+		TERMINAL_ENTER,
 		CLOSED
 	}
 	
@@ -43,6 +47,7 @@ public class SSH implements Protocol {
 	 * Denotes in which state the protocol is right now.
 	 */
 	private STATE state = STATE.NONE;
+	private boolean useEncryption = false;
 		
 	@Override
 	public int getPort() {
@@ -60,6 +65,9 @@ public class SSH implements Protocol {
 		byte[] request = null;
 		if(packet != null) {
 			request = packet.getMessage();
+			if(useEncryption) {
+				request = decryptBytes(request);
+			}
 		}
 		switch(state) {
 		case NONE:			
@@ -75,15 +83,19 @@ public class SSH implements Protocol {
 		case CLIENT_VERSION:
 			extractPubKey(request);
 			response.add(dhKexReply());
-			response.add(newKeys());
 			state = STATE.KEX_INIT;
 			break;
 		case KEX_INIT:
+			response.add(newKeys());
+			useEncryption = true;
+			state = STATE.NEW_KEYS;
+			break;
+		case NEW_KEYS:
 			response.add(serviceReply(request));
 			state = STATE.USERAUTH;
 			break;
 		case USERAUTH:
-			response.add(userauthReply(request));
+			response.add(connectionReply(request));
 			state = STATE.CONNECTION;
 			break;
 		case CONNECTION:
@@ -92,13 +104,11 @@ public class SSH implements Protocol {
 			break;
 		case CHANNEL:
 			response.add(channelSuccessReply(request));
-			response.add(data(request));
-			state = STATE.LOOP;
+			response.add(terminalPrefix());
+			state = STATE.TERMINAL_CMD;
 			break;
-		case LOOP:
-			decryptPacket(request);
-			response.add(data(request));
-			state = STATE.LOOP;
+		case TERMINAL_CMD:
+			response.add(terminalReply(request));
 			break;
 		case CLOSED:
 			break;
@@ -135,7 +145,7 @@ public class SSH implements Protocol {
 	 * @param packet content that is wrapped.
 	 * @return wrapped packet.
 	 */
-	private Packet wrapPacket(byte[] packet, boolean encrypt) {
+	private Packet wrapPacket(byte[] packet) {
 		int packetLength = 5 + packet.length; 	//4 byte packet length, 1 byte padding length, payload length
 		int paddingLengthCBS = cipherBlockSize - (packetLength % cipherBlockSize);
 		int paddingLength8 = 8 - (packetLength % 8);
@@ -145,70 +155,81 @@ public class SSH implements Protocol {
 		
 		byte[] packetLen = ByteBuffer.allocate(4).putInt(packetLength).array();
 		byte[] paddingLen = {(byte) paddingLength};
-		byte[] paddingString = new byte[paddingLength];
-		for(int i = 0; i < paddingLength; i++) {
-			SecureRandom rndm = new SecureRandom();
-			paddingString[i] = (byte) rndm.nextInt(255);
-		}
+		byte[] paddingString = HelperUtils.randomBytes(paddingLength);
 		byte[] response = HelperUtils.concat(packetLen, paddingLen, packet, paddingString);
-		if(encrypt) {
-			byte[] mac = new byte[20];
-			macEnc.initMac(send_packet_number);
-			macEnc.update(response, 0, response.length);
-			macEnc.getMac(mac, 0);		
-			
-			byte[] responseEnc = new byte[response.length];
-			for(int i = 0; i < response.length; i+=8) { 
-				cbcEnc.transformBlock(response, i, responseEnc, i);
-			}
+		if(useEncryption) {
+			byte[] mac = getMac(response);
+			byte[] responseEnc = encryptBytes(response);
 			response = HelperUtils.concat(responseEnc,mac);
 		}
-		send_packet_number++;
+		packetNumber++;
 		
 		return new Packet(response);
 	}
 	
-	private byte[] decryptPacket(byte[] request) {
+	/**
+	 * Encrypts a request with triple DES.
+	 * @param request that is encrypted.
+	 * @return encrypted request.
+	 */
+	private byte[] encryptBytes(byte[] bytes) {
+		byte[] responseEnc = new byte[bytes.length];
+		for(int i = 0; i < bytes.length; i+=8) { 
+			cbcEnc.transformBlock(bytes, i, responseEnc, i);
+		}
+		return responseEnc;
+	}
+	
+	/**
+	 * Decrypts a request with triple DES.
+	 * @param request that is decrypted.
+	 * @return decrypted request.
+	 */
+	private byte[] decryptBytes(byte[] request) {
 		byte[] message = new byte[request.length - ((request.length % 8 == 0) ? 0 : 20)];
 		for(int i = 0; i < message.length; i += 8) { // -12 wegen MAC
 			cbcDec.transformBlock(request, i, message, i);
 		}
-		System.out.println("Encrypted packet:");
-		System.out.println(HelperUtils.bytesToHexString(request));
-		System.out.println("\nDecrypted packet:");
-		System.out.println(HelperUtils.bytesToHexString(message));
-		System.out.println("\nDecrypted string packet:");
-		System.out.println(HelperUtils.byteToStr(message));
-		System.out.println();
 		return message;
 	}
 	
+	/**
+	 * Creates the SHA1 Mac with the given bytes.
+	 * @param bytes that are used for the Mac.
+	 * @return Mac.
+	 */
+	private byte[] getMac(byte[] bytes) {
+		byte[] mac = new byte[20];
+		macEnc.initMac(packetNumber);
+		macEnc.update(bytes, 0, bytes.length);
+		macEnc.getMac(mac, 0);
+		return mac;
+	}
+	
 	/**
 	 * Builds the Kex Init packet that contains all the allowed algorithms by the server.
 	 * @return Kex Init packet.
 	 */
 	private Packet kexInit() {
-		byte[] msgCode = {0x14};
-		byte[] cookie = HelperUtils.randomBytes(16);
-		byte[] kexLength = ByteBuffer.allocate(4).putInt(kex_alg.getBytes().length).array();
-		byte[] serverLength = ByteBuffer.allocate(4).putInt(server_alg.getBytes().length).array();
-		byte[] encrypt_c_Length = ByteBuffer.allocate(4).putInt(encrypt_alg_c.getBytes().length).array();
-		byte[] encrypt_s_Length = ByteBuffer.allocate(4).putInt(encrypt_alg_s.getBytes().length).array();
-		byte[] mac_c_Length = ByteBuffer.allocate(4).putInt(mac_alg_c.getBytes().length).array();
-		byte[] mac_s_Length = ByteBuffer.allocate(4).putInt(mac_alg_s.getBytes().length).array();
-		byte[] comp_c_Length = ByteBuffer.allocate(4).putInt(comp_alg_c.getBytes().length).array();
-		byte[] comp_s_Length = ByteBuffer.allocate(4).putInt(comp_alg_s.getBytes().length).array();
-		byte[] language_c_s = {0x00, 0x00, 0x00, 0x00};
-		byte[] language_s_c = {0x00, 0x00, 0x00, 0x00};
-		byte[] kexFirsPckt = {0x00};
-		byte[] reserved = {0x00, 0x00, 0x00, 0x00};	
-		
-		byte[] response = HelperUtils.concat(msgCode, cookie, kexLength, kex_alg.getBytes(), serverLength, server_alg.getBytes(),
-							encrypt_c_Length, encrypt_alg_c.getBytes(), encrypt_s_Length, encrypt_alg_s.getBytes(), mac_c_Length, mac_alg_c.getBytes(),
-							mac_s_Length, mac_alg_s.getBytes(), comp_c_Length, comp_alg_c.getBytes(), comp_s_Length, comp_alg_s.getBytes(),
-							language_c_s, language_s_c, kexFirsPckt, reserved);
+		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;
-		return wrapPacket(response, false);
+		
+		return wrapPacket(response);
 	}
 	
 	/**
@@ -216,7 +237,7 @@ public class SSH implements Protocol {
 	 * @return Diffie-Hellman Kex Reply packet.
 	 */
 	private Packet dhKexReply() {
-		//TODO make it small		
+		byte[] response = null;
 		try {
 			SecureRandom rnd = new SecureRandom();
 			DhExchange dhx = new DhExchange();
@@ -234,7 +255,7 @@ public class SSH implements Protocol {
 			tw.writeString(K_S, 0, K_S.length);
 			tw.writeMPInt(f);
 			tw.writeString(sig, 0, sig.length);
-			byte[] response = tw.getBytes();
+			response = tw.getBytes();
 			
 			//init for decryption and encryption
 			KeyMaterial km = KeyMaterial.create("SHA1", h, k, h, 24, 8, 20, 24, 8, 20); // alg, h, k, keylength, blocklength, maclength, keylength, blocklength, maclength
@@ -246,12 +267,11 @@ public class SSH implements Protocol {
 			cbcDec = new CBCMode(dec, km.initial_iv_client_to_server, false);
 			macEnc = new MAC("hmac-sha1", km.integrity_key_server_to_client);
 			macDec = new MAC("hmac-sha1", km.integrity_key_client_to_server);
-			
-			return wrapPacket(response, false);
 		} catch (Exception e) {
 			e.printStackTrace();
 		}
-		return wrapPacket(null, false);
+		
+		return wrapPacket(response);
 	}
 
 	/**
@@ -260,79 +280,173 @@ public class SSH implements Protocol {
 	 */
 	private Packet newKeys() {
 		byte[] msgCode = {0x15};
-		return wrapPacket(msgCode, false);
+		return wrapPacket(msgCode);
 	}
 	
-	// TODO
+	/**
+	 * Service ssh-userauth reply.
+	 * @param request from the client.
+	 * @return Service reply.
+	 */
 	private Packet serviceReply(byte[] request) {
-		byte[] messageBuf;
+		byte[] message;
 		if(request[5] == 0x15) {	//if newkeys request is included in the same packet
-			messageBuf = new byte[request.length - 16];
-			System.arraycopy(request, 16, messageBuf, 0, request.length-16);
+			message = new byte[request.length - 16]; //remove it
+			System.arraycopy(request, 16, message, 0, request.length-16);
 		} else {
-			messageBuf = request;
+			message = request;
 		}
-		byte[] message = decryptPacket(messageBuf);
-		
-		if (message[5] != 0x05 && message[9] != 0x0c) {
-			// TODO disconnect because its not servicerequest ssh-userauth
+		if (message[5] != 0x05 && !(HelperUtils.byteToStr(message).contains("ssh-userauth"))) {
+			return disconnectReply(7); // disconnect because its not servicerequest ssh-userauth
 		}
-		byte[] msgcode = {0x06};
-		byte[] serviceLength;
-		byte[] service = "ssh-userauth".getBytes();
-		serviceLength = ByteBuffer.allocate(4).putInt(service.length).array();
-		
-		byte[] response = HelperUtils.concat(msgcode, serviceLength, service);
-		return wrapPacket(response, true);		
+		TypesWriter tw = new TypesWriter();
+		tw.writeByte(0x06);
+		tw.writeString("ssh-userauth");
+		return wrapPacket(tw.getBytes());		
 	}
 	
-	// TODO
-	private Packet userauthReply(byte[] request) {
-		byte[] message = decryptPacket(request);
-		if (message[5] != 0x32) {
-			// TODO disconnect because its not servicerequest ssh-connect
+	/**
+	 * Userauth ssh-connection reply.
+	 * @param request from the client.
+	 * @return ssh-connection reply.
+	 */
+	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
 		}
-		byte[] msgcode = {0x34};
-		byte[] response = msgcode;
-		
-		return wrapPacket(response, true);		
+		try {
+			TypesReader tr = new TypesReader(request, 6);
+			userName = tr.readString();
+			terminalPrefix = "[" + userName + "@" + serverName + "]$";
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		byte[] msgcode = {0x34};		
+		return wrapPacket(msgcode);		
 	}
 	
-	// TODO
+	/**
+	 * Channel Open Reply.
+	 * @param request from client.
+	 * @return Channel Open Reply.
+	 */
 	private Packet channelOpenReply(byte[] request) {
-		byte[] message = decryptPacket(request);
-		// TODO if contains "session" ok else disc
-		byte[] msgcode = {0x5b};
-		recipientChannel = new byte[4];
-		byte[] senderChannel = new byte[4];
-		byte[] initialWindowSize = new byte[4];
-		byte[] maximumPacketSize = new byte[4];
-		System.arraycopy(message, 17, recipientChannel, 0, 4); //TODO dynamic position
-		System.arraycopy(message, 17, senderChannel, 0, 4);
-		System.arraycopy(message, 21, initialWindowSize, 0, 4);
-		System.arraycopy(message, 25, maximumPacketSize, 0, 4);
-		byte[] response = HelperUtils.concat(msgcode, recipientChannel, senderChannel, initialWindowSize, maximumPacketSize);
-		System.out.println("CHANNEL REPLY:");
-		System.out.println(HelperUtils.bytesToHexString(response));
-		return wrapPacket(response, true);
+		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();
+			
+			tw.writeByte(0x5b); //msgcode
+			tw.writeUINT32(recipientChannel);
+			tw.writeUINT32(senderChannel);
+			tw.writeUINT32(initialWindowSize);
+			tw.writeUINT32(maximumPacketSize);
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		
+		return wrapPacket(tw.getBytes());
 	}
 	
-	byte[] recipientChannel; //TODO
+	/**
+	 * Channel Success Reply.
+	 * @param request from client.
+	 * @return Channel Success Reply.
+	 */
 	private Packet channelSuccessReply(byte[] request) {
-		decryptPacket(request);
-		byte[] msgcode = {0x63}; //TODO check msg type
-		byte[] response = HelperUtils.concat(msgcode, recipientChannel);
-		System.out.println(HelperUtils.bytesToHexString(response));
-		return wrapPacket(response, true);
+		if(!(HelperUtils.byteToStr(request)).contains("pty-req")) {
+			return disconnectReply(2);
+		}
+		TypesWriter tw = new TypesWriter();
+		tw.writeByte(0x63); //msgcode
+		tw.writeUINT32(recipientChannel);
+		
+		return wrapPacket(tw.getBytes());
+	}
+	
+	/**
+	 * Returns the terminal prefix for the client.
+	 * @return terminal prefix.
+	 */
+	private Packet terminalPrefix() {
+		TypesWriter tw = new TypesWriter();
+		tw.writeByte(0x5e);
+		tw.writeUINT32(recipientChannel);
+		tw.writeString(terminalPrefix);
+		return wrapPacket(tw.getBytes());
 	}
 	
-	private Packet data(byte[] request) {
-		byte[] msgcode = {0x5E};
-		byte[] msglength = new byte[4];
-		byte[] msg = "lolol".getBytes();
-		msglength = ByteBuffer.allocate(4).putInt(msg.length).array();
-		byte[] response = HelperUtils.concat(msgcode, recipientChannel, msglength, msg);
-		return wrapPacket(response, true);
+	/**
+	 * Computes the reply for the client input.
+	 * @param request client input.
+	 * @return input reply.
+	 */
+	private Packet terminalReply(byte[] request) {
+		//TODO
+		TypesReader tr = new TypesReader(request, 6);
+		String msg = "";
+		System.out.println(HelperUtils.bytesToHexString(request));
+		try {
+			tr.readUINT32();
+			msg = tr.readString();
+			System.out.println(msg);
+			if(msg.contains("\r")) {
+				System.out.println("hi");
+				if(cmd.toString().contains("exit")) {
+					state = STATE.CLOSED; // ugly style
+					return disconnectReply(2);
+				}
+				msg = "\r\nbash: " + cmd + " :command not found\r\n"	+ terminalPrefix;
+				cmd = new StringBuffer();
+			} else if(msg.contains(new String(new char[]{'\u007F'})) && cmd.length() > 0) {
+				cmd = cmd.delete(cmd.length()-1, cmd.length());
+			} else {
+				cmd.append(msg);
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		TypesWriter tw = new TypesWriter();
+		tw.writeByte(0x5e); //msgcode
+		tw.writeUINT32(recipientChannel);
+		tw.writeString(msg);
+		return wrapPacket(tw.getBytes());
+	}
+	
+	/**
+	 * 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());
 	}
 	
 	/**
@@ -394,9 +508,15 @@ public class SSH implements Protocol {
 		  return ret;
 	}
 	
+	//server infos
 	private String serverVersion = ProtocolSettings.getSshVersion();
 	private String serverType = ProtocolSettings.getSshType();
-	private int send_packet_number = 0;
+	private String serverName = ProtocolSettings.getSshName();
+	private int packetNumber = 0;
+	int recipientChannel;
+	String userName;
+	String terminalPrefix;
+	StringBuffer cmd = new StringBuffer();
 	
 	//SSH Parameters for Kex etc.
     private byte[] V_S = (serverVersion + serverType).getBytes();
@@ -422,6 +542,7 @@ public class SSH implements Protocol {
 	
 	private int cipherBlockSize = 16;
 	
+	//for en- and decryption
 	DESede enc;
 	DESede dec;
 	CBCMode cbcEnc;