diff --git a/scripts/base/protocols/ssh/dpd.sig b/scripts/base/protocols/ssh/dpd.sig index 95e22908ab..e56878275c 100644 --- a/scripts/base/protocols/ssh/dpd.sig +++ b/scripts/base/protocols/ssh/dpd.sig @@ -1,13 +1,6 @@ -signature dpd_ssh_client { +signature dpd_ssh { ip-proto == tcp - payload /^[sS][sS][hH]-/ - requires-reverse-signature dpd_ssh_server + payload /^[sS][sS][hH]-[12]./ enable "ssh" - tcp-state originator } -signature dpd_ssh_server { - ip-proto == tcp - payload /^[sS][sS][hH]-/ - tcp-state responder -} diff --git a/scripts/base/protocols/ssh/main.bro b/scripts/base/protocols/ssh/main.bro index bba3f8ac88..00f387d432 100644 --- a/scripts/base/protocols/ssh/main.bro +++ b/scripts/base/protocols/ssh/main.bro @@ -12,6 +12,8 @@ export { uid: string &log; ## The connection's 4-tuple of endpoint addresses/ports. id: conn_id &log; + ## SSH major version (1 or 2) + version: count &log; ## Auth result result: string &log &optional; ## Auth method (password, pubkey, etc.) @@ -54,23 +56,36 @@ event bro_init() &priority=5 Analyzer::register_for_ports(Analyzer::ANALYZER_SSH, ports); } -function determine_auth_method(middle_pkt_len: int, first_pkt_len: int): string +function determine_auth_method(version: int, last_pkt_len: int, middle_pkt_len: int, first_pkt_len: int): string { # This is still being tested. # Based on "Analysis for Identifying User Authentication Methods on SSH Connections" # by Satoh, Nakamura, Ikenaga. - if ( middle_pkt_len == 96 ) - return "password"; - if ( middle_pkt_len == 16 ) - return "gssapi"; - if ( ( middle_pkt_len == 32 ) && ( first_pkt_len == 0 || first_pkt_len == 48 ) ) - return "challenge-response"; - if ( middle_pkt_len < 256 ) - return fmt("unknown (mid=%d, first=%d)", middle_pkt_len, first_pkt_len); - if ( first_pkt_len == 16 ) - return "host-based"; - return fmt("pubkey (~%d bits)", (first_pkt_len - 16)*8); + if ( version == 2 ) + { + if ( first_pkt_len == 0 ) + return "none"; + if ( middle_pkt_len == 96 ) + return "password"; + if ( middle_pkt_len == 16 ) + return "gssapi"; + if ( ( middle_pkt_len == 32 ) && ( first_pkt_len == 0 || first_pkt_len == 48 ) ) + return "challenge-response"; + if ( middle_pkt_len < 256 ) + return fmt("unknown (mid=%d, first=%d)", middle_pkt_len, first_pkt_len); + if ( first_pkt_len == 16 ) + return "host-based"; + return fmt("pubkey (~%d bits)", (first_pkt_len - 16)*8); + } + else if ( version == 1 ) + { + if ( first_pkt_len == 0 ) + return "password"; + if ( first_pkt_len >= 96 && first_pkt_len <= 256 ) + return fmt("pubkey (~%d bits)", first_pkt_len * 8); + return fmt("%d %d %d", first_pkt_len, middle_pkt_len, last_pkt_len); + } } event ssh_server_version(c: connection, version: string) @@ -97,33 +112,37 @@ event ssh_client_version(c: connection, version: string) c$ssh = s; } c$ssh$client = version; + if ( version[4] == "1" ) + c$ssh$version = 1; + if ( version[4] == "2" ) + c$ssh$version = 2; } -event ssh_auth_successful(c: connection, middle_pkt_len: int, first_pkt_len: int) +event ssh_auth_successful(c: connection, last_pkt_len: int, middle_pkt_len: int, first_pkt_len: int) { print "ssh_auth_successful"; if ( !c?$ssh || ( c$ssh?$result && c$ssh$result == "success" ) ) return; c$ssh$result = "success"; - c$ssh$method = determine_auth_method(middle_pkt_len, first_pkt_len); + c$ssh$method = determine_auth_method(c$ssh$version, last_pkt_len, middle_pkt_len, first_pkt_len); } -event ssh_auth_successful(c: connection, middle_pkt_len: int, first_pkt_len: int) &priority=-5 +event ssh_auth_successful(c: connection, last_pkt_len: int, middle_pkt_len: int, first_pkt_len: int) &priority=-5 { c$ssh$logged = T; Log::write(SSH::LOG, c$ssh); } -event ssh_auth_failed(c: connection, middle_pkt_len: int, first_pkt_len: int) +event ssh_auth_failed(c: connection, last_pkt_len: int, middle_pkt_len: int, first_pkt_len: int) { print "ssh_auth_failed"; if ( !c?$ssh || ( c$ssh?$result && c$ssh$result == "success" ) ) return; c$ssh$result = "failure"; - c$ssh$method = determine_auth_method(middle_pkt_len, first_pkt_len); + c$ssh$method = determine_auth_method(c$ssh$version, last_pkt_len, middle_pkt_len, first_pkt_len); } -event ssh_auth_failed(c: connection, middle_pkt_len: int, first_pkt_len: int) &priority=-5 +event ssh_auth_failed(c: connection, last_pkt_len: int, middle_pkt_len: int, first_pkt_len: int) &priority=-5 { c$ssh$logged = T; Log::write(SSH::LOG, c$ssh); diff --git a/src/analyzer/protocol/ssh/SSH.cc b/src/analyzer/protocol/ssh/SSH.cc index 184dbba914..19ca99417c 100644 --- a/src/analyzer/protocol/ssh/SSH.cc +++ b/src/analyzer/protocol/ssh/SSH.cc @@ -17,6 +17,7 @@ SSH_Analyzer::SSH_Analyzer(Connection* c) { interp = new binpac::SSH::SSH_Conn(this); had_gap = false; + auth_decision_made = false; num_encrypted_packets_seen = 0; initial_client_packet_size = 0; initial_server_packet_size = 0; @@ -68,6 +69,7 @@ void SSH_Analyzer::DeliverStream(int len, const u_char* data, bool orig) } catch ( const binpac::Exception& e ) { + printf("Binpac exception: %s\n", e.c_msg()); ProtocolViolation(fmt("Binpac exception: %s", e.c_msg())); } } @@ -92,17 +94,24 @@ void SSH_Analyzer::ProcessEncrypted(int len, bool orig) else relative_len = len - initial_server_packet_size; - if ( num_encrypted_packets_seen >= 6 ) + if ( !auth_decision_made && ( num_encrypted_packets_seen > 3 ) ) { - int auth_result = AuthResult(relative_len, orig); + int auth_result = AuthResult(relative_len, orig, interp->get_version()); if ( auth_result > 0 ) { + auth_decision_made = true; if ( auth_result == 1 ) - BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), - packet_n_1_size, packet_n_2_size); + BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), + interp->bro_analyzer()->Conn(), + len, + packet_n_1_size, + packet_n_2_size); if ( auth_result == 2 ) - BifEvent::generate_ssh_auth_failed(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), - packet_n_1_size, packet_n_2_size); + BifEvent::generate_ssh_auth_failed(interp->bro_analyzer(), + interp->bro_analyzer()->Conn(), + len, + packet_n_1_size, + packet_n_2_size); } } if ( ( num_encrypted_packets_seen >= 2 ) && @@ -111,29 +120,47 @@ void SSH_Analyzer::ProcessEncrypted(int len, bool orig) packet_n_2_is_orig = packet_n_1_is_orig; packet_n_2_size = packet_n_1_size; } - - if ( orig == packet_n_1_is_orig ) + if ( num_encrypted_packets_seen == 0 ) + num_encrypted_packets_seen = 1; + else if ( orig == packet_n_1_is_orig ) packet_n_1_size += len; else { packet_n_1_is_orig = orig; packet_n_1_size = relative_len; - num_encrypted_packets_seen++; + if ( ! ( ( interp->get_version() == binpac::SSH::SSH1 ) && len > 90 ) ) + num_encrypted_packets_seen++; } } -int SSH_Analyzer::AuthResult(int len, bool orig) +int SSH_Analyzer::AuthResult(int len, bool orig, int version) { - if ( !orig && packet_n_1_is_orig && !packet_n_2_is_orig ) - { - printf("Auth result = %d\n", len); - if ( len == -16 ) - return 1; - else if ( len >= 16 && len <= 32 ) - return 2; - return 0; - } + if ( version == binpac::SSH::SSH2 ) + { + if ( !orig && packet_n_1_is_orig && !packet_n_2_is_orig ) + { + if ( len == -16 ) + return 1; + else if ( len >= 16 && len <= 32 ) + return 2; + return 0; + } + } + else if ( version == binpac::SSH::SSH1 ) + { + // On a successful login, the server sends a longer message + if ( !orig && len > 0 ) + { + // To verify a public key, the server sends back a message of the same size + // as the previous one. Ignore that occurrence here. + if ( ! ( packet_n_1_is_orig && ( len == packet_n_1_size ) ) ) + return 1; + } + // If we've seen too many messages without a longer message, treat it as a failure + if ( num_encrypted_packets_seen > 7 ) + return 2; + } return -1; } diff --git a/src/analyzer/protocol/ssh/SSH.h b/src/analyzer/protocol/ssh/SSH.h index 73a5420166..185e83d4de 100644 --- a/src/analyzer/protocol/ssh/SSH.h +++ b/src/analyzer/protocol/ssh/SSH.h @@ -31,11 +31,13 @@ protected: binpac::SSH::SSH_Conn* interp; void ProcessEncrypted(int len, bool orig); - int AuthResult(int len, bool orig); + int AuthResult(int len, bool orig, int version); bool had_gap; // Packet analysis stuff + bool auth_decision_made; + int initial_client_packet_size; int initial_server_packet_size; int num_encrypted_packets_seen; diff --git a/src/analyzer/protocol/ssh/events.bif b/src/analyzer/protocol/ssh/events.bif index 1389b2e0da..1b6b80f8df 100644 --- a/src/analyzer/protocol/ssh/events.bif +++ b/src/analyzer/protocol/ssh/events.bif @@ -2,9 +2,9 @@ event ssh_server_version%(c: connection, version: string%); event ssh_client_version%(c: connection, version: string%); -event ssh_auth_successful%(c: connection, middle_packet_len: int, first_packet_len: int%); +event ssh_auth_successful%(c: connection, last_packet_len: int, middle_packet_len: int, first_packet_len: int%); -event ssh_auth_failed%(c: connection, middle_packet_len: int, first_packet_len: int%); +event ssh_auth_failed%(c: connection, last_packet_len: int, middle_packet_len: int, first_packet_len: int%); event ssh_server_capabilities%(c: connection, kex_algorithms: string, server_host_key_algorithms: string, encryption_algorithms_client_to_server: string, encryption_algorithms_server_to_client: string, mac_algorithms_client_to_server: string, mac_algorithms_server_to_client: string, compression_algorithms_client_to_server: string, compression_algorithms_server_to_client: string, languages_client_to_server: string, languages_server_to_client: string%); diff --git a/src/analyzer/protocol/ssh/ssh-analyzer.pac b/src/analyzer/protocol/ssh/ssh-analyzer.pac index da940fffed..0e627481a4 100644 --- a/src/analyzer/protocol/ssh/ssh-analyzer.pac +++ b/src/analyzer/protocol/ssh/ssh-analyzer.pac @@ -67,6 +67,10 @@ refine typeattr SSH_DH_GEX_REPLY += &let { proc: bool = $context.flow.proc_ssh_server_host_key(k_s.val); }; -refine typeattr SSH_Message += &let { - proc_newkeys: bool = $context.flow.proc_newkeys() &if(msg_type == SSH2_MSG_NEWKEYS); +refine typeattr SSH1_Message += &let { + proc_newkeys: bool = $context.flow.proc_newkeys() &if(msg_type == SSH_CMSG_SESSION_KEY); +}; + +refine typeattr SSH2_Message += &let { + proc_newkeys: bool = $context.flow.proc_newkeys() &if(msg_type == MSG_NEWKEYS); }; \ No newline at end of file diff --git a/src/analyzer/protocol/ssh/ssh-protocol.pac b/src/analyzer/protocol/ssh/ssh-protocol.pac index d14af0a663..3e1ec95ecc 100644 --- a/src/analyzer/protocol/ssh/ssh-protocol.pac +++ b/src/analyzer/protocol/ssh/ssh-protocol.pac @@ -1,41 +1,95 @@ +enum version { + SSH1 = 1, + SSH2 = 2, + UNK = 3, +}; + enum state { VERSION_EXCHANGE = 0, KEY_EXCHANGE_CLEARTEXT = 1, ENCRYPTED = 2, }; -enum message_id { - 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_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_USERAUTH_REQUEST = 50, - SSH2_MSG_USERAUTH_FAILURE = 51, - SSH2_MSG_USERAUTH_SUCCESS = 52, - SSH2_MSG_USERAUTH_BANNER = 53, - 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, +enum ssh1_message_id { + SSH_MSG_NONE = 0, + SSH_MSG_DISCONNECT = 1, + SSH_SMSG_PUBLIC_KEY = 2, + SSH_CMSG_SESSION_KEY = 3, + SSH_CMSG_USER = 4, + SSH_CMSG_AUTH_RHOSTS = 5, + SSH_CMSG_AUTH_RSA = 6, + SSH_SMSG_AUTH_RSA_CHALLENGE = 7, + SSH_CMSG_AUTH_RSA_RESPONSE = 8, + SSH_CMSG_AUTH_PASSWORD = 9, + SSH_CMSG_REQUEST_PTY = 10, + SSH_CMSG_WINDOW_SIZE = 11, + SSH_CMSG_EXEC_SHELL = 12, + SSH_CMSG_EXEC_CMD = 13, + SSH_SMSG_SUCCESS = 14, + SSH_SMSG_FAILURE = 15, + SSH_CMSG_STDIN_DATA = 16, + SSH_SMSG_STDOUT_DATA = 17, + SSH_SMSG_STDERR_DATA = 18, + SSH_CMSG_EOF = 19, + SSH_SMSG_EXITSTATUS = 20, + SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 21, + SSH_MSG_CHANNEL_OPEN_FAILURE = 22, + SSH_MSG_CHANNEL_DATA = 23, + SSH_MSG_CHANNEL_CLOSE = 24, + SSH_MSG_CHANNEL_CLOSE_CONFIRMATION = 25, + SSH_CMSG_X11_REQUEST_FORWARDING_OLD = 26, + SSH_SMSG_X11_OPEN = 27, + SSH_CMSG_PORT_FORWARD_REQUEST = 28, + SSH_MSG_PORT_OPEN = 29, + SSH_CMSG_AGENT_REQUEST_FORWARDING = 30, + SSH_SMSG_AGENT_OPEN = 31, + SSH_MSG_IGNORE = 32, + SSH_CMSG_EXIT_CONFIRMATION = 33, + SSH_CMSG_X11_REQUEST_FORWARDING = 34, + SSH_CMSG_AUTH_RHOSTS_RSA = 35, + SSH_MSG_DEBUG = 36, + SSH_CMSG_REQUEST_COMPRESSION = 37, + SSH_CMSG_MAX_PACKET_SIZE = 38, + SSH_CMSG_AUTH_TIS = 39, + SSH_SMSG_AUTH_TIS_CHALLENGE = 40, + SSH_CMSG_AUTH_TIS_RESPONSE = 41, + SSH_CMSG_AUTH_KERBEROS = 42, + SSH_SMSG_AUTH_KERBEROS_RESPONSE = 43, + SSH_CMSG_HAVE_KERBEROS_TGT = 44, +}; + +enum ssh2_message_id { + MSG_DISCONNECT = 1, + MSG_IGNORE = 2, + MSG_UNIMPLEMENTED = 3, + MSG_DEBUG = 4, + MSG_SERVICE_REQUEST = 5, + MSG_SERVICE_ACCEPT = 6, + MSG_KEXINIT = 20, + MSG_NEWKEYS = 21, + MSG_KEX_DH_GEX_REQUEST_OLD = 30, + MSG_KEX_DH_GEX_GROUP = 31, + MSG_KEX_DH_GEX_INIT = 32, + MSG_KEX_DH_GEX_REPLY = 33, + MSG_KEX_DH_GEX_REQUEST = 34, + MSG_USERAUTH_REQUEST = 50, + MSG_USERAUTH_FAILURE = 51, + MSG_USERAUTH_SUCCESS = 52, + MSG_USERAUTH_BANNER = 53, + MSG_GLOBAL_REQUEST = 80, + MSG_REQUEST_SUCCESS = 81, + MSG_REQUEST_FAILURE = 82, + MSG_CHANNEL_OPEN = 90, + MSG_CHANNEL_OPEN_CONFIRMATION = 91, + MSG_CHANNEL_OPEN_FAILURE = 92, + MSG_CHANNEL_WINDOW_ADJUST = 93, + MSG_CHANNEL_DATA = 94, + MSG_CHANNEL_EXTENDED_DATA = 95, + MSG_CHANNEL_EOF = 96, + MSG_CHANNEL_CLOSE = 97, + MSG_CHANNEL_REQUEST = 98, + MSG_CHANNEL_SUCCESS = 99, + MSG_CHANNEL_FAILURE = 100, }; type SSH_PDU(is_orig: bool) = case $context.connection.get_state(is_orig) of { @@ -46,40 +100,76 @@ type SSH_PDU(is_orig: bool) = case $context.connection.get_state(is_orig) of { type SSH_Version(is_orig: bool) = record { version: bytestring &oneline; + pad: bytestring &length=0 &transient; } &let { - update_state: bool = $context.connection.update_state(KEY_EXCHANGE_CLEARTEXT, is_orig); + update_state : bool = $context.connection.update_state(KEY_EXCHANGE_CLEARTEXT, is_orig); + update_version: bool = $context.connection.update_version(version, is_orig); }; -type SSH_Key_Exchange_Header(is_orig: bool) = record { - packet_length : uint32; +type SSH_Key_Exchange(is_orig: bool) = case $context.connection.get_version() of { + SSH1 -> ssh1_msg: SSH1_Key_Exchange(is_orig); + SSH2 -> ssh2_msg: SSH2_Key_Exchange(is_orig); +}; + +type SSH1_Key_Exchange(is_orig: bool) = record { + packet_length: uint32; + pad_fill : bytestring &length = 8 - (packet_length % 8); + msg_type : uint8; + message : SSH1_Message(is_orig, msg_type, packet_length-5); + crc : uint32; +} &length = packet_length + 4 + 8 - (packet_length % 8); + +type SSH2_Key_Exchange_Header = record { + packet_length : uint32; padding_length: uint8; -} &length=5; - -type SSH_Key_Exchange(is_orig: bool) = record { - header : SSH_Key_Exchange_Header(is_orig); - payload: SSH_Payload(is_orig, header.packet_length - header.padding_length - 2); - pad : bytestring &length=header.padding_length; -}; - -type SSH_Payload_Header = record { - message_type: uint8; -} &length=1; - -type SSH_Payload(is_orig: bool, packet_length: uint32) = record { - header: SSH_Payload_Header; - message: SSH_Message(is_orig, header.message_type, packet_length); -}; - -type SSH_Message(is_orig: bool, msg_type: uint8, packet_length: uint32) = case msg_type of { - SSH2_MSG_KEXINIT -> kexinit: SSH_KEXINIT(packet_length); - SSH2_MSG_KEX_DH_GEX_REQUEST -> dh_gex_request: SSH_DH_GEX_REQUEST(packet_length); - SSH2_MSG_KEX_DH_GEX_REQUEST_OLD -> dh_gex_request_old: SSH_DH_GEX_REQUEST_OLD(packet_length); - SSH2_MSG_KEX_DH_GEX_GROUP -> dh_gex_group: SSH_DH_GEX_GROUP(packet_length); - SSH2_MSG_KEX_DH_GEX_INIT -> dh_gex_init: SSH_DH_GEX_INIT(packet_length); - SSH2_MSG_KEX_DH_GEX_REPLY -> dh_gex_reply: SSH_DH_GEX_REPLY(packet_length); - SSH2_MSG_NEWKEYS -> new_keys: bytestring &length=packet_length; + msg_type : uint8; } &let { - detach: bool = $context.connection.update_state(ENCRYPTED, is_orig) &if(msg_type == SSH2_MSG_NEWKEYS); + payload_length: uint32 = packet_length - padding_length - 2; +} &length=6; + +type SSH2_Key_Exchange(is_orig: bool) = record { + header : SSH2_Key_Exchange_Header; + payload : SSH2_Message(is_orig, header.msg_type, header.payload_length); + pad : bytestring &length=header.padding_length; +} &length=header.packet_length + 4; + +type SSH1_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of { + SSH_SMSG_PUBLIC_KEY -> public_key: SSH1_PUBLIC_KEY(length); + SSH_CMSG_SESSION_KEY -> session_key: SSH1_SESSION_KEY(length); +} &let { + detach: bool = $context.connection.update_state(ENCRYPTED, is_orig); +}; + +type SSH1_PUBLIC_KEY(length: uint32) = record { + cookie : bytestring &length=8; + server_key : uint32; + server_key_p : ssh1_mp_int; + server_key_e : ssh1_mp_int; + host_key : uint32; + host_key_p : ssh1_mp_int; + host_key_e : ssh1_mp_int; + flags : uint32; + supported_ciphers : uint32; + supported_auths : uint32; +} &length=length; + +type SSH1_SESSION_KEY(length: uint32) = record { + cipher : uint8; + cookie : bytestring &length=8; + session_key : ssh1_mp_int; + flags : uint32; +} &length=length; + +type SSH2_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of { + MSG_KEXINIT -> kexinit: SSH_KEXINIT(length); + MSG_KEX_DH_GEX_REQUEST -> dh_gex_request: SSH_DH_GEX_REQUEST(length); + MSG_KEX_DH_GEX_REQUEST_OLD -> dh_gex_request_old: SSH_DH_GEX_REQUEST_OLD(length); + MSG_KEX_DH_GEX_GROUP -> dh_gex_group: SSH_DH_GEX_GROUP(length); + MSG_KEX_DH_GEX_INIT -> dh_gex_init: SSH_DH_GEX_INIT(length); + MSG_KEX_DH_GEX_REPLY -> dh_gex_reply: SSH_DH_GEX_REPLY(length); + MSG_NEWKEYS -> new_keys: bytestring &length=length; +} &let { + detach: bool = $context.connection.update_state(ENCRYPTED, is_orig) &if(msg_type == MSG_NEWKEYS); }; type SSH_KEXINIT(length: uint32) = record { @@ -123,6 +213,11 @@ type SSH_DH_GEX_REPLY(length: uint32) = record { signature: ssh_string; } &length=length; +type ssh1_mp_int = record { + len: uint16; + val: bytestring &length=(len+7)/8; +}; + type ssh_string = record { len: uint32; val: bytestring &length=len; @@ -132,19 +227,25 @@ refine connection SSH_Conn += { %member{ int state_up_; int state_down_; + int version_; %} %init{ state_up_ = VERSION_EXCHANGE; state_down_ = VERSION_EXCHANGE; + version_ = UNK; %} function get_state(is_orig: bool): int %{ if ( is_orig ) + { return state_up_; + } else + { return state_down_; + } %} function update_state(s: state, is_orig: bool): bool @@ -156,4 +257,21 @@ refine connection SSH_Conn += { return true; %} + function get_version(): int + %{ + return version_; + %} + + function update_version(v: bytestring, is_orig: bool): bool + %{ + if ( is_orig && ( v.length() >= 4 ) ) + { + if ( v[4] == '2' ) + version_ = SSH2; + if ( v[4] == '1' ) + version_ = SSH1; + } + return true; + %} + }; \ No newline at end of file