MySQL.java 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. package de.tudarmstadt.informatik.hostage.protocol;
  2. import java.nio.ByteBuffer;
  3. import java.security.SecureRandom;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6. import de.tudarmstadt.informatik.hostage.commons.HelperUtils;
  7. import de.tudarmstadt.informatik.hostage.wrapper.Packet;
  8. /**
  9. * MySQL protocol
  10. * @author Wulf Pfeiffer
  11. */
  12. public class MySQL implements Protocol {
  13. /**
  14. * Represents the states of the protocol
  15. */
  16. private enum STATE {
  17. NONE, CONNECTED, LOGIN_INFO, CLOSED
  18. }
  19. /**
  20. * Denotes in which state the protocol is right now
  21. */
  22. private STATE state = STATE.NONE;
  23. /** last request from client */
  24. private byte[] lastMessage;
  25. @Override
  26. public List<Packet> processMessage(Packet packet) {
  27. byte[] request = null;
  28. if (packet != null) {
  29. request = packet.getMessage();
  30. }
  31. List<Packet> response = new ArrayList<Packet>();
  32. if(request != null)
  33. lastMessage = request;
  34. switch(state) {
  35. case NONE:
  36. response.add(greeting());
  37. state = STATE.CONNECTED;
  38. break;
  39. case CONNECTED:
  40. response.add(responseOK());
  41. state = STATE.LOGIN_INFO;
  42. break;
  43. case LOGIN_INFO:
  44. if(this.lastMessage[4] == 0x01) {
  45. state = STATE.CLOSED;
  46. } else {
  47. response.add(responseError());
  48. }
  49. break;
  50. default:
  51. state = STATE.CLOSED;
  52. break;
  53. }
  54. return response;
  55. }
  56. @Override
  57. public TALK_FIRST whoTalksFirst() {
  58. return TALK_FIRST.SERVER;
  59. }
  60. @Override
  61. public String toString(){
  62. return "MySQL";
  63. }
  64. @Override
  65. public int getDefaultPort() {
  66. return 3306;
  67. }
  68. @Override
  69. public boolean isClosed() {
  70. return state == STATE.CLOSED;
  71. }
  72. @Override
  73. public boolean isSecure() {
  74. return false;
  75. }
  76. @Override
  77. public Class<byte[]> getType() {
  78. return byte[].class;
  79. }
  80. /**
  81. * Wraps the response packet with the packet length and number
  82. * @param packet that is wrapped
  83. * @return wrapped packet
  84. */
  85. private Packet wrapPacket(byte[] packet) {
  86. byte[] buffer = ByteBuffer.allocate(4).putInt(packet.length).array();
  87. byte[] packetLength = {buffer[3], buffer[2], buffer[1]};
  88. byte[] packetNumber = new byte[1];
  89. if(lastMessage != null) packetNumber[0] = (byte) (lastMessage[3] + 1);
  90. else packetNumber[0] = 0x00;
  91. byte[] response = HelperUtils.concat(packetLength, packetNumber, packet);
  92. return new Packet(response);
  93. }
  94. /**
  95. * Builds the greeting packet that the server sends as first packet
  96. * @return greeting packet
  97. */
  98. private Packet greeting() {
  99. byte[] protocol = {0x0a};
  100. byte[] version = serverVersion.getBytes();
  101. byte[] versionFin = {0x00};
  102. byte[] thread = {0x2a, 0x00, 0x00, 0x00};
  103. byte[] salt = {0x44, 0x64, 0x49, 0x7e, 0x60, 0x48, 0x25, 0x7e, 0x00};
  104. byte[] capabilities = {(byte) 0xff, (byte) 0xf7};
  105. byte[] language = {0x08};
  106. byte[] status = {0x02, 0x00};
  107. byte[] unused = {0x0f, (byte) 0x80, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  108. byte[] salt2 = {0x6c, 0x26, 0x71, 0x2c, 0x25, 0x72, 0x31, 0x3d, 0x7d, 0x21, 0x26, 0x3b, 0x00};
  109. String payload = "mysql_native_password";
  110. byte[] fin = {0x00};
  111. byte[] response = HelperUtils.concat(protocol, version,versionFin, thread, salt, capabilities, language, status, unused, salt2, payload.getBytes(), fin);
  112. return wrapPacket(response);
  113. }
  114. /**
  115. * Builds the ok-response packet
  116. * @return ok-response packet
  117. */
  118. private Packet responseOK() {
  119. byte[] affectedRows = {0x00, 0x00, 0x00};
  120. byte[] status = {0x02, 0x00};
  121. byte[] warnings = {0x00, 0x00};
  122. byte[] response = HelperUtils.concat(affectedRows, status, warnings);
  123. return wrapPacket(response);
  124. }
  125. /**
  126. * Builds the error-response packet
  127. * @return error-response packet
  128. */
  129. private Packet responseError() {
  130. byte[] fill1 = {(byte) 0xff};
  131. byte[] code = {0x17, 0x04};
  132. byte[] fill2 = {0x23};
  133. String state = "08S01";
  134. String msg = "Unknown command";
  135. byte[] response = HelperUtils.concat(fill1, code, fill2, state.getBytes(), msg.getBytes());
  136. return wrapPacket(response);
  137. }
  138. //version stuff
  139. private static String[][][] possibleMysqlVersions = {
  140. {{"5.7."},{"1","2"}},
  141. {{"5.6."},{"2","3","4","5","6","7","8","9","10","11","12","13","14"}},
  142. {{"5.5."},{"27","28","29","30","31","32","33","34"}}
  143. };
  144. private static String initMysqlVersion() {
  145. SecureRandom rndm = new SecureRandom();
  146. int majorVersion = rndm.nextInt(possibleMysqlVersions.length);
  147. return possibleMysqlVersions[majorVersion][0][0] + possibleMysqlVersions[majorVersion][1][rndm.nextInt(possibleMysqlVersions[majorVersion][1].length)];
  148. }
  149. private static String serverVersion = initMysqlVersion();
  150. }