# Python module to make it easy to manually encode SSH packets, by # supporting the various uint32, string, mpint primitives. # # The idea of this is that you can use it to manually construct key # exchange sequences of interesting kinds, for testing purposes. import struct, random def boolean(b): return "\1" if b else "\0" def byte(b): assert 0 <= b < 0x100 return chr(b) def uint32(u): assert 0 <= u < 0x100000000 return struct.pack(">I", u) def uint64(u): assert 0 <= u < 0x10000000000000000 return struct.pack(">L", u) def string(s): return uint32(len(s)) + s def mpint(m): s = "" lastbyte = 0 while m > 0: lastbyte = m & 0xFF s = chr(lastbyte) + s m >>= 8 if lastbyte & 0x80: s = "\0" + s return string(s) def name_list(ns): s = "" for n in ns: assert "," not in n if s != "": s += "," s += n return string(s) def ssh_rsa_key_blob(modulus, exponent): return string(string("ssh-rsa") + mpint(modulus) + mpint(exponent)) def ssh_rsa_signature_blob(signature): return string(string("ssh-rsa") + mpint(signature)) def greeting(string): # Greeting at the start of an SSH connection. return string + "\r\n" # Packet types. SSH2_MSG_DISCONNECT = 1 SSH2_MSG_IGNORE = 2 SSH2_MSG_UNIMPLEMENTED = 3 SSH2_MSG_DEBUG = 4 SSH2_MSG_SERVICE_REQUEST = 5 SSH2_MSG_SERVICE_ACCEPT = 6 SSH2_MSG_KEXINIT = 20 SSH2_MSG_NEWKEYS = 21 SSH2_MSG_KEXDH_INIT = 30 SSH2_MSG_KEXDH_REPLY = 31 SSH2_MSG_KEX_DH_GEX_REQUEST_OLD = 30 SSH2_MSG_KEX_DH_GEX_GROUP = 31 SSH2_MSG_KEX_DH_GEX_INIT = 32 SSH2_MSG_KEX_DH_GEX_REPLY = 33 SSH2_MSG_KEX_DH_GEX_REQUEST = 34 SSH2_MSG_KEXRSA_PUBKEY = 30 SSH2_MSG_KEXRSA_SECRET = 31 SSH2_MSG_KEXRSA_DONE = 32 SSH2_MSG_USERAUTH_REQUEST = 50 SSH2_MSG_USERAUTH_FAILURE = 51 SSH2_MSG_USERAUTH_SUCCESS = 52 SSH2_MSG_USERAUTH_BANNER = 53 SSH2_MSG_USERAUTH_PK_OK = 60 SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ = 60 SSH2_MSG_USERAUTH_INFO_REQUEST = 60 SSH2_MSG_USERAUTH_INFO_RESPONSE = 61 SSH2_MSG_GLOBAL_REQUEST = 80 SSH2_MSG_REQUEST_SUCCESS = 81 SSH2_MSG_REQUEST_FAILURE = 82 SSH2_MSG_CHANNEL_OPEN = 90 SSH2_MSG_CHANNEL_OPEN_CONFIRMATION = 91 SSH2_MSG_CHANNEL_OPEN_FAILURE = 92 SSH2_MSG_CHANNEL_WINDOW_ADJUST = 93 SSH2_MSG_CHANNEL_DATA = 94 SSH2_MSG_CHANNEL_EXTENDED_DATA = 95 SSH2_MSG_CHANNEL_EOF = 96 SSH2_MSG_CHANNEL_CLOSE = 97 SSH2_MSG_CHANNEL_REQUEST = 98 SSH2_MSG_CHANNEL_SUCCESS = 99 SSH2_MSG_CHANNEL_FAILURE = 100 SSH2_MSG_USERAUTH_GSSAPI_RESPONSE = 60 SSH2_MSG_USERAUTH_GSSAPI_TOKEN = 61 SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE = 63 SSH2_MSG_USERAUTH_GSSAPI_ERROR = 64 SSH2_MSG_USERAUTH_GSSAPI_ERRTOK = 65 SSH2_MSG_USERAUTH_GSSAPI_MIC = 66 def clearpkt(msgtype, *stuff): # SSH-2 binary packet, in the cleartext format used for initial # setup and kex. s = byte(msgtype) for thing in stuff: s += thing padlen = 0 while padlen < 4 or len(s) % 8 != 3: padlen += 1 s += byte(random.randint(0,255)) s = byte(padlen) + s return string(s) def decode_uint32(s): assert len(s) == 4 return struct.unpack(">I", s)[0] def read_clearpkt(fh): length_field = fh.read(4) s = fh.read(decode_uint32(length_field)) import sys padlen = ord(s[0]) s = s[1:-padlen] msgtype = ord(s[0]) return msgtype, s[1:]