mirror of
https://github.com/zeek/zeek.git
synced 2025-10-07 00:58:19 +00:00
SSH: Various updates.
This commit is contained in:
parent
78b5f6b94b
commit
2698fcea8e
6 changed files with 126 additions and 50 deletions
|
@ -18,6 +18,8 @@ export {
|
||||||
client: string &log &optional;
|
client: string &log &optional;
|
||||||
## The server's version string
|
## The server's version string
|
||||||
server: string &log &optional;
|
server: string &log &optional;
|
||||||
|
## The server's key fingerprint
|
||||||
|
host_key: string &log &optional;
|
||||||
## Auth result
|
## Auth result
|
||||||
result: string &log &optional;
|
result: string &log &optional;
|
||||||
## Auth method
|
## Auth method
|
||||||
|
@ -42,7 +44,7 @@ event bro_init() &priority=5
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
event ssh_version(c: connection, is_orig: bool, version: string)
|
event ssh_server_version(c: connection, version: string)
|
||||||
{
|
{
|
||||||
if ( !c?$ssh )
|
if ( !c?$ssh )
|
||||||
{
|
{
|
||||||
|
@ -52,16 +54,25 @@ event ssh_version(c: connection, is_orig: bool, version: string)
|
||||||
s$id = c$id;
|
s$id = c$id;
|
||||||
c$ssh = s;
|
c$ssh = s;
|
||||||
}
|
}
|
||||||
if ( is_orig )
|
c$ssh$server = version;
|
||||||
c$ssh$client = version;
|
}
|
||||||
else
|
|
||||||
c$ssh$server = version;
|
event ssh_client_version(c: connection, version: string)
|
||||||
# print c$ssh;
|
{
|
||||||
|
if ( !c?$ssh )
|
||||||
|
{
|
||||||
|
local s: SSH::Info;
|
||||||
|
s$ts = network_time();
|
||||||
|
s$uid = c$uid;
|
||||||
|
s$id = c$id;
|
||||||
|
c$ssh = s;
|
||||||
|
}
|
||||||
|
c$ssh$client = version;
|
||||||
}
|
}
|
||||||
|
|
||||||
event ssh_auth_successful(c: connection, method: string)
|
event ssh_auth_successful(c: connection, method: string)
|
||||||
{
|
{
|
||||||
if ( !c?$ssh )
|
if ( !c?$ssh || ( c$ssh?$result && c$ssh$result == "success" ) )
|
||||||
return;
|
return;
|
||||||
c$ssh$result = "success";
|
c$ssh$result = "success";
|
||||||
c$ssh$method = method;
|
c$ssh$method = method;
|
||||||
|
@ -70,7 +81,7 @@ event ssh_auth_successful(c: connection, method: string)
|
||||||
|
|
||||||
event ssh_auth_failed(c: connection, method: string)
|
event ssh_auth_failed(c: connection, method: string)
|
||||||
{
|
{
|
||||||
if ( !c?$ssh )
|
if ( !c?$ssh || ( c$ssh?$result && c$ssh$result == "success" ) )
|
||||||
return;
|
return;
|
||||||
c$ssh$result = "failure";
|
c$ssh$result = "failure";
|
||||||
c$ssh$method = method;
|
c$ssh$method = method;
|
||||||
|
@ -86,3 +97,12 @@ event connection_closed(c: connection)
|
||||||
Log::write(SSH::LOG, c$ssh);
|
Log::write(SSH::LOG, c$ssh);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event ssh_server_host_key(c: connection, key: string)
|
||||||
|
{
|
||||||
|
if ( !c?$ssh )
|
||||||
|
return;
|
||||||
|
local lx = str_split(md5_hash(key), vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30));
|
||||||
|
lx[0] = "";
|
||||||
|
c$ssh$host_key = sub(join_string_vec(lx, ":"), /:/, "");
|
||||||
|
}
|
|
@ -18,6 +18,8 @@ SSH_Analyzer::SSH_Analyzer(Connection* c)
|
||||||
interp = new binpac::SSH::SSH_Conn(this);
|
interp = new binpac::SSH::SSH_Conn(this);
|
||||||
had_gap = false;
|
had_gap = false;
|
||||||
num_encrypted_packets_seen = 0;
|
num_encrypted_packets_seen = 0;
|
||||||
|
initial_client_packet_size = 0;
|
||||||
|
initial_server_packet_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SSH_Analyzer::~SSH_Analyzer()
|
SSH_Analyzer::~SSH_Analyzer()
|
||||||
|
@ -54,7 +56,7 @@ void SSH_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
|
||||||
// deliver data to the other side if the script layer can handle this.
|
// deliver data to the other side if the script layer can handle this.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ( num_encrypted_packets_seen || interp->get_state(orig) == binpac::SSH::ENCRYPTED )
|
if ( interp->get_state(orig) == binpac::SSH::ENCRYPTED )
|
||||||
{
|
{
|
||||||
ProcessEncrypted(len, orig);
|
ProcessEncrypted(len, orig);
|
||||||
return;
|
return;
|
||||||
|
@ -80,23 +82,33 @@ void SSH_Analyzer::Undelivered(int seq, int len, bool orig)
|
||||||
|
|
||||||
void SSH_Analyzer::ProcessEncrypted(int len, bool orig)
|
void SSH_Analyzer::ProcessEncrypted(int len, bool orig)
|
||||||
{
|
{
|
||||||
if (!num_encrypted_packets_seen)
|
if (orig && !initial_client_packet_size)
|
||||||
{
|
initial_client_packet_size = len;
|
||||||
initial_encrypted_packet_size = len;
|
if (!orig && !initial_server_packet_size)
|
||||||
}
|
initial_server_packet_size = len;
|
||||||
// printf("Encrypted packet of size %d from %s.\n", len, orig?"client":"server");
|
|
||||||
int relative_len = len - initial_encrypted_packet_size;
|
int relative_len;
|
||||||
if ( num_encrypted_packets_seen >= 2 )
|
if (orig)
|
||||||
|
relative_len = len - initial_client_packet_size;
|
||||||
|
else
|
||||||
|
relative_len = len - initial_server_packet_size;
|
||||||
|
// printf("Encrypted packet of length %d from %s.\n", len, orig?"client":"server");
|
||||||
|
if ( num_encrypted_packets_seen >= 4 )
|
||||||
{
|
{
|
||||||
int auth_result = AuthResult(relative_len, orig);
|
int auth_result = AuthResult(relative_len, orig);
|
||||||
if ( auth_result > 0 )
|
if ( auth_result > 0 )
|
||||||
{
|
{
|
||||||
|
num_encrypted_packets_seen = 1;
|
||||||
|
//printf("Have auth\n");
|
||||||
StringVal* method = new StringVal(AuthMethod(relative_len, orig));
|
StringVal* method = new StringVal(AuthMethod(relative_len, orig));
|
||||||
if ( auth_result == 1 )
|
if ( auth_result == 1 )
|
||||||
BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), method);
|
BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), method);
|
||||||
if ( auth_result == 2 )
|
if ( auth_result == 2 )
|
||||||
BifEvent::generate_ssh_auth_failed(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), method);
|
BifEvent::generate_ssh_auth_failed(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), method);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if ( num_encrypted_packets_seen >= 2 )
|
||||||
|
{
|
||||||
packet_n_2_is_orig = packet_n_1_is_orig;
|
packet_n_2_is_orig = packet_n_1_is_orig;
|
||||||
packet_n_2_size = packet_n_1_size;
|
packet_n_2_size = packet_n_1_size;
|
||||||
}
|
}
|
||||||
|
@ -108,7 +120,7 @@ void SSH_Analyzer::ProcessEncrypted(int len, bool orig)
|
||||||
|
|
||||||
int SSH_Analyzer::AuthResult(int len, bool orig)
|
int SSH_Analyzer::AuthResult(int len, bool orig)
|
||||||
{
|
{
|
||||||
if ( orig && !packet_n_1_is_orig && packet_n_2_is_orig )
|
if ( !orig && packet_n_1_is_orig && !packet_n_2_is_orig )
|
||||||
{
|
{
|
||||||
if ( len == -16 )
|
if ( len == -16 )
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -123,13 +135,13 @@ int SSH_Analyzer::AuthResult(int len, bool orig)
|
||||||
const char* SSH_Analyzer::AuthMethod(int len, bool orig)
|
const char* SSH_Analyzer::AuthMethod(int len, bool orig)
|
||||||
{
|
{
|
||||||
if ( packet_n_1_size == 96 ) // Password auth
|
if ( packet_n_1_size == 96 ) // Password auth
|
||||||
return "keyboard-interactive";
|
return fmt("password (L=%d, L-1=%d, L-2=%d)", len, packet_n_1_size, packet_n_2_size);
|
||||||
if ( packet_n_1_size == 32 ) // Challenge-response auth
|
if ( packet_n_1_size == 32 && ( packet_n_2_size == 0 || packet_n_2_size == 48 ) ) // Challenge-response auth
|
||||||
return "challenge-response";
|
return fmt("challenge-response (L=%d, L-1=%d, L-2=%d)", len, packet_n_1_size, packet_n_2_size);
|
||||||
if ( packet_n_2_size >= 112 &&
|
if ( packet_n_2_size >= 112 &&
|
||||||
packet_n_2_size <= 432 ) // Public key auth
|
packet_n_2_size <= 432 ) // Public key auth
|
||||||
return "pubkey";
|
return fmt("pubkey (L=%d, L-1=%d, L-2=%d)", len, packet_n_1_size, packet_n_2_size);
|
||||||
if ( packet_n_2_size == 16 ) // Host-based auth
|
if ( packet_n_2_size == 16 ) // Host-based auth
|
||||||
return "host-based";
|
return fmt("host-based (L=%d, L-1=%d, L-2=%d)", len, packet_n_1_size, packet_n_2_size);
|
||||||
return fmt("unknown auth method: n-1=%d n-2=%d", packet_n_1_size, packet_n_2_size);
|
return fmt("unknown (L=%d, L-1=%d, L-2=%d)", len, packet_n_1_size, packet_n_2_size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,9 @@ public:
|
||||||
|
|
||||||
static bool Available()
|
static bool Available()
|
||||||
{
|
{
|
||||||
// TODO: After you define your events, || them together here.
|
return ( ssh_server_version || ssh_client_version ||
|
||||||
// See events.bif for more information
|
ssh_auth_successful || ssh_auth_failed ||
|
||||||
return ( ssh_event );
|
ssh_server_capabilities || ssh_server_host_key );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -49,7 +49,8 @@ protected:
|
||||||
bool had_gap;
|
bool had_gap;
|
||||||
|
|
||||||
// Packet analysis stuff
|
// Packet analysis stuff
|
||||||
int initial_encrypted_packet_size;
|
int initial_client_packet_size;
|
||||||
|
int initial_server_packet_size;
|
||||||
int num_encrypted_packets_seen;
|
int num_encrypted_packets_seen;
|
||||||
|
|
||||||
bool packet_n_1_is_orig;
|
bool packet_n_1_is_orig;
|
||||||
|
|
|
@ -1,17 +1,11 @@
|
||||||
# Generated by binpac_quickstart
|
event ssh_server_version%(c: connection, version: string%);
|
||||||
|
|
||||||
# In this file, you'll define the events that your analyzer will generate. A sample event is included.
|
event ssh_client_version%(c: connection, version: string%);
|
||||||
|
|
||||||
## Generated for SSH connections
|
|
||||||
##
|
|
||||||
## See `Google <http://lmgtfy.com/?q=SSH>`__ for more information about SSH
|
|
||||||
##
|
|
||||||
## c: The connection
|
|
||||||
##3
|
|
||||||
event ssh_event%(c: connection%);
|
|
||||||
|
|
||||||
event ssh_version%(c: connection, is_orig: bool, version: string%);
|
|
||||||
|
|
||||||
event ssh_auth_successful%(c: connection, method: string%);
|
event ssh_auth_successful%(c: connection, method: string%);
|
||||||
|
|
||||||
event ssh_auth_failed%(c: connection, method: string%);
|
event ssh_auth_failed%(c: connection, method: string%);
|
||||||
|
|
||||||
|
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%);
|
||||||
|
|
||||||
|
event ssh_server_host_key%(c: connection, key: string%);
|
|
@ -3,8 +3,34 @@
|
||||||
refine flow SSH_Flow += {
|
refine flow SSH_Flow += {
|
||||||
function proc_ssh_version(msg: SSH_Version): bool
|
function proc_ssh_version(msg: SSH_Version): bool
|
||||||
%{
|
%{
|
||||||
BifEvent::generate_ssh_version(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), ${msg.is_orig},
|
if ( ssh_client_version && ${msg.is_orig } )
|
||||||
bytestring_to_val(${msg.version}));
|
BifEvent::generate_ssh_client_version(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), bytestring_to_val(${msg.version}));
|
||||||
|
else if ( ssh_server_version )
|
||||||
|
BifEvent::generate_ssh_server_version(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), bytestring_to_val(${msg.version}));
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function proc_ssh_kexinit(msg: SSH_KEXINIT): bool
|
||||||
|
%{
|
||||||
|
if ( ssh_server_capabilities )
|
||||||
|
BifEvent::generate_ssh_server_capabilities(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(),
|
||||||
|
bytestring_to_val(${msg.kex_algorithms}), bytestring_to_val(${msg.server_host_key_algorithms}),
|
||||||
|
bytestring_to_val(${msg.encryption_algorithms_client_to_server}),
|
||||||
|
bytestring_to_val(${msg.encryption_algorithms_server_to_client}),
|
||||||
|
bytestring_to_val(${msg.mac_algorithms_client_to_server}),
|
||||||
|
bytestring_to_val(${msg.mac_algorithms_server_to_client}),
|
||||||
|
bytestring_to_val(${msg.compression_algorithms_client_to_server}),
|
||||||
|
bytestring_to_val(${msg.compression_algorithms_server_to_client}),
|
||||||
|
bytestring_to_val(${msg.languages_client_to_server}),
|
||||||
|
bytestring_to_val(${msg.languages_server_to_client}));
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function proc_ssh_server_host_key(key: bytestring): bool
|
||||||
|
%{
|
||||||
|
if ( ssh_server_host_key )
|
||||||
|
BifEvent::generate_ssh_server_host_key(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(),
|
||||||
|
bytestring_to_val(${key}));
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
@ -14,12 +40,30 @@ refine flow SSH_Flow += {
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
function debug(loc: uint8): bool
|
||||||
|
%{
|
||||||
|
printf("DEBUG: %d", loc);
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
refine typeattr SSH_Version += &let {
|
refine typeattr SSH_Version += &let {
|
||||||
proc: bool = $context.flow.proc_ssh_version(this);
|
proc: bool = $context.flow.proc_ssh_version(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
refine typeattr SSH_KEXINIT += &let {
|
||||||
|
proc: bool = $context.flow.proc_ssh_kexinit(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
refine typeattr SSH_DH_GEX_REPLY += &let {
|
||||||
|
proc: bool = $context.flow.proc_ssh_server_host_key(k_s.val);
|
||||||
|
};
|
||||||
|
|
||||||
|
refine typeattr SSH_DH_GEX_GROUP += &let {
|
||||||
|
proc: bool = $context.flow.proc_ssh_server_host_key(p.val);
|
||||||
|
};
|
||||||
|
|
||||||
refine typeattr SSH_Message += &let {
|
refine typeattr SSH_Message += &let {
|
||||||
proc_newkeys: bool = $context.flow.proc_newkeys() &if(msg_type == SSH_MSG_NEWKEYS);
|
proc_newkeys: bool = $context.flow.proc_newkeys() &if(msg_type == SSH_MSG_NEWKEYS);
|
||||||
};
|
};
|
|
@ -41,7 +41,7 @@ enum message_id {
|
||||||
type SSH_PDU(is_orig: bool) = case $context.connection.get_state(is_orig) of {
|
type SSH_PDU(is_orig: bool) = case $context.connection.get_state(is_orig) of {
|
||||||
VERSION_EXCHANGE -> version: SSH_Version(is_orig);
|
VERSION_EXCHANGE -> version: SSH_Version(is_orig);
|
||||||
KEY_EXCHANGE_CLEARTEXT -> kex: SSH_Key_Exchange(is_orig);
|
KEY_EXCHANGE_CLEARTEXT -> kex: SSH_Key_Exchange(is_orig);
|
||||||
ENCRYPTED -> unk: bytestring &length=100;
|
ENCRYPTED -> ciphertext: bytestring &length=1 &transient;
|
||||||
} &byteorder=bigendian;
|
} &byteorder=bigendian;
|
||||||
|
|
||||||
type SSH_Version(is_orig: bool) = record {
|
type SSH_Version(is_orig: bool) = record {
|
||||||
|
@ -71,12 +71,13 @@ type SSH_Payload(is_orig: bool, packet_length: uint32) = record {
|
||||||
};
|
};
|
||||||
|
|
||||||
type SSH_Message(is_orig: bool, msg_type: uint8, packet_length: uint32) = case msg_type of {
|
type SSH_Message(is_orig: bool, msg_type: uint8, packet_length: uint32) = case msg_type of {
|
||||||
SSH_MSG_KEXINIT -> kexinit: SSH_KEXINIT(is_orig, packet_length);
|
SSH_MSG_KEXINIT -> kexinit: SSH_KEXINIT(is_orig, packet_length);
|
||||||
SSH_MSG_KEX_DH_GEX_REQUEST -> dh_gex_request: SSH_DH_GEX_REQUEST(is_orig, packet_length);
|
SSH_MSG_KEX_DH_GEX_REQUEST -> dh_gex_request: SSH_DH_GEX_REQUEST(is_orig, packet_length);
|
||||||
SSH_MSG_KEX_DH_GEX_GROUP -> dh_gex_group: SSH_DH_GEX_GROUP(is_orig, packet_length);
|
SSH_MSG_KEX_DH_GEX_REQUEST_OLD -> dh_gex_request_old: SSH_DH_GEX_REQUEST_OLD(is_orig, packet_length);
|
||||||
SSH_MSG_KEX_DH_GEX_INIT -> dh_gex_init: SSH_DH_GEX_INIT(is_orig, packet_length);
|
SSH_MSG_KEX_DH_GEX_GROUP -> dh_gex_group: SSH_DH_GEX_GROUP(is_orig, packet_length);
|
||||||
SSH_MSG_KEX_DH_GEX_REPLY -> dh_gex_reply: SSH_DH_GEX_REPLY(is_orig, packet_length);
|
SSH_MSG_KEX_DH_GEX_INIT -> dh_gex_init: SSH_DH_GEX_INIT(is_orig, packet_length);
|
||||||
default -> unknown: bytestring &length=packet_length;
|
SSH_MSG_KEX_DH_GEX_REPLY -> dh_gex_reply: SSH_DH_GEX_REPLY(is_orig, packet_length);
|
||||||
|
SSH_MSG_NEWKEYS -> new_keys: bytestring &length=packet_length;
|
||||||
} &let {
|
} &let {
|
||||||
detach: bool = $context.connection.update_state(ENCRYPTED, is_orig) &if(msg_type == SSH_MSG_NEWKEYS);
|
detach: bool = $context.connection.update_state(ENCRYPTED, is_orig) &if(msg_type == SSH_MSG_NEWKEYS);
|
||||||
};
|
};
|
||||||
|
@ -113,6 +114,10 @@ type SSH_DH_GEX_REQUEST(is_orig: bool, length: uint32) = record {
|
||||||
max: uint32;
|
max: uint32;
|
||||||
} &length=12;
|
} &length=12;
|
||||||
|
|
||||||
|
type SSH_DH_GEX_REQUEST_OLD(is_orig: bool, length: uint32) = record {
|
||||||
|
payload: bytestring &length=length;
|
||||||
|
} &length=length;
|
||||||
|
|
||||||
type SSH_DH_GEX_GROUP(is_orig: bool, length: uint32) = record {
|
type SSH_DH_GEX_GROUP(is_orig: bool, length: uint32) = record {
|
||||||
p: mpint;
|
p: mpint;
|
||||||
g: mpint;
|
g: mpint;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue