MySQL.java 4.8 KB

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