mirror of
https://github.com/zeek/zeek.git
synced 2025-10-14 12:38:20 +00:00
Refactored the SSH analyzer. Added supported for algorithm detection and more key exchange message types.
This commit is contained in:
parent
245bd07af7
commit
05ecac2497
11 changed files with 745 additions and 328 deletions
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "Reporter.h"
|
||||
|
||||
#include "types.bif.h"
|
||||
#include "events.bif.h"
|
||||
|
||||
using namespace analyzer::SSH;
|
||||
|
@ -18,10 +19,10 @@ 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;
|
||||
}
|
||||
skipped_banner = false;
|
||||
service_accept_size = 0;
|
||||
userauth_failure_size = 0;
|
||||
}
|
||||
|
||||
SSH_Analyzer::~SSH_Analyzer()
|
||||
{
|
||||
|
@ -59,7 +60,13 @@ void SSH_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
|
|||
|
||||
if ( interp->get_state(orig) == binpac::SSH::ENCRYPTED )
|
||||
{
|
||||
ProcessEncrypted(len, orig);
|
||||
if ( ssh_encrypted_packet )
|
||||
BifEvent::generate_ssh_encrypted_packet(interp->bro_analyzer(), interp->bro_analyzer()->Conn(),
|
||||
orig, len);
|
||||
|
||||
if ( !auth_decision_made )
|
||||
ProcessEncrypted(len, orig);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -69,7 +76,6 @@ 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()));
|
||||
}
|
||||
}
|
||||
|
@ -83,84 +89,65 @@ void SSH_Analyzer::Undelivered(uint64 seq, int len, bool orig)
|
|||
|
||||
void SSH_Analyzer::ProcessEncrypted(int len, bool orig)
|
||||
{
|
||||
if (orig && !initial_client_packet_size)
|
||||
initial_client_packet_size = len;
|
||||
if (!orig && !initial_server_packet_size)
|
||||
initial_server_packet_size = len;
|
||||
|
||||
int relative_len;
|
||||
if (orig)
|
||||
relative_len = len - initial_client_packet_size;
|
||||
else
|
||||
relative_len = len - initial_server_packet_size;
|
||||
|
||||
if ( !auth_decision_made && ( num_encrypted_packets_seen > 3 ) )
|
||||
// We're interested in messages from the server for SSH2
|
||||
if (!orig && (interp->get_version() == binpac::SSH::SSH2))
|
||||
{
|
||||
int auth_result = AuthResult(relative_len, orig, interp->get_version());
|
||||
if ( auth_result > 0 )
|
||||
// The first thing we see and want to know is the length of
|
||||
// SSH_MSG_SERVICE_REQUEST, which has a fixed (decrypted) size
|
||||
// of 24 bytes (17 for content pad-aligned to 8-byte
|
||||
// boundaries)
|
||||
if (!service_accept_size)
|
||||
{
|
||||
service_accept_size = len;
|
||||
return;
|
||||
}
|
||||
|
||||
// If our user can authenticate via the "none" method, this
|
||||
// packet will be a SSH_MSG_USERAUTH_SUCCESS, which has a
|
||||
// fixed (decrypted) size of 8 bytes (1 for content
|
||||
// pad-aligned to 8-byte boundaries). relative_len would be
|
||||
// -16.
|
||||
if (!userauth_failure_size && (len + 16 == service_accept_size))
|
||||
{
|
||||
auth_decision_made = true;
|
||||
if ( auth_result == 1 )
|
||||
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(),
|
||||
len,
|
||||
packet_n_1_size,
|
||||
packet_n_2_size);
|
||||
if ( ssh_auth_successful )
|
||||
BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Normally, this packet would be a SSH_MSG_USERAUTH_FAILURE
|
||||
// message, with a variable length, depending on the
|
||||
// authentication methods the server supports. If it's too
|
||||
// big, it might contain a pre-auth MOTD/banner, so we'll just
|
||||
// skip it.
|
||||
if (!userauth_failure_size)
|
||||
{
|
||||
if ( !skipped_banner && (len - service_accept_size) > 256 )
|
||||
{
|
||||
skipped_banner = true;
|
||||
return;
|
||||
}
|
||||
userauth_failure_size = len;
|
||||
return;
|
||||
}
|
||||
|
||||
// If we've already seen a failure, let's see if this is
|
||||
// another packet of the same size.
|
||||
if (len == userauth_failure_size)
|
||||
{
|
||||
if ( ssh_auth_failed )
|
||||
BifEvent::generate_ssh_auth_failed(interp->bro_analyzer(), interp->bro_analyzer()->Conn());
|
||||
return;
|
||||
}
|
||||
|
||||
// ...or a success packet.
|
||||
if (len - service_accept_size == -16)
|
||||
{
|
||||
auth_decision_made = true;
|
||||
if ( ssh_auth_successful )
|
||||
BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ( ( num_encrypted_packets_seen >= 2 ) &&
|
||||
( orig != packet_n_1_is_orig ) )
|
||||
{
|
||||
packet_n_2_is_orig = packet_n_1_is_orig;
|
||||
packet_n_2_size = packet_n_1_size;
|
||||
}
|
||||
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;
|
||||
if ( ! ( ( interp->get_version() == binpac::SSH::SSH1 ) && len > 90 ) )
|
||||
num_encrypted_packets_seen++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int SSH_Analyzer::AuthResult(int len, bool orig, int version)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue