SSH.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. package de.tudarmstadt.informatik.hostage.protocol;
  2. import java.math.BigInteger;
  3. import java.nio.ByteBuffer;
  4. import java.security.SecureRandom;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. import de.tudarmstadt.informatik.hostage.commons.HelperUtils;
  8. import de.tudarmstadt.informatik.hostage.ssh.crypto.KeyMaterial;
  9. import de.tudarmstadt.informatik.hostage.ssh.crypto.PEMDecoder;
  10. import de.tudarmstadt.informatik.hostage.ssh.crypto.cipher.CBCMode;
  11. import de.tudarmstadt.informatik.hostage.ssh.crypto.cipher.DESede;
  12. import de.tudarmstadt.informatik.hostage.ssh.crypto.dh.DhExchange;
  13. import de.tudarmstadt.informatik.hostage.ssh.crypto.digest.MAC;
  14. import de.tudarmstadt.informatik.hostage.ssh.signature.DSAPrivateKey;
  15. import de.tudarmstadt.informatik.hostage.ssh.signature.DSASHA1Verify;
  16. import de.tudarmstadt.informatik.hostage.ssh.signature.DSASignature;
  17. import de.tudarmstadt.informatik.hostage.ssh.util.TypesWriter;
  18. import de.tudarmstadt.informatik.hostage.wrapper.Packet;
  19. /**
  20. * SSH protocol.
  21. * @author Wulf Pfeiffer
  22. */
  23. public class SSH implements Protocol {
  24. /**
  25. * Represents the states of the protocol.
  26. */
  27. private enum STATE {
  28. NONE,
  29. SERVER_VERSION,
  30. CLIENT_VERSION,
  31. KEX_INIT,
  32. USERAUTH,
  33. CONNECTION,
  34. CHANNEL,
  35. LOOP, // TODO for debugging
  36. CLOSED
  37. }
  38. /**
  39. * Denotes in which state the protocol is right now.
  40. */
  41. private STATE state = STATE.NONE;
  42. @Override
  43. public int getPort() {
  44. return 22;
  45. }
  46. @Override
  47. public TALK_FIRST whoTalksFirst() {
  48. return TALK_FIRST.SERVER;
  49. }
  50. @Override
  51. public List<Packet> processMessage(Packet packet) {
  52. List<Packet> response = new ArrayList<Packet>();
  53. byte[] request = null;
  54. if(packet != null) {
  55. request = packet.getMessage();
  56. }
  57. switch(state) {
  58. case NONE:
  59. response.add(new Packet(serverVersion + serverType + "\r\n"));
  60. state = STATE.SERVER_VERSION;
  61. break;
  62. case SERVER_VERSION:
  63. extractType(request);
  64. extractPayload(request);
  65. response.add(kexInit());
  66. state = STATE.CLIENT_VERSION;
  67. break;
  68. case CLIENT_VERSION:
  69. extractPubKey(request);
  70. response.add(dhKexReply());
  71. response.add(newKeys());
  72. state = STATE.KEX_INIT;
  73. break;
  74. case KEX_INIT:
  75. response.add(serviceReply(request));
  76. state = STATE.USERAUTH;
  77. break;
  78. case USERAUTH:
  79. response.add(userauthReply(request));
  80. state = STATE.CONNECTION;
  81. break;
  82. case CONNECTION:
  83. response.add(channelOpenReply(request));
  84. state = STATE.CHANNEL;
  85. break;
  86. case CHANNEL:
  87. response.add(channelSuccessReply(request));
  88. response.add(data(request));
  89. state = STATE.LOOP;
  90. break;
  91. case LOOP:
  92. decryptPacket(request);
  93. response.add(data(request));
  94. state = STATE.LOOP;
  95. break;
  96. case CLOSED:
  97. break;
  98. default:
  99. state = STATE.CLOSED;
  100. break;
  101. }
  102. return response;
  103. }
  104. @Override
  105. public boolean isClosed() {
  106. return (state == STATE.CLOSED);
  107. }
  108. @Override
  109. public boolean isSecure() {
  110. return false;
  111. }
  112. @Override
  113. public Class<byte[]> getType() {
  114. return byte[].class;
  115. }
  116. @Override
  117. public String toString() {
  118. return "SSH";
  119. }
  120. /**
  121. * Wraps the packets with packet length and padding.
  122. * @param packet content that is wrapped.
  123. * @return wrapped packet.
  124. */
  125. private Packet wrapPacket(byte[] packet, boolean encrypt) {
  126. int packetLength = 5 + packet.length; //4 byte packet length, 1 byte padding length, payload length
  127. int paddingLengthCBS = cipherBlockSize - (packetLength % cipherBlockSize);
  128. int paddingLength8 = 8 - (packetLength % 8);
  129. int paddingLength = paddingLengthCBS > paddingLength8 ? paddingLengthCBS : paddingLength8;
  130. if(paddingLength < 4) paddingLength += cipherBlockSize;
  131. packetLength = packetLength + paddingLength - 4; //add padding string length to packet length
  132. byte[] packetLen = ByteBuffer.allocate(4).putInt(packetLength).array();
  133. byte[] paddingLen = {(byte) paddingLength};
  134. byte[] paddingString = new byte[paddingLength];
  135. for(int i = 0; i < paddingLength; i++) {
  136. SecureRandom rndm = new SecureRandom();
  137. paddingString[i] = (byte) rndm.nextInt(255);
  138. }
  139. byte[] response = HelperUtils.concat(packetLen, paddingLen, packet, paddingString);
  140. if(encrypt) {
  141. byte[] mac = new byte[20];
  142. macEnc.initMac(send_packet_number);
  143. macEnc.update(response, 0, response.length);
  144. macEnc.getMac(mac, 0);
  145. byte[] responseEnc = new byte[response.length];
  146. for(int i = 0; i < response.length; i+=8) {
  147. cbcEnc.transformBlock(response, i, responseEnc, i);
  148. }
  149. response = HelperUtils.concat(responseEnc,mac);
  150. }
  151. send_packet_number++;
  152. return new Packet(response);
  153. }
  154. private byte[] decryptPacket(byte[] request) {
  155. byte[] message = new byte[request.length - ((request.length % 8 == 0) ? 0 : 20)];
  156. for(int i = 0; i < message.length; i += 8) { // -12 wegen MAC
  157. cbcDec.transformBlock(request, i, message, i);
  158. }
  159. System.out.println("Encrypted packet:");
  160. System.out.println(HelperUtils.bytesToHexString(request));
  161. System.out.println("\nDecrypted packet:");
  162. System.out.println(HelperUtils.bytesToHexString(message));
  163. System.out.println("\nDecrypted string packet:");
  164. System.out.println(HelperUtils.byteToStr(message));
  165. System.out.println();
  166. return message;
  167. }
  168. /**
  169. * Builds the Kex Init packet that contains all the allowed algorithms by the server.
  170. * @return Kex Init packet.
  171. */
  172. private Packet kexInit() {
  173. byte[] msgCode = {0x14};
  174. byte[] cookie = HelperUtils.randomBytes(16);
  175. byte[] kexLength = ByteBuffer.allocate(4).putInt(kex_alg.getBytes().length).array();
  176. byte[] serverLength = ByteBuffer.allocate(4).putInt(server_alg.getBytes().length).array();
  177. byte[] encrypt_c_Length = ByteBuffer.allocate(4).putInt(encrypt_alg_c.getBytes().length).array();
  178. byte[] encrypt_s_Length = ByteBuffer.allocate(4).putInt(encrypt_alg_s.getBytes().length).array();
  179. byte[] mac_c_Length = ByteBuffer.allocate(4).putInt(mac_alg_c.getBytes().length).array();
  180. byte[] mac_s_Length = ByteBuffer.allocate(4).putInt(mac_alg_s.getBytes().length).array();
  181. byte[] comp_c_Length = ByteBuffer.allocate(4).putInt(comp_alg_c.getBytes().length).array();
  182. byte[] comp_s_Length = ByteBuffer.allocate(4).putInt(comp_alg_s.getBytes().length).array();
  183. byte[] language_c_s = {0x00, 0x00, 0x00, 0x00};
  184. byte[] language_s_c = {0x00, 0x00, 0x00, 0x00};
  185. byte[] kexFirsPckt = {0x00};
  186. byte[] reserved = {0x00, 0x00, 0x00, 0x00};
  187. byte[] response = HelperUtils.concat(msgCode, cookie, kexLength, kex_alg.getBytes(), serverLength, server_alg.getBytes(),
  188. encrypt_c_Length, encrypt_alg_c.getBytes(), encrypt_s_Length, encrypt_alg_s.getBytes(), mac_c_Length, mac_alg_c.getBytes(),
  189. mac_s_Length, mac_alg_s.getBytes(), comp_c_Length, comp_alg_c.getBytes(), comp_s_Length, comp_alg_s.getBytes(),
  190. language_c_s, language_s_c, kexFirsPckt, reserved);
  191. I_S = response;
  192. return wrapPacket(response, false);
  193. }
  194. /**
  195. * Builds the Diffie-Hellman Kex Reply, containing the host key,f and the signature.
  196. * @return Diffie-Hellman Kex Reply packet.
  197. */
  198. private Packet dhKexReply() {
  199. //TODO make it small
  200. try {
  201. SecureRandom rnd = new SecureRandom();
  202. DhExchange dhx = new DhExchange();
  203. dhx.serverInit(1, rnd);
  204. dhx.setE(new BigInteger(e));
  205. f = dhx.getF();
  206. DSAPrivateKey dsa = (DSAPrivateKey) PEMDecoder.decode(dsa_pem, null);
  207. K_S = DSASHA1Verify.encodeSSHDSAPublicKey(dsa.getPublicKey());
  208. h = dhx.calculateH(V_C, V_S, I_C, I_S, K_S);
  209. k = dhx.getK();
  210. DSASignature ds = DSASHA1Verify.generateSignature(h, dsa, rnd);
  211. sig = DSASHA1Verify.encodeSSHDSASignature(ds);
  212. TypesWriter tw = new TypesWriter();
  213. tw.writeByte(31);
  214. tw.writeString(K_S, 0, K_S.length);
  215. tw.writeMPInt(f);
  216. tw.writeString(sig, 0, sig.length);
  217. byte[] response = tw.getBytes();
  218. //init for decryption and encryption
  219. KeyMaterial km = KeyMaterial.create("SHA1", h, k, h, 24, 8, 20, 24, 8, 20); // alg, h, k, keylength, blocklength, maclength, keylength, blocklength, maclength
  220. enc = new DESede();
  221. dec = new DESede();
  222. enc.init(true, km.enc_key_server_to_client);
  223. dec.init(false, km.enc_key_client_to_server);
  224. cbcEnc = new CBCMode(enc, km.initial_iv_server_to_client, true);
  225. cbcDec = new CBCMode(dec, km.initial_iv_client_to_server, false);
  226. macEnc = new MAC("hmac-sha1", km.integrity_key_server_to_client);
  227. macDec = new MAC("hmac-sha1", km.integrity_key_client_to_server);
  228. return wrapPacket(response, false);
  229. } catch (Exception e) {
  230. e.printStackTrace();
  231. }
  232. return wrapPacket(null, false);
  233. }
  234. /**
  235. * New Keys response.
  236. * @return New Keys response.
  237. */
  238. private Packet newKeys() {
  239. byte[] msgCode = {0x15};
  240. return wrapPacket(msgCode, false);
  241. }
  242. // TODO
  243. private Packet serviceReply(byte[] request) {
  244. byte[] messageBuf;
  245. if(request[5] == 0x15) { //if newkeys request is included in the same packet
  246. messageBuf = new byte[request.length - 16];
  247. System.arraycopy(request, 16, messageBuf, 0, request.length-16);
  248. } else {
  249. messageBuf = request;
  250. }
  251. byte[] message = decryptPacket(messageBuf);
  252. if (message[5] != 0x05 && message[9] != 0x0c) {
  253. // TODO disconnect because its not servicerequest ssh-userauth
  254. }
  255. byte[] msgcode = {0x06};
  256. byte[] serviceLength;
  257. byte[] service = "ssh-userauth".getBytes();
  258. serviceLength = ByteBuffer.allocate(4).putInt(service.length).array();
  259. byte[] response = HelperUtils.concat(msgcode, serviceLength, service);
  260. return wrapPacket(response, true);
  261. }
  262. // TODO
  263. private Packet userauthReply(byte[] request) {
  264. byte[] message = decryptPacket(request);
  265. if (message[5] != 0x32) {
  266. // TODO disconnect because its not servicerequest ssh-connect
  267. }
  268. byte[] msgcode = {0x34};
  269. byte[] response = msgcode;
  270. return wrapPacket(response, true);
  271. }
  272. // TODO
  273. private Packet channelOpenReply(byte[] request) {
  274. byte[] message = decryptPacket(request);
  275. // TODO if contains "session" ok else disc
  276. byte[] msgcode = {0x5b};
  277. recipientChannel = new byte[4];
  278. byte[] senderChannel = new byte[4];
  279. byte[] initialWindowSize = new byte[4];
  280. byte[] maximumPacketSize = new byte[4];
  281. System.arraycopy(message, 17, recipientChannel, 0, 4); //TODO dynamic position
  282. System.arraycopy(message, 17, senderChannel, 0, 4);
  283. System.arraycopy(message, 21, initialWindowSize, 0, 4);
  284. System.arraycopy(message, 25, maximumPacketSize, 0, 4);
  285. byte[] response = HelperUtils.concat(msgcode, recipientChannel, senderChannel, initialWindowSize, maximumPacketSize);
  286. System.out.println("CHANNEL REPLY:");
  287. System.out.println(HelperUtils.bytesToHexString(response));
  288. return wrapPacket(response, true);
  289. }
  290. byte[] recipientChannel; //TODO
  291. private Packet channelSuccessReply(byte[] request) {
  292. decryptPacket(request);
  293. byte[] msgcode = {0x63}; //TODO check msg type
  294. byte[] response = HelperUtils.concat(msgcode, recipientChannel);
  295. System.out.println(HelperUtils.bytesToHexString(response));
  296. return wrapPacket(response, true);
  297. }
  298. private Packet data(byte[] request) {
  299. byte[] msgcode = {0x5E};
  300. byte[] msglength = new byte[4];
  301. byte[] msg = "lolol".getBytes();
  302. msglength = ByteBuffer.allocate(4).putInt(msg.length).array();
  303. byte[] response = HelperUtils.concat(msgcode, recipientChannel, msglength, msg);
  304. return wrapPacket(response, true);
  305. }
  306. /**
  307. * Extracts the type of the client
  308. * @param request containing the clients type
  309. */
  310. private void extractType(byte[] request) {
  311. int length = 0;
  312. for(int i = 0; i < request.length; i++, length++) {
  313. if(request[i] == 0x0d) break; //find the end of the type: '\r'
  314. }
  315. V_C = new byte[length];
  316. System.arraycopy(request, 0, V_C, 0, length);
  317. }
  318. /**
  319. * Extracts the payload of a packet and writes it in I_C.
  320. * @param request packet of which the payload is extracted.
  321. */
  322. private void extractPayload(byte[] request) {
  323. int pos = 0;
  324. if(request[5] != 0x14) {
  325. pos = 1;
  326. for(int i = 0; i < request.length; i++, pos++) {
  327. if(request[i] == 0xa) break;
  328. }
  329. }
  330. int packetLength = byteToInt(new byte[]{request[pos], request[1+pos], request[2+pos], request[3+pos]});
  331. int paddingLength = byteToInt(new byte[]{request[4+pos]});
  332. byte[] payload = new byte[packetLength - paddingLength - 1];
  333. for(int i = 5; i < packetLength - paddingLength - 1; i++) {
  334. payload[i-5] = request[i+pos];
  335. }
  336. I_C = payload;
  337. }
  338. /**
  339. * Extracts the public key from the DH Kex Request
  340. * @param request containing the clients public key
  341. */
  342. private void extractPubKey(byte[] request) {
  343. e = new byte[byteToInt(new byte[] {request[6], request[7], request[8], request[9]})];
  344. for(int i = 0; i < e.length; i++) {
  345. e[i] = request[i+10];
  346. }
  347. }
  348. /**
  349. * Converts a byte[] to int
  350. * @param bytes that are converted
  351. * @return converted byte[] as int
  352. */
  353. private static int byteToInt(byte[] bytes) {
  354. int ret = 0;
  355. for (int i=0; i < bytes.length; i++) {
  356. ret <<= 8;
  357. ret |= bytes[i] & 0xFF;
  358. }
  359. return ret;
  360. }
  361. private String serverVersion = ProtocolSettings.getSshVersion();
  362. private String serverType = ProtocolSettings.getSshType();
  363. private int send_packet_number = 0;
  364. //SSH Parameters for Kex etc.
  365. private byte[] V_S = (serverVersion + serverType).getBytes();
  366. private byte[] V_C;
  367. private byte[] I_S;
  368. private byte[] I_C;
  369. private byte[] e;
  370. private BigInteger f;
  371. private byte[] h;
  372. private BigInteger k;
  373. private byte[] K_S;
  374. private byte[] sig;
  375. //allowed algorithms for kexinit
  376. private String kex_alg = "diffie-hellman-group1-sha1";
  377. private String server_alg = "ssh-dss";
  378. private String encrypt_alg_c = "3des-cbc";
  379. private String encrypt_alg_s = "3des-cbc";
  380. private String mac_alg_c = "hmac-sha1";
  381. private String mac_alg_s = "hmac-sha1";
  382. private String comp_alg_c = "none";
  383. private String comp_alg_s = "none";
  384. private int cipherBlockSize = 16;
  385. DESede enc;
  386. DESede dec;
  387. CBCMode cbcEnc;
  388. CBCMode cbcDec;
  389. MAC macEnc;
  390. MAC macDec;
  391. //dsa private key
  392. private final char[] dsa_pem = ("-----BEGIN DSA PRIVATE KEY-----\n" +
  393. "MIIBugIBAAKBgQCDZ9R2vfCPwjv5vKF1igIv9drrZ7G0dhMkGT9AZTjgI34Qm4w0\n" +
  394. "0iWeCqO7SmqiaMIjbRIm91MeDed4ObAq4sAkqRE/2P4mTbzFx5KhEczRRiDoqQBX\n" +
  395. "xYa0yWKJpeZ94SGM6DEPuBTxKo0T4uMjbq2FzHL2FXT1/WoNCmRU6gFSiwIVAMK4\n" +
  396. "Epz3JiwDUbkSpLOjIqtEhJmVAoGAL6zlXRI4Q8iwvSDh0vDf1j9a5Aaaq+93LTjK\n" +
  397. "SwL4nvUWBl2Aa0vqu05ZS5rOD1I+/naLMg0fNgFJRhA03sl+12MI3a2HXJWXRSdj\n" +
  398. "m1Vq9cUXqiYrX6+iGfEaA/y9UO4ZPF6if6eLypXB8VuqjtjDCiMMsM6+qQki7L71\n" +
  399. "yN4M75ICgYAcFXUhN2zRug3JvwmGxW8gMgHquSiBnbx1582KGh2B/ukE/kOrbKYD\n" +
  400. "HUkBzolcm4x1Odq5apowlriFxY6zMQP615plIK4x9NaU6dvc/HoTkjzT5EYSMN39\n" +
  401. "eAGufJ0jrtIpKL4lP8o8yrAHfmbR7bjecWc0viTH0+OWlyVsex/bZAIUEKn310Li\n" +
  402. "v62Zs4hlDvhwvx8MQ+A=\n" +
  403. "-----END DSA PRIVATE KEY-----").toCharArray();
  404. }