diff --git a/CHANGES b/CHANGES index 38107c9364..34ae3dbef7 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,17 @@ +3.3.0-dev.537 | 2020-11-16 11:03:05 +0000 + + * [SSH] Handle SSH version 1.99 + SSH can set in its identification a version 1.99 (SSH-1.99-xxx). + That means the client/server is compatible with SSHv1 and SSHv2. + So the version choice depends of the both side. + + 1.99 : 1.99 => 2.0 + 1.99 : 1.x => 1.x + 1.99 : 2.0 => 2.O + + (see "Compatibility With Old SSH Versions" in RFC 4253) (Brevet Vivien) + 3.3.0-dev.534 | 2020-11-12 14:31:10 -0800 * Move UnknownProtocol options to init-bare.zeek (Jon Siwek, Corelight) diff --git a/NEWS b/NEWS index 349aeb51b9..dd24cc43fd 100644 --- a/NEWS +++ b/NEWS @@ -80,6 +80,10 @@ New Functionality - dns_NSEC3PARAM - dns_BINDS +- Zeek now supports SSH clients/servers that advertise SSH version 1.99, which + is a special version indicating that the server/client supports both SSH2 and + SSH1. + Changed Functionality --------------------- diff --git a/VERSION b/VERSION index 94b4ccf195..9ad01af2e3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.3.0-dev.534 +3.3.0-dev.537 diff --git a/scripts/base/protocols/ssh/main.zeek b/scripts/base/protocols/ssh/main.zeek index 373007c3a9..4a6d281c56 100644 --- a/scripts/base/protocols/ssh/main.zeek +++ b/scripts/base/protocols/ssh/main.zeek @@ -166,21 +166,65 @@ function set_session(c: connection) } } +function set_version(c: connection, version: string) + { + if ( c$ssh?$server && c$ssh?$client && |c$ssh$client| > 4 && |c$ssh$server| > 4 ) + { + if ( c$ssh$client[4] == "1" && c$ssh$server[4] == "2" ) + { + # SSH199 vs SSH2 -> 2 + if ( ( |c$ssh$client| > 7 ) && ( c$ssh$client[6] == "9" ) && ( c$ssh$client[7] == "9" ) ) + c$ssh$version = 2; + # SSH1 vs SSH2 -> Undefined + else + c$ssh$version = 0; + } + else if ( c$ssh$client[4] == "2" && c$ssh$server[4] == "1" ) + { + # SSH2 vs SSH199 -> 2 + if ( ( |c$ssh$server| > 7 ) && ( c$ssh$server[6] == "9" ) && ( c$ssh$server[7] == "9" ) ) + c$ssh$version = 2; + else + # SSH2 vs SSH1 -> Undefined + c$ssh$version = 0; + } + else if ( c$ssh$client[4] == "1" && c$ssh$server[4] == "1" ) + { + # SSH1 vs SSH199 -> 1 + if ( ( |c$ssh$server| > 7 ) && ( c$ssh$server[6] == "9" ) && ( c$ssh$server[7] == "9" ) ) + { + # SSH199 vs SSH199 + if (( |c$ssh$client| > 7 ) && ( c$ssh$client[6] == "9" ) && ( c$ssh$client[7] == "9" )) + c$ssh$version = 2; + else + c$ssh$version = 1; + } + else + { + # SSH1 vs SSH1 -> 1 + c$ssh$version = 1; + } + } + # SSH2 vs SSH2 + else if (c$ssh$client[4] == "2" && c$ssh$server[4] == "2" ) + { + c$ssh$version = 2; + } + } + } + event ssh_server_version(c: connection, version: string) { set_session(c); c$ssh$server = version; + set_version(c, version); } event ssh_client_version(c: connection, version: string) { set_session(c); c$ssh$client = version; - - if ( ( |version| > 3 ) && ( version[4] == "1" ) ) - c$ssh$version = 1; - if ( ( |version| > 3 ) && ( version[4] == "2" ) ) - c$ssh$version = 2; + set_version(c, version); } event ssh_auth_attempted(c: connection, authenticated: bool) &priority=5 diff --git a/src/analyzer/protocol/ssh/consts.pac b/src/analyzer/protocol/ssh/consts.pac index 78027f8a51..ec6eefb16b 100644 --- a/src/analyzer/protocol/ssh/consts.pac +++ b/src/analyzer/protocol/ssh/consts.pac @@ -2,6 +2,7 @@ enum version { SSH1 = 1, SSH2 = 2, UNK = 3, + SSH199 = 4, }; enum state { diff --git a/src/analyzer/protocol/ssh/ssh-protocol.pac b/src/analyzer/protocol/ssh/ssh-protocol.pac index 99f361974f..52a6db4b4b 100644 --- a/src/analyzer/protocol/ssh/ssh-protocol.pac +++ b/src/analyzer/protocol/ssh/ssh-protocol.pac @@ -275,6 +275,8 @@ refine connection SSH_Conn += { int state_up_; int state_down_; int version_; + int version_client_; + int version_server_; int encrypted_bytes_in_current_segment_; bool kex_orig_; @@ -287,6 +289,8 @@ refine connection SSH_Conn += { state_up_ = VERSION_EXCHANGE; state_down_ = VERSION_EXCHANGE; version_ = UNK; + version_client_ = UNK; + version_server_ = UNK; encrypted_bytes_in_current_segment_ = 0; kex_seen_ = false; @@ -343,15 +347,67 @@ refine connection SSH_Conn += { return version_; %} + # If the version is 1.99, that means the client/server is compatible + # with sshv1 and sshv2. So one says version 2 and the other 1.99 + # the connection will be in version 2 otherwise if its version 1.x and + # 1.99 the connection be in version 1. See RFC 4253 chapter 5. function update_version(v: bytestring, is_orig: bool) : bool %{ - if ( is_orig && ( v.length() >= 4 ) ) + if ( v.length() >= 5 ) { if ( v[4] == '2' ) - version_ = SSH2; + { + if ( is_orig ) + version_client_ = SSH2; + else + version_server_ = SSH2; + } if ( v[4] == '1' ) - version_ = SSH1; + { + if ( v.length() >= 8 && v[6] == '9' && v[7] == '9' ) + { + if ( is_orig ) + version_client_ = SSH199; + else + version_server_ = SSH199; + } + else + { + if ( is_orig) + version_client_ = SSH1; + else + version_server_ = SSH1; + } + } } + + if ( version_server_ == version_client_ ) + { + // SSH199 vs SSH199 -> 2 + if (version_server_ == SSH199 ) + version_ = SSH2; + else + version_ = version_server_; + } + // SSH1 vs SSH2 -> Undefined + else if ( version_client_ == SSH1 && version_server_ == SSH2 ) + version_ = UNK; + // SSH2 vs SSH1 -> Undefined + else if ( version_client_ == SSH2 && version_server_ == SSH1 ) + version_ = UNK; + // SSH199 vs SSH2 -> 2 + else if ( version_client_ == SSH199 && version_server_ == SSH2 ) + version_ = version_server_; + // SSH2 vs SSH199 -> 2 + else if ( version_client_ == SSH2 && version_server_ == SSH199 ) + version_ = version_client_; + // SSH1 vs SSH199 -> 1 + else if ( version_client_ == SSH1 && version_server_ == SSH199 ) + version_ = version_client_; + // SSH199 vs SSH1 -> 1 + else if ( version_client_ == SSH199 && version_server_ == SSH1 ) + version_ = version_server_; + return true; %} diff --git a/testing/btest/Baseline/scripts.base.protocols.ssh.ssh_version_199/ssh.log b/testing/btest/Baseline/scripts.base.protocols.ssh.ssh_version_199/ssh.log new file mode 100644 index 0000000000..5a6a76ad89 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.ssh.ssh_version_199/ssh.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path ssh +#open 2019-12-06-09-53-13 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version auth_success auth_attempts direction client server cipher_alg mac_alg compression_alg kex_alg host_key_alg host_key +#types time string addr port addr port count bool count enum string string string string string string string string +1213964540.292082 CHhAvVGS1DHFjwGM9 10.0.0.1 59139 10.0.0.2 22 2 T 1 - SSH-1.99-Cisco-1.25 SSH-2.0-Cisco-1.25 aes128-cbc hmac-sha1 none diffie-hellman-group1-sha1 ssh-rsa 91:0a:ed:3f:79:71:22:f9:97:66:71:f8:c9:a5:b4:10 +#close 2019-12-06-09-53-13 diff --git a/testing/btest/Traces/ssh/ssh_version_199.pcap b/testing/btest/Traces/ssh/ssh_version_199.pcap new file mode 100644 index 0000000000..b682bb8030 Binary files /dev/null and b/testing/btest/Traces/ssh/ssh_version_199.pcap differ diff --git a/testing/btest/scripts/base/protocols/ssh/ssh_version_199.zeek b/testing/btest/scripts/base/protocols/ssh/ssh_version_199.zeek new file mode 100644 index 0000000000..ffd62a557e --- /dev/null +++ b/testing/btest/scripts/base/protocols/ssh/ssh_version_199.zeek @@ -0,0 +1,6 @@ +# This tests a successful auth between an SSHv1.99 and SSHv2. + +# @TEST-EXEC: zeek -r $TRACES/ssh/ssh_version_199.pcap %INPUT +# @TEST-EXEC: btest-diff ssh.log + +@load base/protocols/ssh