MySQL.java 4.9 KB

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