Browse Source

Added ECHO protocol
Added SSH code parts (signature corrupted)
Fixed minor bugs

qam 10 years ago
parent
commit
07972101ef

+ 3 - 2
res/values/protocols.xml

@@ -2,12 +2,13 @@
 <resources>
 
     <string-array name="protocols">
+        <item>ECHO</item>
         <item>FTP</item>
         <item>HTTP</item>
-        <item>SMB</item>
-        <item>SSH</item>
         <item>TELNET</item>
         <item>MySQL</item>
+        <item>SMB</item>
+        <item>SSH</item>
     </string-array>
 
 </resources>

+ 47 - 0
src/de/tudarmstadt/informatik/hostage/protocol/ECHO.java

@@ -0,0 +1,47 @@
+package de.tudarmstadt.informatik.hostage.protocol;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.tudarmstadt.informatik.hostage.wrapper.ByteArray;
+
+public class ECHO implements Protocol<ByteArray>{
+
+	@Override
+	public int getPort() {
+		return 7;
+	}
+
+	@Override
+	public TALK_FIRST whoTalksFirst() {
+		return TALK_FIRST.CLIENT;
+	}
+
+	@Override
+	public List<ByteArray> processMessage(ByteArray message) {
+		List<ByteArray> response = new ArrayList<ByteArray>();
+		response.add(message);
+		return response;
+	}
+
+	@Override
+	public boolean isClosed() {
+		return true;
+	}
+
+	@Override
+	public boolean isSecure() {
+		return false;
+	}
+
+	@Override
+	public Class<ByteArray> getType() {
+		return ByteArray.class;
+	}
+	
+	@Override
+	public String toString() {
+		return "ECHO";
+	}
+
+}

+ 351 - 10
src/de/tudarmstadt/informatik/hostage/protocol/SSH.java

@@ -1,14 +1,85 @@
 package de.tudarmstadt.informatik.hostage.protocol;
 
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.interfaces.DSAPublicKey;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Random;
 
