Added several events for detailed info on the SSH2 key init directions

This commit is contained in:
Joost Jansen 2022-12-02 11:27:25 +01:00
parent dbbb6cd6f0
commit bcdbca4bb9
8 changed files with 178 additions and 18 deletions

View file

@ -100,7 +100,8 @@ event ssh_auth_attempted%(c: connection, authenticated: bool%);
## ssh_auth_successful ssh_auth_failed ssh_auth_result ## ssh_auth_successful ssh_auth_failed ssh_auth_result
## ssh_auth_attempted ssh2_server_host_key ssh1_server_host_key ## ssh_auth_attempted ssh2_server_host_key ssh1_server_host_key
## ssh_server_host_key ssh_encrypted_packet ssh2_dh_server_params ## ssh_server_host_key ssh_encrypted_packet ssh2_dh_server_params
## ssh2_gss_error ssh2_ecc_key ## ssh2_gss_error ssh2_ecc_key ssh2_ecc_init ssh2_dh_gex_init
## ssh2_rsa_secret ssh2_gss_init
event ssh_capabilities%(c: connection, cookie: string, capabilities: SSH::Capabilities%); event ssh_capabilities%(c: connection, cookie: string, capabilities: SSH::Capabilities%);
## During the :abbr:`SSH (Secure Shell)` key exchange, the server ## During the :abbr:`SSH (Secure Shell)` key exchange, the server
@ -171,7 +172,8 @@ event ssh1_server_host_key%(c: connection, modulus: string, exponent: string%);
## ssh_auth_successful ssh_auth_failed ssh_auth_result ## ssh_auth_successful ssh_auth_failed ssh_auth_result
## ssh_auth_attempted ssh_capabilities ssh2_server_host_key ## ssh_auth_attempted ssh_capabilities ssh2_server_host_key
## ssh1_server_host_key ssh_encrypted_packet ssh2_dh_server_params ## ssh1_server_host_key ssh_encrypted_packet ssh2_dh_server_params
## ssh2_gss_error ssh2_ecc_key ## ssh2_gss_error ssh2_ecc_key ssh2_ecc_init ssh2_dh_gex_init
## ssh2_gss_init ssh2_rsa_secret
event ssh_server_host_key%(c: connection, hash: string%); event ssh_server_host_key%(c: connection, hash: string%);
## This event is generated when an :abbr:`SSH (Secure Shell)` ## This event is generated when an :abbr:`SSH (Secure Shell)`
@ -211,7 +213,7 @@ event ssh_encrypted_packet%(c: connection, orig: bool, len: count%);
## ssh_auth_successful ssh_auth_failed ssh_auth_result ## ssh_auth_successful ssh_auth_failed ssh_auth_result
## ssh_auth_attempted ssh_capabilities ssh2_server_host_key ## ssh_auth_attempted ssh_capabilities ssh2_server_host_key
## ssh1_server_host_key ssh_server_host_key ssh_encrypted_packet ## ssh1_server_host_key ssh_server_host_key ssh_encrypted_packet
## ssh2_gss_error ssh2_ecc_key ## ssh2_gss_error ssh2_dh_gex_init
event ssh2_dh_server_params%(c: connection, p: string, q: string%); event ssh2_dh_server_params%(c: connection, p: string, q: string%);
## In the event of a GSS-API error on the server, the server MAY send ## In the event of a GSS-API error on the server, the server MAY send
@ -231,7 +233,7 @@ event ssh2_dh_server_params%(c: connection, p: string, q: string%);
## ssh_auth_successful ssh_auth_failed ssh_auth_result ## ssh_auth_successful ssh_auth_failed ssh_auth_result
## ssh_auth_attempted ssh_capabilities ssh2_server_host_key ## ssh_auth_attempted ssh_capabilities ssh2_server_host_key
## ssh1_server_host_key ssh_server_host_key ssh_encrypted_packet ## ssh1_server_host_key ssh_server_host_key ssh_encrypted_packet
## ssh2_dh_server_params ssh2_ecc_key ## ssh2_dh_server_params ssh2_ecc_key ssh2_gss_init
event ssh2_gss_error%(c: connection, major_status: count, minor_status: count, err_msg: string%); event ssh2_gss_error%(c: connection, major_status: count, minor_status: count, err_msg: string%);
## The :abbr:`ECDH (Elliptic Curve Diffie-Hellman)` and ## The :abbr:`ECDH (Elliptic Curve Diffie-Hellman)` and
@ -251,5 +253,72 @@ event ssh2_gss_error%(c: connection, major_status: count, minor_status: count, e
## ssh_auth_successful ssh_auth_failed ssh_auth_result ## ssh_auth_successful ssh_auth_failed ssh_auth_result
## ssh_auth_attempted ssh_capabilities ssh2_server_host_key ## ssh_auth_attempted ssh_capabilities ssh2_server_host_key
## ssh1_server_host_key ssh_server_host_key ssh_encrypted_packet ## ssh1_server_host_key ssh_server_host_key ssh_encrypted_packet
## ssh2_dh_server_params ssh2_gss_error ## ssh2_dh_server_params ssh2_gss_error ssh2_ecc_init
event ssh2_ecc_key%(c: connection, is_orig: bool, q: string%); event ssh2_ecc_key%(c: connection, is_orig: bool, q: string%);
## The :abbr:`ECDH (Elliptic Curve Diffie-Hellman)` and
## :abbr:`ECMQV (Elliptic Curve Menezes-Qu-Vanstone)` key exchange
## algorithms use two ephemeral key pairs to generate a shared
## secret. This event is generated when either the SSH_MSG_KEX_ECDH_INIT
## or SSH_MSG_ECMQV_INIT message is observed. By definition, these need
## to originate from the client and not from the server.
## For more information, see:
## :rfc:`5656#section-4`.
##
## c: The connection
##
## is_orig: Did this message come from the originator?
##
## .. 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_server_host_key ssh_encrypted_packet
## ssh2_dh_server_params ssh2_gss_error ssh2_ecc_key
event ssh2_ecc_init%(c: connection, is_orig: bool%);
## Generated if the connection uses a Diffie-Hellman Group Exchange
## key exchange method. This event contains the direction of the key
## exchange setup, which is indicated by the the SSH_MSG_KEX_DH_GEX_INIT
## message as defined in :rfc:`4419#section-3`.
##
## c: The connection
##
## is_orig: Did this message come from the originator?
##
## .. 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_server_host_key ssh_encrypted_packet
## ssh2_dh_server_params ssh2_gss_error
event ssh2_dh_gex_init%(c: connection, is_orig: bool%);
## In the event of a GSS-API key exchange, this event is raised on
## SSH_MSG_KEXGSS_INIT message.
## For more information see :rfc:`4462#section-2.1`.
##
## c: The connection
##
## is_orig: Did this message come from the originator?
##
## .. 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_server_host_key ssh_encrypted_packet
## ssh2_dh_server_params ssh2_gss_error
event ssh2_gss_init%(c: connection, is_orig: bool%);
## In the event of a GSS-API key exchange, this event is raised on
## SSH_MSG_KEXRSA_PUBKEY message. This message is sent first by the server,
## after which the server will respond with a SSH_MSG_KEXRSA_SECRET message.
## For more information see :rfc:`4432#section-4`.
##
## c: The connection
##
## is_orig: Did this message come from the originator?
##
## .. 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_server_host_key ssh_encrypted_packet
## ssh2_dh_server_params ssh2_gss_error
event ssh2_rsa_secret%(c: connection, is_orig: bool%);

View file

@ -120,6 +120,50 @@ refine flow SSH_Flow += {
return true; return true;
%} %}
function proc_ssh2_ecc_init(is_orig: bool): bool
%{
if ( ssh2_ecc_init )
{
zeek::BifEvent::enqueue_ssh2_ecc_init(connection()->zeek_analyzer(),
connection()->zeek_analyzer()->Conn(),
is_orig);
}
return true;
%}
function proc_ssh2_dh_gex_init(is_orig: bool): bool
%{
if ( ssh2_dh_gex_init )
{
zeek::BifEvent::enqueue_ssh2_dh_gex_init(connection()->zeek_analyzer(),
connection()->zeek_analyzer()->Conn(),
is_orig);
}
return true;
%}
function proc_ssh2_gss_init(is_orig: bool): bool
%{
if ( ssh2_gss_init )
{
zeek::BifEvent::enqueue_ssh2_gss_init(connection()->zeek_analyzer(),
connection()->zeek_analyzer()->Conn(),
is_orig);
}
return true;
%}
function proc_ssh2_rsa_secret(is_orig: bool): bool
%{
if ( ssh2_rsa_secret )
{
zeek::BifEvent::enqueue_ssh2_rsa_secret(connection()->zeek_analyzer(),
connection()->zeek_analyzer()->Conn(),
is_orig);
}
return true;
%}
function proc_ssh2_dh_gex_group(msg: SSH2_DH_GEX_GROUP): bool function proc_ssh2_dh_gex_group(msg: SSH2_DH_GEX_GROUP): bool
%{ %{
@ -257,11 +301,24 @@ refine typeattr SSH2_DH_GEX_GROUP += &let {
refine typeattr SSH2_ECC_REPLY += &let { refine typeattr SSH2_ECC_REPLY += &let {
proc_k: bool = $context.flow.proc_ssh2_server_host_key(k_s.val); proc_k: bool = $context.flow.proc_ssh2_server_host_key(k_s.val);
proc_q: bool = $context.flow.proc_ssh2_ecc_key(q_s.val, false); proc_q: bool = $context.flow.proc_ssh2_ecc_key(q_s.val, is_orig);
}; };
refine typeattr SSH2_ECC_INIT += &let { refine typeattr SSH2_ECC_INIT += &let {
proc: bool = $context.flow.proc_ssh2_ecc_key(q_c.val, true); proc: bool = $context.flow.proc_ssh2_ecc_key(q_c.val, is_orig);
proc_init: bool = $context.flow.proc_ssh2_ecc_init(is_orig);
};
refine typeattr SSH2_DH_GEX_INIT += &let {
proc_init: bool = $context.flow.proc_ssh2_dh_gex_init(is_orig);
};
refine typeattr SSH2_GSS_INIT += &let {
proc_init: bool = $context.flow.proc_ssh2_gss_init(is_orig);
};
refine typeattr SSH2_RSA_SECRET += &let {
proc_init: bool = $context.flow.proc_ssh2_rsa_secret(is_orig);
}; };
refine typeattr SSH1_PUBLIC_KEY += &let { refine typeattr SSH1_PUBLIC_KEY += &let {

View file

@ -130,7 +130,7 @@ type SSH2_KEXINIT(length: uint32, is_orig: bool) = record {
# KEX_DH exchanges # KEX_DH exchanges
type SSH2_Key_Exchange_DH_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of { type SSH2_Key_Exchange_DH_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of {
SSH_MSG_KEXDH_INIT -> init : SSH2_DH_GEX_INIT(length); SSH_MSG_KEXDH_INIT -> init : SSH2_DH_GEX_INIT(length, is_orig);
SSH_MSG_KEXDH_REPLY -> reply : SSH2_DH_GEX_REPLY(length); SSH_MSG_KEXDH_REPLY -> reply : SSH2_DH_GEX_REPLY(length);
default -> unknown: bytestring &length=length &transient; default -> unknown: bytestring &length=length &transient;
}; };
@ -141,7 +141,7 @@ type SSH2_Key_Exchange_DH_GEX_Message(is_orig: bool, msg_type: uint8, length: ui
SSH_MSG_KEX_DH_GEX_REQUEST_OLD -> request_old : SSH2_DH_GEX_REQUEST_OLD; SSH_MSG_KEX_DH_GEX_REQUEST_OLD -> request_old : SSH2_DH_GEX_REQUEST_OLD;
SSH_MSG_KEX_DH_GEX_REQUEST -> request : SSH2_DH_GEX_REQUEST; SSH_MSG_KEX_DH_GEX_REQUEST -> request : SSH2_DH_GEX_REQUEST;
SSH_MSG_KEX_DH_GEX_GROUP -> group : SSH2_DH_GEX_GROUP(length); SSH_MSG_KEX_DH_GEX_GROUP -> group : SSH2_DH_GEX_GROUP(length);
SSH_MSG_KEX_DH_GEX_INIT -> init : SSH2_DH_GEX_INIT(length); SSH_MSG_KEX_DH_GEX_INIT -> init : SSH2_DH_GEX_INIT(length, is_orig);
SSH_MSG_KEX_DH_GEX_REPLY -> reply : SSH2_DH_GEX_REPLY(length); SSH_MSG_KEX_DH_GEX_REPLY -> reply : SSH2_DH_GEX_REPLY(length);
default -> unknown : bytestring &length=length &transient; default -> unknown : bytestring &length=length &transient;
}; };
@ -161,7 +161,7 @@ type SSH2_DH_GEX_GROUP(length: uint32) = record {
g : ssh_string; g : ssh_string;
} &length=length; } &length=length;
type SSH2_DH_GEX_INIT(length: uint32) = record { type SSH2_DH_GEX_INIT(length: uint32, is_orig: bool) = record {
e : ssh_string; e : ssh_string;
} &length=length; } &length=length;
@ -175,7 +175,7 @@ type SSH2_DH_GEX_REPLY(length: uint32) = record {
type SSH2_Key_Exchange_RSA_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of { type SSH2_Key_Exchange_RSA_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of {
SSH_MSG_KEXRSA_PUBKEY -> pubkey : SSH2_RSA_PUBKEY(length); SSH_MSG_KEXRSA_PUBKEY -> pubkey : SSH2_RSA_PUBKEY(length);
SSH_MSG_KEXRSA_SECRET -> secret : SSH2_RSA_SECRET(length); SSH_MSG_KEXRSA_SECRET -> secret : SSH2_RSA_SECRET(length, is_orig);
SSH_MSG_KEXRSA_DONE -> done : SSH2_RSA_DONE(length); SSH_MSG_KEXRSA_DONE -> done : SSH2_RSA_DONE(length);
}; };
@ -184,7 +184,7 @@ type SSH2_RSA_PUBKEY(length: uint32) = record {
k_t : ssh_string; k_t : ssh_string;
} &length=length; } &length=length;
type SSH2_RSA_SECRET(length: uint32) = record { type SSH2_RSA_SECRET(length: uint32, is_orig: bool) = record {
encrypted_payload : ssh_string; encrypted_payload : ssh_string;
} &length=length; } &length=length;
@ -195,7 +195,7 @@ type SSH2_RSA_DONE(length: uint32) = record {
# KEX_GSS exchanges # KEX_GSS exchanges
type SSH2_Key_Exchange_GSS_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of { type SSH2_Key_Exchange_GSS_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of {
SSH_MSG_KEXGSS_INIT -> init : SSH2_GSS_INIT(length); SSH_MSG_KEXGSS_INIT -> init : SSH2_GSS_INIT(length, is_orig);
SSH_MSG_KEXGSS_CONTINUE -> cont : SSH2_GSS_CONTINUE(length); SSH_MSG_KEXGSS_CONTINUE -> cont : SSH2_GSS_CONTINUE(length);
SSH_MSG_KEXGSS_COMPLETE -> complete : SSH2_GSS_COMPLETE(length); SSH_MSG_KEXGSS_COMPLETE -> complete : SSH2_GSS_COMPLETE(length);
SSH_MSG_KEXGSS_HOSTKEY -> hostkey : SSH2_GSS_HOSTKEY(length); SSH_MSG_KEXGSS_HOSTKEY -> hostkey : SSH2_GSS_HOSTKEY(length);
@ -204,7 +204,7 @@ type SSH2_Key_Exchange_GSS_Message(is_orig: bool, msg_type: uint8, length: uint3
SSH_MSG_KEXGSS_GROUP -> group : SSH2_DH_GEX_GROUP(length); SSH_MSG_KEXGSS_GROUP -> group : SSH2_DH_GEX_GROUP(length);
}; };
type SSH2_GSS_INIT(length: uint32) = record { type SSH2_GSS_INIT(length: uint32, is_orig: bool) = record {
output_token : ssh_string; output_token : ssh_string;
e : ssh_string; e : ssh_string;
} &length=length; } &length=length;
@ -237,19 +237,19 @@ type SSH2_GSS_ERROR(length: uint32) = record {
# KEX_ECDH and KEX_ECMQV exchanges # KEX_ECDH and KEX_ECMQV exchanges
type SSH2_Key_Exchange_ECC_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of { type SSH2_Key_Exchange_ECC_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of {
SSH_MSG_KEX_ECDH_INIT -> init : SSH2_ECC_INIT(length); SSH_MSG_KEX_ECDH_INIT -> init : SSH2_ECC_INIT(length, is_orig);
SSH_MSG_KEX_ECDH_REPLY -> reply : SSH2_ECC_REPLY(length); SSH_MSG_KEX_ECDH_REPLY -> reply : SSH2_ECC_REPLY(length, is_orig);
}; };
# This deviates from the RFC. SSH_MSG_KEX_ECDH_INIT and # This deviates from the RFC. SSH_MSG_KEX_ECDH_INIT and
# SSH_MSG_KEX_ECMQV_INIT can be parsed the same way. # SSH_MSG_KEX_ECMQV_INIT can be parsed the same way.
type SSH2_ECC_INIT(length: uint32) = record { type SSH2_ECC_INIT(length: uint32, is_orig: bool) = record {
q_c : ssh_string; q_c : ssh_string;
}; };
# This deviates from the RFC. SSH_MSG_KEX_ECDH_REPLY and # This deviates from the RFC. SSH_MSG_KEX_ECDH_REPLY and
# SSH_MSG_KEX_ECMQV_REPLY can be parsed the same way. # SSH_MSG_KEX_ECMQV_REPLY can be parsed the same way.
type SSH2_ECC_REPLY(length: uint32) = record { type SSH2_ECC_REPLY(length: uint32, is_orig: bool) = record {
k_s : ssh_string; k_s : ssh_string;
q_s : ssh_string; q_s : ssh_string;
signature : ssh_string; signature : ssh_string;

View file

@ -0,0 +1,2 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
Detected an ECC INIT not from the TCP client

View file

@ -0,0 +1,12 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
Found SSH2_DH_GEX_INIT event
Found SSH2_DH_GEX_INIT event
Found SSH2_DH_GEX_INIT event
Found SSH2_DH_GEX_INIT event
Found SSH2_DH_GEX_INIT event
Found SSH2_DH_GEX_INIT event
Found SSH2_DH_GEX_INIT event
Found SSH2_DH_GEX_INIT event
Found SSH2_DH_GEX_INIT event
Found SSH2_DH_GEX_INIT event
Found SSH2_DH_GEX_INIT event

Binary file not shown.

View file

@ -0,0 +1,12 @@
# @TEST-EXEC: zeek -b -Cr $TRACES/ssh/reverse-ssh.pcap %INPUT >out
# @TEST-EXEC: btest-diff out
@load base/protocols/ssh
event ssh2_ecc_init(c: connection, is_orig: bool) {
## If a machine sends out the initial key material for the handshake, this should come from the client.
## In most cases, this client is the machine that set up the TCP connection.
if ( ! is_orig ) {
print("Detected an ECC INIT not from the TCP client");
}
}

View file

@ -0,0 +1,8 @@
# @TEST-EXEC: zeek -b -Cr $TRACES/ssh/sshguess.pcap %INPUT >out
# @TEST-EXEC: btest-diff out
@load base/protocols/ssh
event ssh2_dh_gex_init(c: connection, is_orig: bool) {
print("Found SSH2_DH_GEX_INIT event");
}