/main/sshmessage.py

https://github.com/stribika/sshlabs · Python · 219 lines · 193 code · 0 blank · 26 comment · 0 complexity · 04420eff4ad4d26ab43f877c69bf1b5c MD5 · raw file

  1. """
  2. This might look like an SSH protocol implementation. IT IS NOT. It's not even
  3. remotely secure. Nothing is verified, nothing is random. DO NOT USE.
  4. """
  5. import binascii
  6. import struct
  7. from sshtransport import BinaryPacket
  8. from sshtype import *
  9. SSH_MSG_KEXINIT = 20
  10. SSH_MSG_KEX_DH_GEX_REQUEST_OLD = 30
  11. SSH_MSG_KEX_DH_GEX_GROUP = 31
  12. SSH_MSG_KEX_DH_GEX_INIT = 32
  13. SSH_MSG_KEX_DH_GEX_REPLY = 33
  14. SSH_MSG_KEX_DH_GEX_REQUEST = 34
  15. def message_from_packet(binpkt):
  16. message_types = {
  17. SSH_MSG_KEXINIT: KexInit,
  18. SSH_MSG_KEX_DH_GEX_REQUEST: DHGEXRequest,
  19. SSH_MSG_KEX_DH_GEX_GROUP: DHGEXGroup,
  20. }
  21. return message_types[binpkt.payload[0]](packet=binpkt)
  22. class SSHStruct(object):
  23. def __init__(self, parse_attr, *args, **kwargs):
  24. object.__setattr__(self, "_SSHStruct__structure", args)
  25. object.__setattr__(self, "_SSHStruct__values", {})
  26. for arg in args:
  27. self.__values[arg.name] = arg.default
  28. if parse_attr in kwargs:
  29. self.parse(kwargs[parse_attr])
  30. else:
  31. kwargs_set = set(kwargs)
  32. valid_set = set([ s.name for s in self.__structure ])
  33. if not kwargs_set.issubset(valid_set):
  34. raise TypeError(
  35. "unexpected arguments: " + ", ".join(kwargs_set.difference(valid_set))
  36. )
  37. self.__values.update(kwargs)
  38. def __getattr__(self, name):
  39. if name in self.__values:
  40. return self.__values[name]
  41. else:
  42. raise AttributeError("'{0}' object has no attribute '{1}'".format(
  43. type(self).__name__,
  44. name
  45. ))
  46. def __setattr__(self, name, value):
  47. if name in self.__values:
  48. self.__values[name] = value
  49. else:
  50. raise AttributeError("'{0}' object has no attribute '{1}'".format(
  51. type(self).__name__,
  52. name
  53. ))
  54. def __dir__(self):
  55. return object.__dir__(self) + list(self.__values.keys())
  56. def __eq__(self, value):
  57. return type(self) == type(value) and self.__values == value.__values
  58. def __hash__(self):
  59. return hash(( type(self), frozenset(self.__values.items()) ))
  60. def __str__(self):
  61. return "{0}({1})".format(
  62. type(self).__name__,
  63. ", ".join([ s.name + "=" + s.to_str(self.__values[s.name]) for s in self.__structure ])
  64. )
  65. def parse(self, x):
  66. self.from_bytes(x)
  67. def from_bytes(self, data):
  68. for s in self.__structure:
  69. ( data, value ) = s.from_bytes(data)
  70. self.__values[s.name] = value
  71. def to_bytes(self):
  72. data = b""
  73. for s in self.__structure:
  74. data += s.to_bytes(self.__values[s.name])
  75. return data
  76. def to_dict(self):
  77. result = {}
  78. for key, value in self.__values.items():
  79. result[key] = binascii.hexlify(value).decode() if type(value) == bytes else value
  80. return result
  81. class SSHMessage(SSHStruct):
  82. def __init__(self, message_type, *args, **kwargs):
  83. object.__setattr__(self, "_SSHMessage__message_type", message_type)
  84. super(SSHMessage, self).__init__("packet", *args, **kwargs)
  85. def parse(self, x):
  86. self.from_packet(x)
  87. def from_packet(self, packet):
  88. data = packet.payload
  89. if data[0] != self.__message_type:
  90. raise RuntimeError("invalid type {0}, expected {1}".format(
  91. data[0],
  92. self.__message_type
  93. ))
  94. self.from_bytes(data[1:])
  95. def to_packet(self):
  96. data = bytes([ self.__message_type ])
  97. data += self.to_bytes()
  98. return BinaryPacket(payload=data)
  99. class KexInit(SSHMessage):
  100. def __init__(self, **kwargs):
  101. super(type(self), self).__init__(
  102. SSH_MSG_KEXINIT,
  103. Bytes(16, "cookie", b"\x00" * 16),
  104. NameList("kex_algorithms", []),
  105. NameList("server_host_key_algorithms", []),
  106. NameList("encryption_algorithms_c2s", []),
  107. NameList("encryption_algorithms_s2c", []),
  108. NameList("mac_algorithms_c2s", []),
  109. NameList("mac_algorithms_s2c", []),
  110. NameList("compression_algorithms_c2s", []),
  111. NameList("compression_algorithms_s2c", []),
  112. NameList("languages_c2s", []),
  113. NameList("languages_s2c", []),
  114. Boolean("first_kex_packet_follows", False),
  115. UInt32("reserved", 0),
  116. **kwargs
  117. )
  118. def optimal_response(self):
  119. return KexInit(
  120. kex_algorithms=self.kex_algorithms,
  121. server_host_key_algorithms=self.server_host_key_algorithms,
  122. encryption_algorithms_c2s=self.encryption_algorithms_c2s,
  123. encryption_algorithms_s2c=self.encryption_algorithms_s2c,
  124. mac_algorithms_c2s=self.mac_algorithms_c2s,
  125. mac_algorithms_s2c=self.mac_algorithms_s2c,
  126. compression_algorithms_c2s=self.compression_algorithms_c2s,
  127. compression_algorithms_s2c=self.compression_algorithms_s2c,
  128. languages_c2s=self.languages_c2s,
  129. languages_s2c=self.languages_s2c
  130. )
  131. def __str__(self):
  132. def strlist(name, value):
  133. return name + ": " + ", ".join(value)
  134. return "\n".join([
  135. strlist("Key exchange algorithms", self.kex_algorithms),
  136. strlist("Host key algorithms", self.server_host_key_algorithms),
  137. strlist("Encryption algorithms (client to server)", self.encryption_algorithms_c2s),
  138. strlist("Encryption algorithms (server to client)", self.encryption_algorithms_s2c),
  139. strlist("MAC algorithms (client to server)", self.mac_algorithms_c2s),
  140. strlist("MAC algorithms (server to client)", self.mac_algorithms_s2c),
  141. ])
  142. class DHGEXRequest(SSHMessage):
  143. def __init__(self, **kwargs):
  144. super(type(self), self).__init__(
  145. SSH_MSG_KEX_DH_GEX_REQUEST,
  146. UInt32("min", 1024),
  147. UInt32("n"),
  148. UInt32("max", 8192),
  149. **kwargs
  150. )
  151. class DHGEXGroup(SSHMessage):
  152. def __init__(self, **kwargs):
  153. super(type(self), self).__init__(
  154. SSH_MSG_KEX_DH_GEX_GROUP,
  155. MPInt("prime"),
  156. MPInt("generator"),
  157. **kwargs
  158. )
  159. class DHGEXInit(SSHMessage):
  160. def __init__(self, **kwargs):
  161. super(type(self), self).__init__(SSH_MSG_KEX_DH_GEX_INIT, MPInt("e"), **kwargs)
  162. class DHGEXReply(SSHMessage):
  163. def __init__(self, **kwargs):
  164. super(DHGEXReply, self).__init__(
  165. SSH_MSG_KEX_DH_GEX_REPLY,
  166. String("server_public_key"),
  167. MPInt("f"),
  168. String("signature"),
  169. **kwargs
  170. )
  171. class RSAPublicKey(SSHStruct):
  172. def __init__(self, **kwargs):
  173. super(RSAPublicKey, self).__init__(
  174. "data",
  175. String("key_type"),
  176. MPInt("public_exponent"),
  177. MPInt("modulus"),
  178. **kwargs
  179. )
  180. if self.key_type != b"ssh-rsa":
  181. raise RuntimeError("invalid key type {0}, expected 'ssh-rsa'".format(
  182. self.key_type.decode("ASCII"),
  183. ))