-public final class SSH implements Protocol<String> {
+import javax.crypto.KeyAgreement;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPublicKeySpec;
 
-	private static enum STATE {
-		NONE, OPEN, CLOSED
-	};
+import de.tudarmstadt.informatik.hostage.wrapper.ByteArray;
 
+public final class SSH implements Protocol<ByteArray> {
+	
+	private enum STATE {
+		NONE,
+		SRVR_VERSION,
+		CLNT_VERSION,
+		KEX_INIT,
+		DH_KEX_REP,
+		CLOSED
+	}
+	
+	private STATE connectionState = STATE.NONE;
+	
+	private String serverVersion = "SSH-2.0-";
+	private String serverType = "OpenSSH_6.0p1 Debian-4";
+		
+	private final byte[] pDH = {
+            (byte)0x00,
+            (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
+            (byte)0xC9,(byte)0x0F,(byte)0xDA,(byte)0xA2,(byte)0x21,(byte)0x68,(byte)0xC2,(byte)0x34,
+            (byte)0xC4,(byte)0xC6,(byte)0x62,(byte)0x8B,(byte)0x80,(byte)0xDC,(byte)0x1C,(byte)0xD1,
+            (byte)0x29,(byte)0x02,(byte)0x4E,(byte)0x08,(byte)0x8A,(byte)0x67,(byte)0xCC,(byte)0x74,
+            (byte)0x02,(byte)0x0B,(byte)0xBE,(byte)0xA6,(byte)0x3B,(byte)0x13,(byte)0x9B,(byte)0x22,
+            (byte)0x51,(byte)0x4A,(byte)0x08,(byte)0x79,(byte)0x8E,(byte)0x34,(byte)0x04,(byte)0xDD,
+            (byte)0xEF,(byte)0x95,(byte)0x19,(byte)0xB3,(byte)0xCD,(byte)0x3A,(byte)0x43,(byte)0x1B,
+            (byte)0x30,(byte)0x2B,(byte)0x0A,(byte)0x6D,(byte)0xF2,(byte)0x5F,(byte)0x14,(byte)0x37,
+            (byte)0x4F,(byte)0xE1,(byte)0x35,(byte)0x6D,(byte)0x6D,(byte)0x51,(byte)0xC2,(byte)0x45,
+            (byte)0xE4,(byte)0x85,(byte)0xB5,(byte)0x76,(byte)0x62,(byte)0x5E,(byte)0x7E,(byte)0xC6,
+            (byte)0xF4,(byte)0x4C,(byte)0x42,(byte)0xE9,(byte)0xA6,(byte)0x37,(byte)0xED,(byte)0x6B,
+            (byte)0x0B,(byte)0xFF,(byte)0x5C,(byte)0xB6,(byte)0xF4,(byte)0x06,(byte)0xB7,(byte)0xED,
+            (byte)0xEE,(byte)0x38,(byte)0x6B,(byte)0xFB,(byte)0x5A,(byte)0x89,(byte)0x9F,(byte)0xA5,
+            (byte)0xAE,(byte)0x9F,(byte)0x24,(byte)0x11,(byte)0x7C,(byte)0x4B,(byte)0x1F,(byte)0xE6,
+            (byte)0x49,(byte)0x28,(byte)0x66,(byte)0x51,(byte)0xEC,(byte)0xE6,(byte)0x53,(byte)0x81,
+            (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF
+      };
+	private final byte[] gDH = {0x02};
+    private byte[] V_S = serverType.getBytes();
+    private byte[] V_C;
+    private byte[] I_S;
+    private byte[] I_C;
+    private byte[] eDH;
+    private byte[] fDH;
+    private byte[] kDH;
+    private byte[] hDH;
+    private byte[] K_S;
+    private byte[] sigH;
+
+    private KeyPair dsa;
+			
+	private String kex_alg = "diffie-hellman-group1-sha1";
+	private String server_alg = "ssh-dss";
+	private String encrypt_alg_c = "aes128-ctr";
+	private String encrypt_alg_s = "aes128-ctr";
+	private String mac_alg_c = "hmac-sha1";
+	private String mac_alg_s = "hmac-sha1";
+	private String comp_alg_c = "none";
+	private String comp_alg_s = "none";
+	
+	private int cipherBlockSize = 16;
+	
 	private STATE state = STATE.NONE;
 
 	@Override
@@ -18,13 +89,50 @@ public final class SSH implements Protocol<String> {
 
 	@Override
 	public TALK_FIRST whoTalksFirst() {
-		return TALK_FIRST.CLIENT;
+		return TALK_FIRST.SERVER;
 	}
 
 	@Override
-	public List<String> processMessage(String message) {
-		ArrayList<String> response = new ArrayList<String>();
-		response.add("Not implemented yet!");
+	public List<ByteArray> processMessage(ByteArray message) {
+		List<ByteArray> response = new ArrayList<ByteArray>();
+		byte[] request = null;
+		if(message != null) request = message.get();
+		
+		switch(connectionState) {
+		case NONE:			
+			response.add(new ByteArray(serverVersion + serverType + "\r\n"));
+			response.add(new ByteArray(kexInit()));
+			connectionState = STATE.SRVR_VERSION;
+			break;
+		case SRVR_VERSION:
+			if(request != null && request.length >= 8) {
+				extractType(request);
+				connectionState = STATE.CLNT_VERSION;
+			}
+			break;
+		case CLNT_VERSION:
+			if(request != null && request.length > 5 && request[5] == 0x14) {
+				extractCookie(request);
+				connectionState = STATE.KEX_INIT;
+			}
+			break;
+		case KEX_INIT:
+			if(request.length > 5 && request[5] == 0x1e) {
+				extractPubKey(request);
+				response.add(new ByteArray(dhKexReply()));
+				connectionState = STATE.DH_KEX_REP;
+			}
+			break;
+		case DH_KEX_REP:
+			connectionState = STATE.CLOSED;
+			break;
+		case CLOSED:
+			break;
+		default:
+			connectionState = STATE.CLOSED;
+			break;
+		}
+		
 		return response;
 	}
 
@@ -39,13 +147,246 @@ public final class SSH implements Protocol<String> {
 	}
 
 	@Override
-	public Class<String> getType() {
-		return String.class;
+	public Class<ByteArray> getType() {
+		return ByteArray.class;
 	}
 
 	@Override
 	public String toString() {
 		return "SSH";
 	}
+	
+	
+	private byte[] wrapPckt(byte[] payload) {
+		int pcktLen = 5 + payload.length; 	//4 byte packet length, 1 byte padding length, payload length
+		int paddLenCBS = cipherBlockSize - (pcktLen % cipherBlockSize);
+		int paddLen8 = 8 - (pcktLen % 8);
+		int paddingLen = paddLenCBS > paddLen8 ? paddLenCBS : paddLen8;
+		if(paddingLen < 4) paddingLen += cipherBlockSize;
+		pcktLen = pcktLen + paddingLen - 4;					//add padding string length to packet length
+		
+		byte[] pcktLength = ByteBuffer.allocate(4).putInt(pcktLen).array();
+		byte[] paddingLength = {(byte) paddingLen};
+		byte[] paddingString = new byte[paddingLen];
+		for(int i = 0; i < paddingLen; i++) {
+			paddingString[i] = 0x00;
+		}
+		
+		return concat(pcktLength, paddingLength, payload, paddingString);
+	}
+	
+	private byte[] kexInit() {
+		byte[] msgCode = {0x14};
+		I_S = 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 = concat(msgCode, I_S, 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);
+		return wrapPckt(response);
+	}
+	
+	private byte[] dhKexReply() {
+		generateDHKeys();
+		generateHostKey();
+		generateSha1Hash();
+		generateSignature();
+		byte[] msgCode = {0x1f};
+		byte[] hostKeyLength = ByteBuffer.allocate(4).putInt(K_S.length).array();
+				byte[] fDHLength = ByteBuffer.allocate(4).putInt(fDH.length).array();
+		byte[] signatureLength = ByteBuffer.allocate(4).putInt(sigH.length).array();
+		byte[] server_algLength = ByteBuffer.allocate(4).putInt(server_alg.getBytes().length).array();
+		byte[] payloadLength = ByteBuffer.allocate(4).putInt(server_algLength.length + signatureLength.length + sigH.length + server_alg.getBytes().length).array();
+		byte[] response = concat(msgCode, hostKeyLength,  K_S,
+									fDHLength, fDH, payloadLength, server_algLength, server_alg.getBytes(), signatureLength, sigH);
+		return wrapPckt(response);
+	}
+	
+	private byte[] newKeys() {
+		byte[] msgCode = {0x15};
+		return wrapPckt(msgCode);
+	}
+	
+	private void generateDHKeys() {	
+		try {
+			KeyPairGenerator myKpairGen = KeyPairGenerator.getInstance("DH");
+			KeyAgreement myKeyAgree = KeyAgreement.getInstance("DH");
+			BigInteger p = new BigInteger(pDH);
+			BigInteger g = new BigInteger(gDH);
+			BigInteger e = new BigInteger(eDH);
+			
+            DHParameterSpec dhParamSpec = new DHParameterSpec(p, g);
+            myKpairGen.initialize(dhParamSpec);
+            KeyPair myKpair = myKpairGen.generateKeyPair();
+            myKeyAgree.init(myKpair.getPrivate());
+            BigInteger f = ((DHPublicKey) (myKpair.getPublic())).getY();
+            fDH = f.toByteArray();
+            
+            KeyFactory myKeyFac = KeyFactory.getInstance("DH");
+            DHPublicKeySpec keySpec = new DHPublicKeySpec(e, p, g);
+            PublicKey yourPubKey = myKeyFac.generatePublic(keySpec);
+            myKeyAgree.doPhase(yourPubKey, true);
+            byte[] mySharedSecret = myKeyAgree.generateSecret();
+            kDH = mySharedSecret;
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+	
+	private void generateHostKey() {
+        try {
+            KeyPairGenerator generator = KeyPairGenerator.getInstance("DSA");
+            dsa = generator.generateKeyPair();
+            
+            byte[] string = "ssh-dss".getBytes();
+            byte[] stringLength = ByteBuffer.allocate(4).putInt(string.length).array();
+            
+            byte[] p = ((DSAPublicKey) dsa.getPublic()).getParams().getP().toByteArray();
+            if(p[0] != 0x00) p = concat(new byte[]{0x00}, p);
+            byte[] pLength = ByteBuffer.allocate(4).putInt(p.length).array();
+            
+            byte[] q = ((DSAPublicKey) dsa.getPublic()).getParams().getQ().toByteArray();
+            if(q[0] != 0x00) q = concat(new byte[]{0x00}, q);
+            byte[] qLength = ByteBuffer.allocate(4).putInt(q.length).array();
+            
+            byte[] g = ((DSAPublicKey) dsa.getPublic()).getParams().getG().toByteArray();
+            if(g[0] != 0x00) g = concat(new byte[]{0x00}, g);
+            byte[] gLength = ByteBuffer.allocate(4).putInt(g.length).array();
+            
+            byte[] y = ((DSAPublicKey) dsa.getPublic()).getY().toByteArray();
+            if(y[0] != 0x00) y = concat(new byte[]{0x00}, y);
+            byte[] yLength = ByteBuffer.allocate(4).putInt(y.length).array();
+            
+            K_S = concat(stringLength, string, pLength, p, qLength, q, gLength, g, yLength, y);       		
+        } catch (Exception e) {
+        	e.printStackTrace();
+        }
+	}
+	
+	private void generateSha1Hash() {
+		try {
+			MessageDigest sha = MessageDigest.getInstance("SHA-1");
+			sha.update(V_C);
+			sha.update(V_S);
+			sha.update(I_C);
+			sha.update(I_S);
+			sha.update(K_S);
+			sha.update(eDH);
+			sha.update(fDH);
+			sha.update(kDH);
+			hDH = sha.digest();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+	
+	private void generateSignature() {		
+		try {
+			Signature sig = Signature.getInstance("SHA1withDSA");
+            sig.initVerify(dsa.getPublic());
+            sig.initSign(dsa.getPrivate());
+            sig.update(hDH);
+            sigH = doStuff(sig.sign());
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+	
+	private void extractType(byte[] request) {
+		V_C = new byte[request.length - 10];
+		for(int i = 0; i < V_C.length; i++) {
+			if(request[i] == 0x0d) break;
+			V_C[i] = request[i+8];
+		}
+	}
+	
+	private void extractCookie(byte[] request) {
+		I_C = new byte[16];
+		for(int i = 0; i < I_C.length; i++) {
+			I_C[i] = request[i+6];
+		}
+	}
+	
+	private void extractPubKey(byte[] request) {
+		eDH = new byte[byteToInt(new byte[] {request[6], request[7], request[8], request[9]})];
+		for(int i = 0; i < eDH.length; i++) {
+			eDH[i] = request[i+10];
+		}
+	}
+	
+	private static int byteToInt(byte[] bytes) {
+		  int ret = 0;
+		  for (int i=0; i < bytes.length; i++) {
+		    ret <<= 8;
+		    ret |= (int)bytes[i] & 0xFF;
+		  }
+		  return ret;
+	}
+	
+	private byte[] randomBytes(int size) {
+		byte[] bytes = new byte[size];
+		Random rdm = new Random();
+		rdm.nextBytes(bytes);
+		return bytes;		
+	}
+	
+	private byte[] concat(byte[]...bytes) {
+		int newSize = 0;	
+		for(byte[] b: bytes) newSize += b.length;		//get total new size
+		byte[] dst = new byte[newSize];				//create new array with new size
+		
+		int currentPos = 0;				
+		int newPos;	
+		for(byte[] b:bytes) {									//for each elem b out of bytes
+			newPos = b.length;									//get b.length and new position
+			System.arraycopy(b, 0, dst, currentPos, newPos);	//copy b in dst from currentPos to newPos
+			currentPos += newPos;								//increase currentPos to newPos + currentPos
+		}
+		return dst;
+	}
+
+	private byte[] doStuff(byte[] sig) {
+        // sig is in ASN.1
+        // SEQUENCE::={ r INTEGER, s INTEGER }
+        int len = 0;
+        int index = 3;
+        len = sig[index++] & 0xff;
+        byte[] r = new byte[len];
+        System.arraycopy(sig, index, r, 0, r.length);
+        index = index + len + 1;
+        len = sig[index++] & 0xff;
+        byte[] s = new byte[len];
+        System.arraycopy(sig, index, s, 0, s.length);
+
+        byte[] result = new byte[40];
+
+        // result must be 40 bytes, but length of r and s may not be 20 bytes
+
+        System.arraycopy(r,
+                         (r.length > 20) ? 1 : 0,
+                         result,
+                         (r.length > 20) ? 0 : 20 - r.length,
+                         (r.length > 20) ? 20 : r.length);
+        System.arraycopy(s,
+                         (s.length > 20) ? 1 : 0,
+                         result,
+                         (s.length > 20) ? 20 : 40 - s.length,
+                         (s.length > 20) ? 20 : s.length);
+
+        return result;
+    }
+
 
 }

+ 2 - 1
src/de/tudarmstadt/informatik/hostage/protocol/TELNET.java

@@ -30,7 +30,8 @@ public final class TELNET implements Protocol<ByteArray> {
 
 	@Override
 	public List<ByteArray> processMessage(ByteArray message) {
-		byte[] request = message.get();
+		byte[] request = null;
+		if(message != null) request = message.get();
 		List<ByteArray> response = new ArrayList<ByteArray>();
 		switch (state) {
 		case NONE: