mirror of
https://github.com/zeek/zeek.git
synced 2025-10-17 05:58:20 +00:00
BinPAC SSH analyzer basic functionality.
This commit is contained in:
parent
9d6c8769ea
commit
78b5f6b94b
12 changed files with 465 additions and 301 deletions
|
@ -1,105 +1,135 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
// Generated by binpac_quickstart
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "NetVar.h"
|
||||
#include "SSH.h"
|
||||
#include "Event.h"
|
||||
#include "analyzer/protocol/tcp/ContentLine.h"
|
||||
|
||||
#include "analyzer/protocol/tcp/TCP_Reassembler.h"
|
||||
|
||||
#include "Reporter.h"
|
||||
|
||||
#include "events.bif.h"
|
||||
|
||||
using namespace analyzer::ssh;
|
||||
using namespace analyzer::SSH;
|
||||
|
||||
SSH_Analyzer::SSH_Analyzer(Connection* c)
|
||||
|
||||
: tcp::TCP_ApplicationAnalyzer("SSH", c)
|
||||
{
|
||||
orig = new tcp::ContentLine_Analyzer(c, true);
|
||||
orig->SetSkipPartial(true);
|
||||
orig->SetCRLFAsEOL(LF_as_EOL);
|
||||
AddSupportAnalyzer(orig);
|
||||
|
||||
resp = new tcp::ContentLine_Analyzer(c, false);
|
||||
resp->SetSkipPartial(true);
|
||||
resp->SetCRLFAsEOL(LF_as_EOL);
|
||||
AddSupportAnalyzer(resp);
|
||||
{
|
||||
interp = new binpac::SSH::SSH_Conn(this);
|
||||
had_gap = false;
|
||||
num_encrypted_packets_seen = 0;
|
||||
}
|
||||
|
||||
SSH_Analyzer::~SSH_Analyzer()
|
||||
{
|
||||
delete interp;
|
||||
}
|
||||
|
||||
void SSH_Analyzer::DeliverStream(int length, const u_char* data, bool is_orig)
|
||||
void SSH_Analyzer::Done()
|
||||
{
|
||||
tcp::TCP_ApplicationAnalyzer::DeliverStream(length, data, is_orig);
|
||||
|
||||
tcp::TCP_ApplicationAnalyzer::Done();
|
||||
|
||||
// We're all done processing this endpoint - flag it as such,
|
||||
// before we even determine whether we have any event generation
|
||||
// work to do, to make sure we don't do any further work on it.
|
||||
if ( is_orig )
|
||||
orig->SetSkipDeliveries(true);
|
||||
else
|
||||
resp->SetSkipDeliveries(true);
|
||||
interp->FlowEOF(true);
|
||||
interp->FlowEOF(false);
|
||||
|
||||
}
|
||||
|
||||
if ( TCP() )
|
||||
void SSH_Analyzer::EndpointEOF(bool is_orig)
|
||||
{
|
||||
tcp::TCP_ApplicationAnalyzer::EndpointEOF(is_orig);
|
||||
interp->FlowEOF(is_orig);
|
||||
}
|
||||
|
||||
void SSH_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
|
||||
{
|
||||
tcp::TCP_ApplicationAnalyzer::DeliverStream(len, data, orig);
|
||||
|
||||
assert(TCP());
|
||||
if ( TCP()->IsPartial() )
|
||||
return;
|
||||
|
||||
if ( had_gap )
|
||||
// If only one side had a content gap, we could still try to
|
||||
// deliver data to the other side if the script layer can handle this.
|
||||
return;
|
||||
|
||||
if ( num_encrypted_packets_seen || interp->get_state(orig) == binpac::SSH::ENCRYPTED )
|
||||
{
|
||||
// Don't try to parse version if there has already been a gap.
|
||||
tcp::TCP_Endpoint* endp = is_orig ? TCP()->Orig() : TCP()->Resp();
|
||||
if ( endp->HadGap() )
|
||||
return;
|
||||
}
|
||||
|
||||
const char* line = (const char*) data;
|
||||
|
||||
// The SSH identification looks like this:
|
||||
//
|
||||
// SSH-<protocolmajor>.<protocolminor>-<version>\n
|
||||
//
|
||||
// We're interested in the "version" part here.
|
||||
|
||||
if ( length < 4 || memcmp(line, "SSH-", 4) != 0 )
|
||||
{
|
||||
Weird("malformed_ssh_identification");
|
||||
ProtocolViolation("malformed ssh identification", line, length);
|
||||
ProcessEncrypted(len, orig);
|
||||
return;
|
||||
}
|
||||
|
||||
int i;
|
||||
for ( i = 4; i < length && line[i] != '-'; ++i )
|
||||
;
|
||||
|
||||
if ( TCP() )
|
||||
try
|
||||
{
|
||||
if ( length >= i )
|
||||
{
|
||||
IPAddr dst;
|
||||
|
||||
if ( is_orig )
|
||||
dst = TCP()->Orig()->dst_addr;
|
||||
else
|
||||
dst = TCP()->Resp()->dst_addr;
|
||||
|
||||
if ( Conn()->VersionFoundEvent(dst, line + i,
|
||||
length - i) )
|
||||
ProtocolConfirmation();
|
||||
else
|
||||
ProtocolViolation("malformed ssh version",
|
||||
line, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
Weird("malformed_ssh_version");
|
||||
ProtocolViolation("malformed ssh version", line, length);
|
||||
}
|
||||
interp->NewData(orig, data, data + len);
|
||||
}
|
||||
catch ( const binpac::Exception& e )
|
||||
{
|
||||
printf(" **** %s\n", e.c_msg());
|
||||
ProtocolViolation(fmt("Binpac exception: %s", e.c_msg()));
|
||||
}
|
||||
|
||||
// Generate SSH events.
|
||||
EventHandlerPtr event = is_orig ?
|
||||
ssh_client_version : ssh_server_version;
|
||||
if ( ! event )
|
||||
return;
|
||||
|
||||
val_list* vl = new val_list;
|
||||
vl->append(BuildConnVal());
|
||||
vl->append(new StringVal(length, line));
|
||||
|
||||
ConnectionEvent(event, vl);
|
||||
}
|
||||
|
||||
void SSH_Analyzer::Undelivered(int seq, int len, bool orig)
|
||||
{
|
||||
tcp::TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
|
||||
had_gap = true;
|
||||
interp->NewGap(orig, len);
|
||||
}
|
||||
|
||||
void SSH_Analyzer::ProcessEncrypted(int len, bool orig)
|
||||
{
|
||||
if (!num_encrypted_packets_seen)
|
||||
{
|
||||
initial_encrypted_packet_size = len;
|
||||
}
|
||||
// printf("Encrypted packet of size %d from %s.\n", len, orig?"client":"server");
|
||||
int relative_len = len - initial_encrypted_packet_size;
|
||||
if ( num_encrypted_packets_seen >= 2 )
|
||||
{
|
||||
int auth_result = AuthResult(relative_len, orig);
|
||||
if ( auth_result > 0 )
|
||||
{
|
||||
StringVal* method = new StringVal(AuthMethod(relative_len, orig));
|
||||
if ( auth_result == 1 )
|
||||
BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), method);
|
||||
if ( auth_result == 2 )
|
||||
BifEvent::generate_ssh_auth_failed(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), method);
|
||||
}
|
||||
packet_n_2_is_orig = packet_n_1_is_orig;
|
||||
packet_n_2_size = packet_n_1_size;
|
||||
}
|
||||
packet_n_1_is_orig = orig;
|
||||
packet_n_1_size = relative_len;
|
||||
num_encrypted_packets_seen++;
|
||||
}
|
||||
|
||||
|
||||
int SSH_Analyzer::AuthResult(int len, bool orig)
|
||||
{
|
||||
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;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* SSH_Analyzer::AuthMethod(int len, bool orig)
|
||||
{
|
||||
if ( packet_n_1_size == 96 ) // Password auth
|
||||
return "keyboard-interactive";
|
||||
if ( packet_n_1_size == 32 ) // Challenge-response auth
|
||||
return "challenge-response";
|
||||
if ( packet_n_2_size >= 112 &&
|
||||
packet_n_2_size <= 432 ) // Public key auth
|
||||
return "pubkey";
|
||||
if ( packet_n_2_size == 16 ) // Host-based auth
|
||||
return "host-based";
|
||||
return fmt("unknown auth method: n-1=%d n-2=%d", packet_n_1_size, packet_n_2_size);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue