diff --git a/scripts/base/frameworks/intel/main.zeek b/scripts/base/frameworks/intel/main.zeek index 0d1f9aa374..8c6a85fedb 100644 --- a/scripts/base/frameworks/intel/main.zeek +++ b/scripts/base/frameworks/intel/main.zeek @@ -30,7 +30,8 @@ export { USER_NAME, ## Certificate SHA-1 hash. CERT_HASH, - ## Public key MD5 hash. (SSH server host keys are a good example.) + ## Public key MD5 hash, formatted as hexadecimal digits delimited by colons. + ## (SSH server host keys are a good example.) PUBKEY_HASH, }; diff --git a/scripts/base/protocols/ssh/main.zeek b/scripts/base/protocols/ssh/main.zeek index 373007c3a9..a26234abf5 100644 --- a/scripts/base/protocols/ssh/main.zeek +++ b/scripts/base/protocols/ssh/main.zeek @@ -111,17 +111,6 @@ export { ## ssh_server_host_key ssh_encrypted_packet ssh2_dh_server_params ## ssh2_gss_error ssh2_ecc_key global ssh_auth_result: event(c: connection, result: bool, auth_attempts: count); - - ## Event that can be handled when the analyzer sees an SSH server host - ## key. This abstracts :zeek:id:`ssh1_server_host_key` and - ## :zeek:id:`ssh2_server_host_key`. - ## - ## .. zeek:see:: ssh_server_version ssh_client_version - ## ssh_auth_successful ssh_auth_failed ssh_auth_result - ## ssh_auth_attempted ssh_capabilities ssh2_server_host_key - ## ssh1_server_host_key ssh_encrypted_packet ssh2_dh_server_params - ## ssh2_gss_error ssh2_ecc_key - global ssh_server_host_key: event(c: connection, hash: string); } module SSH; diff --git a/src/analyzer/protocol/ssh/events.bif b/src/analyzer/protocol/ssh/events.bif index 6ff62e501d..abc0c2d3f6 100644 --- a/src/analyzer/protocol/ssh/events.bif +++ b/src/analyzer/protocol/ssh/events.bif @@ -138,6 +138,34 @@ event ssh2_server_host_key%(c: connection, key: string%); ## ssh2_gss_error ssh2_ecc_key event ssh1_server_host_key%(c: connection, p: string, e: string%); +## During the :abbr:`SSH (Secure Shell)` key exchange, the server +## supplies its public host key. This event is generated when the +## appropriate key exchange message is seen for SSH1 or SSH2 and provides +## a fingerprint of the server's host key. +## +## c: The connection over which the :abbr:`SSH (Secure Shell)` +## connection took place. +## +## hash: an MD5 hash fingerprint associated with the server's host key. +## For SSH2, this is the hash of the "server public host key" string as +## seen on the wire in the Diffie-Hellman key exchange reply message +## (the string itself, excluding the 4-byte length associated with it), +## which is also the *key* parameter of :zeek:see:`ssh2_server_host_key` +## For SSH1, this is the hash of the combined multiprecision integer +## strings representing the RSA1 key's prime modulus and public exponent +## (concatenated in that order) as seen on the wire, +## which are also the parameters of :zeek:see:`ssh1_server_host_key`. +## In either case, the hash is the same "fingerprint" string as presented +## by other traditional tools, ``ssh``, ``ssh-keygen``, etc, and is the +## hexadecimal representation of all 16 MD5 hash bytes delimited by colons. +## +## .. zeek:see:: ssh_server_version ssh_client_version +## ssh_auth_successful ssh_auth_failed ssh_auth_result +## ssh_auth_attempted ssh_capabilities ssh2_server_host_key +## ssh1_server_host_key ssh_encrypted_packet ssh2_dh_server_params +## ssh2_gss_error ssh2_ecc_key +event ssh_server_host_key%(c: connection, hash: string%); + ## This event is generated when an :abbr:`SSH (Secure Shell)` ## encrypted packet is seen. This event is not handled by default, but ## is provided for heuristic analysis scripts. Note that you have to set diff --git a/src/analyzer/protocol/ssh/ssh-analyzer.pac b/src/analyzer/protocol/ssh/ssh-analyzer.pac index 59a6864083..84735440bb 100644 --- a/src/analyzer/protocol/ssh/ssh-analyzer.pac +++ b/src/analyzer/protocol/ssh/ssh-analyzer.pac @@ -2,10 +2,12 @@ #include #include #include +#include "zeek/digest.h" %} %header{ zeek::VectorValPtr name_list_to_vector(const bytestring& nl); +const char* fingerprint_md5(const unsigned char* d); %} %code{ @@ -45,6 +47,14 @@ zeek::VectorValPtr name_list_to_vector(const bytestring& nl) } return vv; } + +const char* fingerprint_md5(const unsigned char* d) + { + return zeek::util::fmt("%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:" + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], + d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); + } %} refine flow SSH_Flow += { @@ -153,6 +163,17 @@ refine flow SSH_Flow += { connection()->zeek_analyzer()->Conn(), to_stringval(${key})); } + + if ( ssh_server_host_key ) + { + unsigned char digest[MD5_DIGEST_LENGTH]; + zeek::detail::internal_md5(${key}.data(), ${key}.length(), digest); + + zeek::BifEvent::enqueue_ssh_server_host_key(connection()->zeek_analyzer(), + connection()->zeek_analyzer()->Conn(), + zeek::make_intrusive(fingerprint_md5(digest))); + } + return true; %} @@ -165,6 +186,23 @@ refine flow SSH_Flow += { to_stringval(${p}), to_stringval(${e})); } + + if ( ssh_server_host_key ) + { + unsigned char digest[MD5_DIGEST_LENGTH]; + auto ctx = zeek::detail::hash_init(zeek::detail::Hash_MD5); + // Note: the 'p' and 'e' parameters actually have swapped meanings with + // 'p' actually being the exponent. + // Fingerprint is calculated over concatenation of modulus + exponent. + zeek::detail::hash_update(ctx, ${e}.data(), ${e}.length()); + zeek::detail::hash_update(ctx, ${p}.data(), ${p}.length()); + zeek::detail::hash_final(ctx, digest); + + zeek::BifEvent::enqueue_ssh_server_host_key(connection()->zeek_analyzer(), + connection()->zeek_analyzer()->Conn(), + zeek::make_intrusive(fingerprint_md5(digest))); + } + return true; %} diff --git a/testing/btest/Baseline/scripts.base.frameworks.intel.remove-non-existing/output b/testing/btest/Baseline/scripts.base.frameworks.intel.remove-non-existing/output index d8517f51c6..f9158f8618 100644 --- a/testing/btest/Baseline/scripts.base.frameworks.intel.remove-non-existing/output +++ b/testing/btest/Baseline/scripts.base.frameworks.intel.remove-non-existing/output @@ -1,11 +1,12 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. #separator \x09 #set_separator , #empty_field (empty) #unset_field - #path reporter -#open 2020-09-24-00-19-13 +#open XXXX-XX-XX-XX-XX-XX #fields ts level message location #types time enum string string -1600906753.185591 Reporter::INFO Tried to remove non-existing item '192.168.1.1' (Intel::ADDR). /home/christian/devel/zeek/zeek/scripts/base/frameworks/intel/./main.zeek, lines 565-566 -1600906753.185591 Reporter::INFO received termination signal (empty) -#close 2020-09-24-00-19-13 +XXXXXXXXXX.XXXXXX Reporter::INFO Tried to remove non-existing item '192.168.1.1' (Intel::ADDR). <...>/main.zeek, lines 566-567 +XXXXXXXXXX.XXXXXX Reporter::INFO received termination signal (empty) +#close XXXX-XX-XX-XX-XX-XX diff --git a/testing/btest/Baseline/scripts.base.protocols.ssh.fingerprints/out b/testing/btest/Baseline/scripts.base.protocols.ssh.fingerprints/out new file mode 100644 index 0000000000..dd1454e9b1 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.ssh.fingerprints/out @@ -0,0 +1,5 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +ssh2 server host key fingerprint, c7eb775dd16431d61be8995fa709a1d7 +ssh server host key fingerprint, c7:eb:77:5d:d1:64:31:d6:1b:e8:99:5f:a7:09:a1:d7 +ssh1 server host key fingerprint, 5517ebfa2e7fb37b33427c9d448556da +ssh server host key fingerprint, 55:17:eb:fa:2e:7f:b3:7b:33:42:7c:9d:44:85:56:da diff --git a/testing/btest/Traces/ssh/ssh1-ssh2-fingerprints.pcap b/testing/btest/Traces/ssh/ssh1-ssh2-fingerprints.pcap new file mode 100644 index 0000000000..730240e963 Binary files /dev/null and b/testing/btest/Traces/ssh/ssh1-ssh2-fingerprints.pcap differ diff --git a/testing/btest/scripts/base/protocols/ssh/fingerprints.zeek b/testing/btest/scripts/base/protocols/ssh/fingerprints.zeek new file mode 100644 index 0000000000..6af3c55a6a --- /dev/null +++ b/testing/btest/scripts/base/protocols/ssh/fingerprints.zeek @@ -0,0 +1,19 @@ +# @TEST-EXEC: zeek -r $TRACES/ssh/ssh1-ssh2-fingerprints.pcap %INPUT >out +# @TEST-EXEC: btest-diff out + +@load base/protocols/ssh + +event ssh2_server_host_key(c: connection, key: string) + { + print "ssh2 server host key fingerprint", md5_hash(key); + } + +event ssh1_server_host_key(c: connection, p: string, e: string) + { + print "ssh1 server host key fingerprint", md5_hash(e + p); + } + +event ssh_server_host_key(c: connection, hash: string) + { + print "ssh server host key fingerprint", hash; + }