mirror of
https://github.com/zeek/zeek.git
synced 2025-10-08 09:38:19 +00:00
Merge remote-tracking branch 'origin/topic/vladg/ssh'
I replaced a few strcmps with either calls to std::str.compare or with the == operator of BroString. Also changed two of the input framework tests that did not pass anymore after the merge. The new SSH analyzer no longer loads the scripts that let network time run, hence those tests failed because updates were not propagated from the threads (that took a while to find.) * origin/topic/vladg/ssh: (25 commits) SSH: Register analyzer for 22/tcp. SSH: Add 22/tcp to likely_server_ports SSH: Ignore encrypted packets by default. SSH: Fix some edge-cases which created BinPAC exceptions SSH: Add memleak btest SSH: Update baselines SSH: Added some more events for SSH2 SSH: Intel framework integration (PUBKEY_HASH) Update baselines for new SSH analyzer. Update SSH policy scripts with new events. SSH: Add documentation Refactoring ssh-protocol.pac: SSH: Use the compression_algorithms const in another place. Some cleanup and refactoring on SSH main.bro. SSH: A bit of code cleanup. Move SSH constants to consts.pac SSH: Cleanup code style. SSH: Fix some memleaks. Refactored the SSH analyzer. Added supported for algorithm detection and more key exchange message types. Add host key support for SSH1. Add support for SSH1 Move SSH analyzer to new plugin architecture. ... Conflicts: scripts/base/protocols/ssh/main.bro testing/btest/Baseline/core.print-bpf-filters/output2 testing/btest/Baseline/plugins.hooks/output BIT-1344: #merged
This commit is contained in:
commit
f79b5adc08
38 changed files with 1494 additions and 324 deletions
4
CHANGES
4
CHANGES
|
@ -1,4 +1,8 @@
|
||||||
|
|
||||||
|
2.3-631 | 2015-03-25 11:03:12 -0700
|
||||||
|
|
||||||
|
* New SSH analyzer. (Vlad Grigorescu)
|
||||||
|
|
||||||
2.3-600 | 2015-03-25 10:23:46 -0700
|
2.3-600 | 2015-03-25 10:23:46 -0700
|
||||||
|
|
||||||
* Add defensive checks in code to calculate log rotation intervals.
|
* Add defensive checks in code to calculate log rotation intervals.
|
||||||
|
|
4
NEWS
4
NEWS
|
@ -28,6 +28,10 @@ New Functionality
|
||||||
- Bro now has supoprt for the MySQL wire protocol. Activity gets
|
- Bro now has supoprt for the MySQL wire protocol. Activity gets
|
||||||
logged into mysql.log.
|
logged into mysql.log.
|
||||||
|
|
||||||
|
- Bro now features a completely rewritten, enhanced SSH analyzer. A lot
|
||||||
|
more information about SSH sessions is logged. The analyzer is able to
|
||||||
|
determine if logins failed or succeeded in most circumstances.
|
||||||
|
|
||||||
- Bro's file analysis now supports reassembly of files that are not
|
- Bro's file analysis now supports reassembly of files that are not
|
||||||
transferred/seen sequentially.
|
transferred/seen sequentially.
|
||||||
|
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
2.3-600
|
2.3-631
|
||||||
|
|
|
@ -32,6 +32,8 @@ export {
|
||||||
FILE_NAME,
|
FILE_NAME,
|
||||||
## Certificate SHA-1 hash.
|
## Certificate SHA-1 hash.
|
||||||
CERT_HASH,
|
CERT_HASH,
|
||||||
|
## Public key MD5 hash. (SSH server host keys are a good example.)
|
||||||
|
PUBKEY_HASH,
|
||||||
};
|
};
|
||||||
|
|
||||||
## Data about an :bro:type:`Intel::Item`.
|
## Data about an :bro:type:`Intel::Item`.
|
||||||
|
|
|
@ -21,7 +21,7 @@ export {
|
||||||
## underscores and using leading capitals on each word except for
|
## underscores and using leading capitals on each word except for
|
||||||
## abbreviations which are kept in all capitals. For example,
|
## abbreviations which are kept in all capitals. For example,
|
||||||
## SSH::Password_Guessing is for hosts that have crossed a threshold of
|
## SSH::Password_Guessing is for hosts that have crossed a threshold of
|
||||||
## heuristically determined failed SSH logins.
|
## failed SSH logins.
|
||||||
type Type: enum {
|
type Type: enum {
|
||||||
## Notice reporting a count of how often a notice occurred.
|
## Notice reporting a count of how often a notice occurred.
|
||||||
Tally,
|
Tally,
|
||||||
|
|
|
@ -2216,6 +2216,41 @@ export {
|
||||||
const heartbeat_interval = 1.0 secs &redef;
|
const heartbeat_interval = 1.0 secs &redef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module SSH;
|
||||||
|
|
||||||
|
export {
|
||||||
|
## The client and server each have some preferences for the algorithms used
|
||||||
|
## in each direction.
|
||||||
|
type Algorithm_Prefs: record {
|
||||||
|
## The algorithm preferences for client to server communication
|
||||||
|
client_to_server: vector of string &optional;
|
||||||
|
## The algorithm preferences for server to client communication
|
||||||
|
server_to_client: vector of string &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
## This record lists the preferences of an SSH endpoint for
|
||||||
|
## algorithm selection. During the initial :abbr:`SSH (Secure Shell)`
|
||||||
|
## key exchange, each endpoint lists the algorithms
|
||||||
|
## that it supports, in order of preference. See
|
||||||
|
## :rfc:`4253#section-7.1` for details.
|
||||||
|
type Capabilities: record {
|
||||||
|
## Key exchange algorithms
|
||||||
|
kex_algorithms: string_vec;
|
||||||
|
## The algorithms supported for the server host key
|
||||||
|
server_host_key_algorithms: string_vec;
|
||||||
|
## Symmetric encryption algorithm preferences
|
||||||
|
encryption_algorithms: Algorithm_Prefs;
|
||||||
|
## Symmetric MAC algorithm preferences
|
||||||
|
mac_algorithms: Algorithm_Prefs;
|
||||||
|
## Compression algorithm preferences
|
||||||
|
compression_algorithms: Algorithm_Prefs;
|
||||||
|
## Language preferences
|
||||||
|
languages: Algorithm_Prefs &optional;
|
||||||
|
## Are these the capabilities of the server?
|
||||||
|
is_server: bool;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
module GLOBAL;
|
module GLOBAL;
|
||||||
|
|
||||||
## An NTP message.
|
## An NTP message.
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Support for Secure Shell (SSH) protocol analysis.
|
|
|
@ -1,3 +1,2 @@
|
||||||
@load ./main
|
@load ./main
|
||||||
|
|
||||||
@load-sigs ./dpd.sig
|
@load-sigs ./dpd.sig
|
|
@ -1,6 +1,6 @@
|
||||||
signature dpd_ssh_client {
|
signature dpd_ssh_client {
|
||||||
ip-proto == tcp
|
ip-proto == tcp
|
||||||
payload /^[sS][sS][hH]-/
|
payload /^[sS][sS][hH]-[12]\./
|
||||||
requires-reverse-signature dpd_ssh_server
|
requires-reverse-signature dpd_ssh_server
|
||||||
enable "ssh"
|
enable "ssh"
|
||||||
tcp-state originator
|
tcp-state originator
|
||||||
|
@ -8,6 +8,6 @@ signature dpd_ssh_client {
|
||||||
|
|
||||||
signature dpd_ssh_server {
|
signature dpd_ssh_server {
|
||||||
ip-proto == tcp
|
ip-proto == tcp
|
||||||
payload /^[sS][sS][hH]-/
|
payload /^[sS][sS][hH]-[12]\./
|
||||||
tcp-state responder
|
tcp-state responder
|
||||||
}
|
}
|
|
@ -1,15 +1,5 @@
|
||||||
##! Base SSH analysis script. The heuristic to blindly determine success or
|
##! Implements base functionality for SSH analysis. Generates the ssh.log file.
|
||||||
##! failure for SSH connections is implemented here. At this time, it only
|
|
||||||
##! uses the size of the data being returned from the server to make the
|
|
||||||
##! heuristic determination about success of the connection.
|
|
||||||
##! Requires that :bro:id:`use_conn_size_analyzer` is set to T! The heuristic
|
|
||||||
##! is not attempted if the connection size analyzer isn't enabled.
|
|
||||||
|
|
||||||
@load base/protocols/conn
|
|
||||||
@load base/frameworks/notice
|
|
||||||
@load base/utils/site
|
|
||||||
@load base/utils/thresholds
|
|
||||||
@load base/utils/conn-ids
|
|
||||||
@load base/utils/directions-and-hosts
|
@load base/utils/directions-and-hosts
|
||||||
|
|
||||||
module SSH;
|
module SSH;
|
||||||
|
@ -25,45 +15,63 @@ export {
|
||||||
uid: string &log;
|
uid: string &log;
|
||||||
## The connection's 4-tuple of endpoint addresses/ports.
|
## The connection's 4-tuple of endpoint addresses/ports.
|
||||||
id: conn_id &log;
|
id: conn_id &log;
|
||||||
## Indicates if the login was heuristically guessed to be
|
## SSH major version (1 or 2)
|
||||||
## "success", "failure", or "undetermined".
|
version: count &log;
|
||||||
status: string &log &default="undetermined";
|
## Authentication result (T=success, F=failure, unset=unknown)
|
||||||
|
auth_success: bool &log &optional;
|
||||||
## Direction of the connection. If the client was a local host
|
## Direction of the connection. If the client was a local host
|
||||||
## logging into an external host, this would be OUTBOUND. INBOUND
|
## logging into an external host, this would be OUTBOUND. INBOUND
|
||||||
## would be set for the opposite situation.
|
## would be set for the opposite situation.
|
||||||
# TODO: handle local-local and remote-remote better.
|
# TODO - handle local-local and remote-remote better.
|
||||||
direction: Direction &log &optional;
|
direction: Direction &log &optional;
|
||||||
## Software string from the client.
|
## The client's version string
|
||||||
client: string &log &optional;
|
client: string &log &optional;
|
||||||
## Software string from the server.
|
## The server's version string
|
||||||
server: string &log &optional;
|
server: string &log &optional;
|
||||||
## Indicate if the SSH session is done being watched.
|
## The encryption algorithm in use
|
||||||
done: bool &default=F;
|
cipher_alg: string &log &optional;
|
||||||
|
## The signing (MAC) algorithm in use
|
||||||
|
mac_alg: string &log &optional;
|
||||||
|
## The compression algorithm in use
|
||||||
|
compression_alg: string &log &optional;
|
||||||
|
## The key exchange algorithm in use
|
||||||
|
kex_alg: string &log &optional;
|
||||||
|
## The server host key's algorithm
|
||||||
|
host_key_alg: string &log &optional;
|
||||||
|
## The server's key fingerprint
|
||||||
|
host_key: string &log &optional;
|
||||||
};
|
};
|
||||||
|
|
||||||
## The size in bytes of data sent by the server at which the SSH
|
## The set of compression algorithms. We can't accurately determine
|
||||||
## connection is presumed to be successful.
|
## authentication success or failure when compression is enabled.
|
||||||
const authentication_data_size = 4000 &redef;
|
const compression_algorithms = set("zlib", "zlib@openssh.com") &redef;
|
||||||
|
|
||||||
## If true, we tell the event engine to not look at further data
|
## If true, we tell the event engine to not look at further data
|
||||||
## packets after the initial SSH handshake. Helps with performance
|
## packets after the initial SSH handshake. Helps with performance
|
||||||
## (especially with large file transfers) but precludes some
|
## (especially with large file transfers) but precludes some
|
||||||
## kinds of analyses.
|
## kinds of analyses. Defaults to T.
|
||||||
const skip_processing_after_detection = F &redef;
|
const skip_processing_after_detection = T &redef;
|
||||||
|
|
||||||
## Event that is generated when the heuristic thinks that a login
|
## Event that can be handled to access the SSH record as it is sent on
|
||||||
## was successful.
|
## to the logging framework.
|
||||||
global heuristic_successful_login: event(c: connection);
|
|
||||||
|
|
||||||
## Event that is generated when the heuristic thinks that a login
|
|
||||||
## failed.
|
|
||||||
global heuristic_failed_login: event(c: connection);
|
|
||||||
|
|
||||||
## Event that can be handled to access the :bro:type:`SSH::Info`
|
|
||||||
## record as it is sent on to the logging framework.
|
|
||||||
global log_ssh: event(rec: Info);
|
global log_ssh: event(rec: Info);
|
||||||
|
|
||||||
|
## Event that can be handled when the analyzer sees an SSH server host
|
||||||
|
## key. This abstracts :bro:id:`SSH::ssh1_server_host_key` and
|
||||||
|
## :bro:id:`SSH::ssh2_server_host_key`.
|
||||||
|
global ssh_server_host_key: event(c: connection, hash: string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
redef record Info += {
|
||||||
|
# This connection has been logged (internal use)
|
||||||
|
logged: bool &default=F;
|
||||||
|
# Number of failures seen (internal use)
|
||||||
|
num_failures: count &default=0;
|
||||||
|
# Store capabilities from the first host for
|
||||||
|
# comparison with the second (internal use)
|
||||||
|
capabilities: Capabilities &optional;
|
||||||
|
};
|
||||||
|
|
||||||
redef record connection += {
|
redef record connection += {
|
||||||
ssh: Info &optional;
|
ssh: Info &optional;
|
||||||
};
|
};
|
||||||
|
@ -73,15 +81,15 @@ redef likely_server_ports += { ports };
|
||||||
|
|
||||||
event bro_init() &priority=5
|
event bro_init() &priority=5
|
||||||
{
|
{
|
||||||
Log::create_stream(SSH::LOG, [$columns=Info, $ev=log_ssh, $path="ssh"]);
|
|
||||||
Analyzer::register_for_ports(Analyzer::ANALYZER_SSH, ports);
|
Analyzer::register_for_ports(Analyzer::ANALYZER_SSH, ports);
|
||||||
|
Log::create_stream(SSH::LOG, [$columns=Info, $ev=log_ssh, $path="ssh"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_session(c: connection)
|
function set_session(c: connection)
|
||||||
{
|
{
|
||||||
if ( ! c?$ssh )
|
if ( ! c?$ssh )
|
||||||
{
|
{
|
||||||
local info: Info;
|
local info: SSH::Info;
|
||||||
info$ts = network_time();
|
info$ts = network_time();
|
||||||
info$uid = c$uid;
|
info$uid = c$uid;
|
||||||
info$id = c$id;
|
info$id = c$id;
|
||||||
|
@ -89,116 +97,135 @@ function set_session(c: connection)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function check_ssh_connection(c: connection, done: bool)
|
event ssh_server_version(c: connection, version: string)
|
||||||
{
|
|
||||||
# If already done watching this connection, just return.
|
|
||||||
if ( c$ssh$done )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( done )
|
|
||||||
{
|
|
||||||
# If this connection is done, then we can look to see if
|
|
||||||
# this matches the conditions for a failed login. Failed
|
|
||||||
# logins are only detected at connection state removal.
|
|
||||||
|
|
||||||
if ( # Require originators and responders to have sent at least 50 bytes.
|
|
||||||
c$orig$size > 50 && c$resp$size > 50 &&
|
|
||||||
# Responders must be below 4000 bytes.
|
|
||||||
c$resp$size < authentication_data_size &&
|
|
||||||
# Responder must have sent fewer than 40 packets.
|
|
||||||
c$resp$num_pkts < 40 &&
|
|
||||||
# If there was a content gap we can't reliably do this heuristic.
|
|
||||||
c?$conn && c$conn$missed_bytes == 0 )# &&
|
|
||||||
# Only "normal" connections can count.
|
|
||||||
#c$conn?$conn_state && c$conn$conn_state in valid_states )
|
|
||||||
{
|
|
||||||
c$ssh$status = "failure";
|
|
||||||
event SSH::heuristic_failed_login(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( c$resp$size >= authentication_data_size )
|
|
||||||
{
|
|
||||||
c$ssh$status = "success";
|
|
||||||
event SSH::heuristic_successful_login(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
# If this connection is still being tracked, then it's possible
|
|
||||||
# to watch for it to be a successful connection.
|
|
||||||
if ( c$resp$size >= authentication_data_size )
|
|
||||||
{
|
|
||||||
c$ssh$status = "success";
|
|
||||||
event SSH::heuristic_successful_login(c);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
# This connection must be tracked longer. Let the scheduled
|
|
||||||
# check happen again.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Set the direction for the log.
|
|
||||||
c$ssh$direction = Site::is_local_addr(c$id$orig_h) ? OUTBOUND : INBOUND;
|
|
||||||
|
|
||||||
# Set the "done" flag to prevent the watching event from rescheduling
|
|
||||||
# after detection is done.
|
|
||||||
c$ssh$done=T;
|
|
||||||
|
|
||||||
if ( skip_processing_after_detection )
|
|
||||||
{
|
|
||||||
# Stop watching this connection, we don't care about it anymore.
|
|
||||||
skip_further_processing(c$id);
|
|
||||||
set_record_packets(c$id, F);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
event heuristic_successful_login(c: connection) &priority=-5
|
|
||||||
{
|
|
||||||
Log::write(SSH::LOG, c$ssh);
|
|
||||||
}
|
|
||||||
|
|
||||||
event heuristic_failed_login(c: connection) &priority=-5
|
|
||||||
{
|
|
||||||
Log::write(SSH::LOG, c$ssh);
|
|
||||||
}
|
|
||||||
|
|
||||||
event connection_state_remove(c: connection) &priority=-5
|
|
||||||
{
|
|
||||||
if ( c?$ssh )
|
|
||||||
{
|
|
||||||
check_ssh_connection(c, T);
|
|
||||||
if ( c$ssh$status == "undetermined" )
|
|
||||||
Log::write(SSH::LOG, c$ssh);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
event ssh_watcher(c: connection)
|
|
||||||
{
|
|
||||||
local id = c$id;
|
|
||||||
# don't go any further if this connection is gone already!
|
|
||||||
if ( ! connection_exists(id) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
lookup_connection(c$id);
|
|
||||||
check_ssh_connection(c, F);
|
|
||||||
if ( ! c$ssh$done )
|
|
||||||
schedule +15secs { ssh_watcher(c) };
|
|
||||||
}
|
|
||||||
|
|
||||||
event ssh_server_version(c: connection, version: string) &priority=5
|
|
||||||
{
|
{
|
||||||
set_session(c);
|
set_session(c);
|
||||||
c$ssh$server = version;
|
c$ssh$server = version;
|
||||||
}
|
}
|
||||||
|
|
||||||
event ssh_client_version(c: connection, version: string) &priority=5
|
event ssh_client_version(c: connection, version: string)
|
||||||
{
|
{
|
||||||
set_session(c);
|
set_session(c);
|
||||||
c$ssh$client = version;
|
c$ssh$client = version;
|
||||||
|
|
||||||
# The heuristic detection for SSH relies on the ConnSize analyzer.
|
if ( ( |version| > 3 ) && ( version[4] == "1" ) )
|
||||||
# Don't do the heuristics if it's disabled.
|
c$ssh$version = 1;
|
||||||
if ( use_conn_size_analyzer )
|
if ( ( |version| > 3 ) && ( version[4] == "2" ) )
|
||||||
schedule +15secs { ssh_watcher(c) };
|
c$ssh$version = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
event ssh_auth_successful(c: connection, auth_method_none: bool)
|
||||||
|
{
|
||||||
|
# TODO - what to do here?
|
||||||
|
if ( !c?$ssh || ( c$ssh?$auth_success && c$ssh$auth_success ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
# We can't accurately tell for compressed streams
|
||||||
|
if ( c$ssh?$compression_alg && ( c$ssh$compression_alg in compression_algorithms ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
c$ssh$auth_success = T;
|
||||||
|
|
||||||
|
if ( skip_processing_after_detection)
|
||||||
|
{
|
||||||
|
skip_further_processing(c$id);
|
||||||
|
set_record_packets(c$id, F);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event ssh_auth_successful(c: connection, auth_method_none: bool) &priority=-5
|
||||||
|
{
|
||||||
|
if ( c?$ssh && !c$ssh$logged )
|
||||||
|
{
|
||||||
|
c$ssh$logged = T;
|
||||||
|
Log::write(SSH::LOG, c$ssh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event ssh_auth_failed(c: connection)
|
||||||
|
{
|
||||||
|
if ( !c?$ssh || ( c$ssh?$auth_success && !c$ssh$auth_success ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
# We can't accurately tell for compressed streams
|
||||||
|
if ( c$ssh?$compression_alg && ( c$ssh$compression_alg in compression_algorithms ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
c$ssh$auth_success = F;
|
||||||
|
c$ssh$num_failures += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine the negotiated algorithm
|
||||||
|
function find_alg(client_algorithms: vector of string, server_algorithms: vector of string): string
|
||||||
|
{
|
||||||
|
for ( i in client_algorithms )
|
||||||
|
for ( j in server_algorithms )
|
||||||
|
if ( client_algorithms[i] == server_algorithms[j] )
|
||||||
|
return client_algorithms[i];
|
||||||
|
return "Algorithm negotiation failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
# This is a simple wrapper around find_alg for cases where client to server and server to client
|
||||||
|
# negotiate different algorithms. This is rare, but provided for completeness.
|
||||||
|
function find_bidirectional_alg(client_prefs: Algorithm_Prefs, server_prefs: Algorithm_Prefs): string
|
||||||
|
{
|
||||||
|
local c_to_s = find_alg(client_prefs$client_to_server, server_prefs$client_to_server);
|
||||||
|
local s_to_c = find_alg(client_prefs$server_to_client, server_prefs$server_to_client);
|
||||||
|
|
||||||
|
# Usually these are the same, but if they're not, return the details
|
||||||
|
return c_to_s == s_to_c ? c_to_s : fmt("To server: %s, to client: %s", c_to_s, s_to_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
event ssh_capabilities(c: connection, cookie: string, capabilities: Capabilities)
|
||||||
|
{
|
||||||
|
if ( !c?$ssh || ( c$ssh?$capabilities && c$ssh$capabilities$is_server == capabilities$is_server ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( !c$ssh?$capabilities )
|
||||||
|
{
|
||||||
|
c$ssh$capabilities = capabilities;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
local client_caps = capabilities$is_server ? c$ssh$capabilities : capabilities;
|
||||||
|
local server_caps = capabilities$is_server ? capabilities : c$ssh$capabilities;
|
||||||
|
|
||||||
|
c$ssh$cipher_alg = find_bidirectional_alg(client_caps$encryption_algorithms,
|
||||||
|
server_caps$encryption_algorithms);
|
||||||
|
c$ssh$mac_alg = find_bidirectional_alg(client_caps$mac_algorithms,
|
||||||
|
server_caps$mac_algorithms);
|
||||||
|
c$ssh$compression_alg = find_bidirectional_alg(client_caps$compression_algorithms,
|
||||||
|
server_caps$compression_algorithms);
|
||||||
|
c$ssh$kex_alg = find_alg(client_caps$kex_algorithms, server_caps$kex_algorithms);
|
||||||
|
c$ssh$host_key_alg = find_alg(client_caps$server_host_key_algorithms,
|
||||||
|
server_caps$server_host_key_algorithms);
|
||||||
|
}
|
||||||
|
|
||||||
|
event connection_state_remove(c: connection) &priority=-5
|
||||||
|
{
|
||||||
|
if ( c?$ssh && !c$ssh$logged && c$ssh?$client && c$ssh?$server )
|
||||||
|
{
|
||||||
|
c$ssh$logged = T;
|
||||||
|
Log::write(SSH::LOG, c$ssh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generate_fingerprint(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, ":"), /:/, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
event ssh1_server_host_key(c: connection, p: string, e: string) &priority=5
|
||||||
|
{
|
||||||
|
generate_fingerprint(c, e + p);
|
||||||
|
}
|
||||||
|
|
||||||
|
event ssh2_server_host_key(c: connection, key: string) &priority=5
|
||||||
|
{
|
||||||
|
generate_fingerprint(c, key);
|
||||||
}
|
}
|
||||||
|
|
11
scripts/policy/frameworks/intel/seen/pubkey-hashes.bro
Normal file
11
scripts/policy/frameworks/intel/seen/pubkey-hashes.bro
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
@load base/frameworks/intel
|
||||||
|
@load ./where-locations
|
||||||
|
|
||||||
|
event ssh_server_host_key(c: connection, hash: string)
|
||||||
|
{
|
||||||
|
local seen = Intel::Seen($indicator=hash,
|
||||||
|
$indicator_type=Intel::PUBKEY_HASH,
|
||||||
|
$conn=c,
|
||||||
|
$where=SSH::IN_SERVER_HOST_KEY);
|
||||||
|
Intel::seen(seen);
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ export {
|
||||||
SMTP::IN_REPLY_TO,
|
SMTP::IN_REPLY_TO,
|
||||||
SMTP::IN_X_ORIGINATING_IP_HEADER,
|
SMTP::IN_X_ORIGINATING_IP_HEADER,
|
||||||
SMTP::IN_MESSAGE,
|
SMTP::IN_MESSAGE,
|
||||||
|
SSH::IN_SERVER_HOST_KEY,
|
||||||
SSL::IN_SERVER_NAME,
|
SSL::IN_SERVER_NAME,
|
||||||
SMTP::IN_HEADER,
|
SMTP::IN_HEADER,
|
||||||
X509::IN_CERT,
|
X509::IN_CERT,
|
||||||
|
|
|
@ -12,10 +12,10 @@ export {
|
||||||
redef enum Notice::Type += {
|
redef enum Notice::Type += {
|
||||||
## Indicates that a host has been identified as crossing the
|
## Indicates that a host has been identified as crossing the
|
||||||
## :bro:id:`SSH::password_guesses_limit` threshold with
|
## :bro:id:`SSH::password_guesses_limit` threshold with
|
||||||
## heuristically determined failed logins.
|
## failed logins.
|
||||||
Password_Guessing,
|
Password_Guessing,
|
||||||
## Indicates that a host previously identified as a "password
|
## Indicates that a host previously identified as a "password
|
||||||
## guesser" has now had a heuristically successful login
|
## guesser" has now had a successful login
|
||||||
## attempt. This is not currently implemented.
|
## attempt. This is not currently implemented.
|
||||||
Login_By_Password_Guesser,
|
Login_By_Password_Guesser,
|
||||||
};
|
};
|
||||||
|
@ -34,8 +34,7 @@ export {
|
||||||
const guessing_timeout = 30 mins &redef;
|
const guessing_timeout = 30 mins &redef;
|
||||||
|
|
||||||
## This value can be used to exclude hosts or entire networks from being
|
## This value can be used to exclude hosts or entire networks from being
|
||||||
## tracked as potential "guessers". There are cases where the success
|
## tracked as potential "guessers". The index represents
|
||||||
## heuristic fails and this acts as the whitelist. The index represents
|
|
||||||
## client subnets and the yield value represents server subnets.
|
## client subnets and the yield value represents server subnets.
|
||||||
const ignore_guessers: table[subnet] of subnet &redef;
|
const ignore_guessers: table[subnet] of subnet &redef;
|
||||||
}
|
}
|
||||||
|
@ -70,7 +69,7 @@ event bro_init()
|
||||||
}]);
|
}]);
|
||||||
}
|
}
|
||||||
|
|
||||||
event SSH::heuristic_successful_login(c: connection)
|
event SSH::ssh_auth_successful(c: connection, auth_method_none: bool)
|
||||||
{
|
{
|
||||||
local id = c$id;
|
local id = c$id;
|
||||||
|
|
||||||
|
@ -79,7 +78,7 @@ event SSH::heuristic_successful_login(c: connection)
|
||||||
$where=SSH::SUCCESSFUL_LOGIN]);
|
$where=SSH::SUCCESSFUL_LOGIN]);
|
||||||
}
|
}
|
||||||
|
|
||||||
event SSH::heuristic_failed_login(c: connection)
|
event SSH::ssh_auth_failed(c: connection)
|
||||||
{
|
{
|
||||||
local id = c$id;
|
local id = c$id;
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ function get_location(c: connection): geo_location
|
||||||
return lookup_location(lookup_ip);
|
return lookup_location(lookup_ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
event SSH::heuristic_successful_login(c: connection) &priority=5
|
event SSH::ssh_auth_successful(c: connection, auth_method_none: bool) &priority=3
|
||||||
{
|
{
|
||||||
# Add the location data to the SSH record.
|
# Add the location data to the SSH record.
|
||||||
c$ssh$remote_location = get_location(c);
|
c$ssh$remote_location = get_location(c);
|
||||||
|
@ -45,7 +45,7 @@ event SSH::heuristic_successful_login(c: connection) &priority=5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
event SSH::heuristic_failed_login(c: connection) &priority=5
|
event SSH::ssh_auth_failed(c: connection) &priority=3
|
||||||
{
|
{
|
||||||
# Add the location data to the SSH record.
|
# Add the location data to the SSH record.
|
||||||
c$ssh$remote_location = get_location(c);
|
c$ssh$remote_location = get_location(c);
|
||||||
|
|
|
@ -27,7 +27,7 @@ export {
|
||||||
/^ftp[0-9]*\./ &redef;
|
/^ftp[0-9]*\./ &redef;
|
||||||
}
|
}
|
||||||
|
|
||||||
event SSH::heuristic_successful_login(c: connection)
|
event SSH::ssh_auth_successful(c: connection, auth_method_none: bool)
|
||||||
{
|
{
|
||||||
for ( host in set(c$id$orig_h, c$id$resp_h) )
|
for ( host in set(c$id$orig_h, c$id$resp_h) )
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
@load frameworks/intel/seen/file-names.bro
|
@load frameworks/intel/seen/file-names.bro
|
||||||
@load frameworks/intel/seen/http-headers.bro
|
@load frameworks/intel/seen/http-headers.bro
|
||||||
@load frameworks/intel/seen/http-url.bro
|
@load frameworks/intel/seen/http-url.bro
|
||||||
|
@load frameworks/intel/seen/pubkey-hashes.bro
|
||||||
@load frameworks/intel/seen/smtp-url-extraction.bro
|
@load frameworks/intel/seen/smtp-url-extraction.bro
|
||||||
@load frameworks/intel/seen/smtp.bro
|
@load frameworks/intel/seen/smtp.bro
|
||||||
@load frameworks/intel/seen/ssl.bro
|
@load frameworks/intel/seen/ssl.bro
|
||||||
|
|
|
@ -5,5 +5,7 @@ include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DI
|
||||||
|
|
||||||
bro_plugin_begin(Bro SSH)
|
bro_plugin_begin(Bro SSH)
|
||||||
bro_plugin_cc(SSH.cc Plugin.cc)
|
bro_plugin_cc(SSH.cc Plugin.cc)
|
||||||
|
bro_plugin_bif(types.bif)
|
||||||
bro_plugin_bif(events.bif)
|
bro_plugin_bif(events.bif)
|
||||||
|
bro_plugin_pac(ssh.pac ssh-analyzer.pac ssh-protocol.pac)
|
||||||
bro_plugin_end()
|
bro_plugin_end()
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// See the file in the main distribution directory for copyright.
|
// See the file in the main distribution directory for copyright.
|
||||||
|
|
||||||
|
|
||||||
#include "plugin/Plugin.h"
|
#include "plugin/Plugin.h"
|
||||||
|
|
||||||
#include "SSH.h"
|
#include "SSH.h"
|
||||||
|
|
||||||
namespace plugin {
|
namespace plugin {
|
||||||
|
@ -12,14 +10,15 @@ class Plugin : public plugin::Plugin {
|
||||||
public:
|
public:
|
||||||
plugin::Configuration Configure()
|
plugin::Configuration Configure()
|
||||||
{
|
{
|
||||||
AddComponent(new ::analyzer::Component("SSH", ::analyzer::ssh::SSH_Analyzer::Instantiate));
|
AddComponent(new ::analyzer::Component("SSH", ::analyzer::SSH::SSH_Analyzer::Instantiate));
|
||||||
|
|
||||||
plugin::Configuration config;
|
plugin::Configuration config;
|
||||||
config.name = "Bro::SSH";
|
config.name = "Bro::SSH";
|
||||||
config.description = "SSH analyzer";
|
config.description = "Secure Shell analyzer";
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
} plugin;
|
} plugin;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,105 +1,148 @@
|
||||||
// See the file "COPYING" in the main distribution directory for copyright.
|
// See the file "COPYING" in the main distribution directory for copyright.
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
#include "NetVar.h"
|
|
||||||
#include "SSH.h"
|
#include "SSH.h"
|
||||||
#include "Event.h"
|
|
||||||
#include "analyzer/protocol/tcp/ContentLine.h"
|
|
||||||
|
|
||||||
|
#include "analyzer/protocol/tcp/TCP_Reassembler.h"
|
||||||
|
|
||||||
|
#include "Reporter.h"
|
||||||
|
|
||||||
|
#include "types.bif.h"
|
||||||
#include "events.bif.h"
|
#include "events.bif.h"
|
||||||
|
|
||||||
using namespace analyzer::ssh;
|
using namespace analyzer::SSH;
|
||||||
|
|
||||||
SSH_Analyzer::SSH_Analyzer(Connection* c)
|
SSH_Analyzer::SSH_Analyzer(Connection* c)
|
||||||
: tcp::TCP_ApplicationAnalyzer("SSH", c)
|
: tcp::TCP_ApplicationAnalyzer("SSH", c)
|
||||||
{
|
{
|
||||||
orig = new tcp::ContentLine_Analyzer(c, true);
|
interp = new binpac::SSH::SSH_Conn(this);
|
||||||
orig->SetSkipPartial(true);
|
had_gap = false;
|
||||||
orig->SetCRLFAsEOL(LF_as_EOL);
|
auth_decision_made = false;
|
||||||
AddSupportAnalyzer(orig);
|
skipped_banner = false;
|
||||||
|
service_accept_size = 0;
|
||||||
resp = new tcp::ContentLine_Analyzer(c, false);
|
userauth_failure_size = 0;
|
||||||
resp->SetSkipPartial(true);
|
|
||||||
resp->SetCRLFAsEOL(LF_as_EOL);
|
|
||||||
AddSupportAnalyzer(resp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SSH_Analyzer::DeliverStream(int length, const u_char* data, bool is_orig)
|
SSH_Analyzer::~SSH_Analyzer()
|
||||||
{
|
{
|
||||||
tcp::TCP_ApplicationAnalyzer::DeliverStream(length, data, is_orig);
|
delete interp;
|
||||||
|
}
|
||||||
|
|
||||||
// We're all done processing this endpoint - flag it as such,
|
void SSH_Analyzer::Done()
|
||||||
// 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);
|
|
||||||
|
|
||||||
if ( TCP() )
|
|
||||||
{
|
{
|
||||||
// Don't try to parse version if there has already been a gap.
|
tcp::TCP_ApplicationAnalyzer::Done();
|
||||||
tcp::TCP_Endpoint* endp = is_orig ? TCP()->Orig() : TCP()->Resp();
|
|
||||||
if ( endp->HadGap() )
|
interp->FlowEOF(true);
|
||||||
|
interp->FlowEOF(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ( interp->get_state(orig) == binpac::SSH::ENCRYPTED )
|
||||||
|
{
|
||||||
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* line = (const char*) data;
|
try
|
||||||
|
|
||||||
// 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");
|
interp->NewData(orig, data, data + len);
|
||||||
ProtocolViolation("malformed ssh identification", line, length);
|
}
|
||||||
|
catch ( const binpac::Exception& e )
|
||||||
|
{
|
||||||
|
ProtocolViolation(fmt("Binpac exception: %s", e.c_msg()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSH_Analyzer::Undelivered(uint64 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)
|
||||||
|
{
|
||||||
|
// We're interested in messages from the server for SSH2
|
||||||
|
if ( ! orig && (interp->get_version() == binpac::SSH::SSH2) )
|
||||||
|
{
|
||||||
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int i;
|
// If our user can authenticate via the "none" method, this
|
||||||
for ( i = 4; i < length && line[i] != '-'; ++i )
|
// 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
|
||||||
if ( TCP() )
|
// -16.
|
||||||
|
if ( ! userauth_failure_size && (len + 16 == service_accept_size) )
|
||||||
{
|
{
|
||||||
if ( length >= i )
|
auth_decision_made = true;
|
||||||
{
|
if ( ssh_auth_successful )
|
||||||
IPAddr dst;
|
BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), true);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate SSH events.
|
|
||||||
EventHandlerPtr event = is_orig ?
|
|
||||||
ssh_client_version : ssh_server_version;
|
|
||||||
if ( ! event )
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
val_list* vl = new val_list;
|
|
||||||
vl->append(BuildConnVal());
|
// Normally, this packet would be a SSH_MSG_USERAUTH_FAILURE
|
||||||
vl->append(new StringVal(length, line));
|
// message, with a variable length, depending on the
|
||||||
|
// authentication methods the server supports. If it's too
|
||||||
ConnectionEvent(event, vl);
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,25 +3,46 @@
|
||||||
#ifndef ANALYZER_PROTOCOL_SSH_SSH_H
|
#ifndef ANALYZER_PROTOCOL_SSH_SSH_H
|
||||||
#define ANALYZER_PROTOCOL_SSH_SSH_H
|
#define ANALYZER_PROTOCOL_SSH_SSH_H
|
||||||
|
|
||||||
|
#include "events.bif.h"
|
||||||
|
|
||||||
#include "analyzer/protocol/tcp/TCP.h"
|
#include "analyzer/protocol/tcp/TCP.h"
|
||||||
#include "analyzer/protocol/tcp/ContentLine.h"
|
#include "ssh_pac.h"
|
||||||
|
|
||||||
namespace analyzer { namespace ssh {
|
|
||||||
|
|
||||||
|
namespace analyzer {
|
||||||
|
namespace SSH {
|
||||||
class SSH_Analyzer : public tcp::TCP_ApplicationAnalyzer {
|
class SSH_Analyzer : public tcp::TCP_ApplicationAnalyzer {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SSH_Analyzer(Connection* conn);
|
SSH_Analyzer(Connection* conn);
|
||||||
|
virtual ~SSH_Analyzer();
|
||||||
|
|
||||||
|
// Overriden from Analyzer.
|
||||||
|
virtual void Done();
|
||||||
virtual void DeliverStream(int len, const u_char* data, bool orig);
|
virtual void DeliverStream(int len, const u_char* data, bool orig);
|
||||||
|
virtual void Undelivered(uint64 seq, int len, bool orig);
|
||||||
|
|
||||||
|
// Overriden from tcp::TCP_ApplicationAnalyzer.
|
||||||
|
virtual void EndpointEOF(bool is_orig);
|
||||||
|
|
||||||
static analyzer::Analyzer* Instantiate(Connection* conn)
|
static analyzer::Analyzer* Instantiate(Connection* conn)
|
||||||
{ return new SSH_Analyzer(conn); }
|
{ return new SSH_Analyzer(conn); }
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
tcp::ContentLine_Analyzer* orig;
|
binpac::SSH::SSH_Conn* interp;
|
||||||
tcp::ContentLine_Analyzer* resp;
|
|
||||||
|
void ProcessEncrypted(int len, bool orig);
|
||||||
|
|
||||||
|
bool had_gap;
|
||||||
|
|
||||||
|
// Packet analysis stuff
|
||||||
|
bool auth_decision_made;
|
||||||
|
bool skipped_banner;
|
||||||
|
|
||||||
|
int service_accept_size;
|
||||||
|
int userauth_failure_size;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} } // namespace analyzer::*
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
126
src/analyzer/protocol/ssh/consts.pac
Normal file
126
src/analyzer/protocol/ssh/consts.pac
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
enum version {
|
||||||
|
SSH1 = 1,
|
||||||
|
SSH2 = 2,
|
||||||
|
UNK = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum state {
|
||||||
|
VERSION_EXCHANGE = 0,
|
||||||
|
KEX_INIT = 1,
|
||||||
|
KEX_DH_GEX = 2,
|
||||||
|
KEX_DH = 3,
|
||||||
|
KEX_ECC = 4,
|
||||||
|
KEX_GSS = 5,
|
||||||
|
KEX_RSA = 6,
|
||||||
|
ENCRYPTED = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
# diffie-hellman-group1-sha1 [RFC4253] Section 8.1
|
||||||
|
# diffie-hellman-group14-sha1 [RFC4253] Section 8.2
|
||||||
|
enum KEX_DH_message_id {
|
||||||
|
SSH_MSG_KEXDH_INIT = 30,
|
||||||
|
SSH_MSG_KEXDH_REPLY = 31,
|
||||||
|
};
|
||||||
|
|
||||||
|
# diffie-hellman-group-exchange-sha1 [RFC4419] Section 4.1
|
||||||
|
# diffie-hellman-group-exchange-sha256 [RFC4419] Section 4.2
|
||||||
|
enum KEX_DH_GEX_message_id {
|
||||||
|
SSH_MSG_KEX_DH_GEX_REQUEST_OLD = 30,
|
||||||
|
SSH_MSG_KEX_DH_GEX_GROUP = 31,
|
||||||
|
SSH_MSG_KEX_DH_GEX_INIT = 32,
|
||||||
|
SSH_MSG_KEX_DH_GEX_REPLY = 33,
|
||||||
|
SSH_MSG_KEX_DH_GEX_REQUEST = 34,
|
||||||
|
};
|
||||||
|
|
||||||
|
# rsa1024-sha1 [RFC4432]
|
||||||
|
# rsa2048-sha256 [RFC4432]
|
||||||
|
enum KEX_RSA_message_id {
|
||||||
|
SSH_MSG_KEXRSA_PUBKEY = 30,
|
||||||
|
SSH_MSG_KEXRSA_SECRET = 31,
|
||||||
|
SSH_MSG_KEXRSA_DONE = 32,
|
||||||
|
};
|
||||||
|
|
||||||
|
# gss-group1-sha1-* [RFC4462] Section 2.3
|
||||||
|
# gss-group14-sha1-* [RFC4462] Section 2.4
|
||||||
|
# gss-gex-sha1-* [RFC4462] Section 2.5
|
||||||
|
# gss-* [RFC4462] Section 2.6
|
||||||
|
enum KEX_GSS_message_id {
|
||||||
|
SSH_MSG_KEXGSS_INIT = 30,
|
||||||
|
SSH_MSG_KEXGSS_CONTINUE = 31,
|
||||||
|
SSH_MSG_KEXGSS_COMPLETE = 32,
|
||||||
|
SSH_MSG_KEXGSS_HOSTKEY = 33,
|
||||||
|
SSH_MSG_KEXGSS_ERROR = 34,
|
||||||
|
SSH_MSG_KEXGSS_GROUPREQ = 40,
|
||||||
|
SSH_MSG_KEXGSS_GROUP = 41,
|
||||||
|
};
|
||||||
|
|
||||||
|
# ecdh-sha2-* [RFC5656]
|
||||||
|
enum KEX_ECDH_message_id {
|
||||||
|
SSH_MSG_KEX_ECDH_INIT = 30,
|
||||||
|
SSH_MSG_KEX_ECDH_REPLY = 31,
|
||||||
|
};
|
||||||
|
|
||||||
|
# ecmqv-sha2 [RFC5656]
|
||||||
|
enum KEX_ECMQV_message_id {
|
||||||
|
SSH_MSG_ECMQV_INIT = 30,
|
||||||
|
SSH_MSG_ECMQV_REPLY = 31,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ssh1_message_id {
|
||||||
|
SSH_MSG_NONE = 0,
|
||||||
|
SSH_MSG_DISCONNECT = 1,
|
||||||
|
SSH_SMSG_PUBLIC_KEY = 2,
|
||||||
|
SSH_CMSG_SESSION_KEY = 3,
|
||||||
|
SSH_CMSG_USER = 4,
|
||||||
|
SSH_CMSG_AUTH_RHOSTS = 5,
|
||||||
|
SSH_CMSG_AUTH_RSA = 6,
|
||||||
|
SSH_SMSG_AUTH_RSA_CHALLENGE = 7,
|
||||||
|
SSH_CMSG_AUTH_RSA_RESPONSE = 8,
|
||||||
|
SSH_CMSG_AUTH_PASSWORD = 9,
|
||||||
|
SSH_CMSG_REQUEST_PTY = 10,
|
||||||
|
SSH_CMSG_WINDOW_SIZE = 11,
|
||||||
|
SSH_CMSG_EXEC_SHELL = 12,
|
||||||
|
SSH_CMSG_EXEC_CMD = 13,
|
||||||
|
SSH_SMSG_SUCCESS = 14,
|
||||||
|
SSH_SMSG_FAILURE = 15,
|
||||||
|
SSH_CMSG_STDIN_DATA = 16,
|
||||||
|
SSH_SMSG_STDOUT_DATA = 17,
|
||||||
|
SSH_SMSG_STDERR_DATA = 18,
|
||||||
|
SSH_CMSG_EOF = 19,
|
||||||
|
SSH_SMSG_EXITSTATUS = 20,
|
||||||
|
SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 21,
|
||||||
|
SSH_MSG_CHANNEL_OPEN_FAILURE = 22,
|
||||||
|
SSH_MSG_CHANNEL_DATA = 23,
|
||||||
|
SSH_MSG_CHANNEL_CLOSE = 24,
|
||||||
|
SSH_MSG_CHANNEL_CLOSE_CONFIRMATION = 25,
|
||||||
|
SSH_CMSG_X11_REQUEST_FORWARDING_OLD = 26,
|
||||||
|
SSH_SMSG_X11_OPEN = 27,
|
||||||
|
SSH_CMSG_PORT_FORWARD_REQUEST = 28,
|
||||||
|
SSH_MSG_PORT_OPEN = 29,
|
||||||
|
SSH_CMSG_AGENT_REQUEST_FORWARDING = 30,
|
||||||
|
SSH_SMSG_AGENT_OPEN = 31,
|
||||||
|
SSH_MSG_IGNORE = 32,
|
||||||
|
SSH_CMSG_EXIT_CONFIRMATION = 33,
|
||||||
|
SSH_CMSG_X11_REQUEST_FORWARDING = 34,
|
||||||
|
SSH_CMSG_AUTH_RHOSTS_RSA = 35,
|
||||||
|
SSH_MSG_DEBUG = 36,
|
||||||
|
SSH_CMSG_REQUEST_COMPRESSION = 37,
|
||||||
|
SSH_CMSG_MAX_PACKET_SIZE = 38,
|
||||||
|
SSH_CMSG_AUTH_TIS = 39,
|
||||||
|
SSH_SMSG_AUTH_TIS_CHALLENGE = 40,
|
||||||
|
SSH_CMSG_AUTH_TIS_RESPONSE = 41,
|
||||||
|
SSH_CMSG_AUTH_KERBEROS = 42,
|
||||||
|
SSH_SMSG_AUTH_KERBEROS_RESPONSE = 43,
|
||||||
|
SSH_CMSG_HAVE_KERBEROS_TGT = 44,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ssh2_message_id {
|
||||||
|
MSG_DISCONNECT = 1,
|
||||||
|
MSG_IGNORE = 2,
|
||||||
|
MSG_UNIMPLEMENTED = 3,
|
||||||
|
MSG_DEBUG = 4,
|
||||||
|
MSG_SERVICE_REQUEST = 5,
|
||||||
|
MSG_SERVICE_ACCEPT = 6,
|
||||||
|
MSG_KEXINIT = 20,
|
||||||
|
MSG_NEWKEYS = 21,
|
||||||
|
};
|
|
@ -1,38 +1,194 @@
|
||||||
## Generated when seeing an SSH client's version identification. The SSH
|
## An :abbr:`SSH (Secure Shell)` Protocol Version Exchange message
|
||||||
## protocol starts with a clear-text handshake message that reports client and
|
## from the server. This contains an identification string that's used
|
||||||
## server protocol/software versions. This event provides access to what the
|
## for version identification. See :rfc:`4253#section-4.2` for
|
||||||
## client sent.
|
## details.
|
||||||
##
|
##
|
||||||
|
## c: The connection over which the message was sent.
|
||||||
##
|
##
|
||||||
## See `Wikipedia <http://en.wikipedia.org/wiki/Secure_Shell>`__ for more
|
## version: The identification string
|
||||||
## information about the SSH protocol.
|
|
||||||
##
|
##
|
||||||
## c: The connection.
|
## .. bro:see:: ssh_client_version ssh_auth_successful ssh_auth_failed
|
||||||
##
|
## ssh_capabilities ssh2_server_host_key ssh1_server_host_key
|
||||||
## version: The version string the client sent (e.g., `SSH-2.0-libssh-0.11`).
|
## ssh_encrypted_packet ssh2_dh_server_params
|
||||||
##
|
## ssh2_gss_error ssh2_ecc_key
|
||||||
## .. bro:see:: ssh_server_version
|
|
||||||
##
|
|
||||||
## .. note:: As everything after the initial version handshake proceeds
|
|
||||||
## encrypted, Bro cannot further analyze SSH sessions.
|
|
||||||
event ssh_client_version%(c: connection, version: string%);
|
|
||||||
|
|
||||||
## Generated when seeing an SSH server's version identification. The SSH
|
|
||||||
## protocol starts with a clear-text handshake message that reports client and
|
|
||||||
## server protocol/software versions. This event provides access to what the
|
|
||||||
## server sent.
|
|
||||||
##
|
|
||||||
## See `Wikipedia <http://en.wikipedia.org/wiki/Secure_Shell>`__ for more
|
|
||||||
## information about the SSH protocol.
|
|
||||||
##
|
|
||||||
## c: The connection.
|
|
||||||
##
|
|
||||||
## version: The version string the server sent (e.g.,
|
|
||||||
## ``SSH-1.99-OpenSSH_3.9p1``).
|
|
||||||
##
|
|
||||||
## .. bro:see:: ssh_client_version
|
|
||||||
##
|
|
||||||
## .. note:: As everything coming after the initial version handshake proceeds
|
|
||||||
## encrypted, Bro cannot further analyze SSH sessions.
|
|
||||||
event ssh_server_version%(c: connection, version: string%);
|
event ssh_server_version%(c: connection, version: string%);
|
||||||
|
|
||||||
|
## An :abbr:`SSH (Secure Shell)` Protocol Version Exchange message
|
||||||
|
## from the client. This contains an identification string that's used
|
||||||
|
## for version identification. See :rfc:`4253#section-4.2` for
|
||||||
|
## details.
|
||||||
|
##
|
||||||
|
## c: The connection over which the message was sent.
|
||||||
|
##
|
||||||
|
## version: The identification string
|
||||||
|
##
|
||||||
|
## .. bro:see:: ssh_server_version ssh_auth_successful ssh_auth_failed
|
||||||
|
## 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_client_version%(c: connection, version: string%);
|
||||||
|
|
||||||
|
## This event is generated when an :abbr:`SSH (Secure Shell)`
|
||||||
|
## connection was determined to have had a successful
|
||||||
|
## authentication. This determination is based on packet size
|
||||||
|
## analysis, and errs on the side of caution - that is, if there's any
|
||||||
|
## doubt about the authentication success, this event is *not* raised.
|
||||||
|
##
|
||||||
|
## c: The connection over which the :abbr:`SSH (Secure Shell)`
|
||||||
|
## connection took place.
|
||||||
|
##
|
||||||
|
## auth_method_none: This is true if the analyzer detected a
|
||||||
|
## successful connection before any authentication challenge. The
|
||||||
|
## :abbr:`SSH (Secure Shell)` protocol provides a mechanism for
|
||||||
|
## unauthenticated access, which some servers support.
|
||||||
|
##
|
||||||
|
## .. bro:see:: ssh_server_version ssh_client_version ssh_auth_failed
|
||||||
|
## 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_auth_successful%(c: connection, auth_method_none: bool%);
|
||||||
|
|
||||||
|
## This event is generated when an :abbr:`SSH (Secure Shell)`
|
||||||
|
## connection was determined to have had a failed authentication. This
|
||||||
|
## determination is based on packet size analysis, and errs on the
|
||||||
|
## side of caution - that is, if there's any doubt about the
|
||||||
|
## authentication failure, this event is *not* raised.
|
||||||
|
##
|
||||||
|
## c: The connection over which the :abbr:`SSH (Secure Shell)`
|
||||||
|
## connection took place.
|
||||||
|
##
|
||||||
|
## .. bro:see:: ssh_server_version ssh_client_version
|
||||||
|
## ssh_auth_successful 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_auth_failed%(c: connection%);
|
||||||
|
|
||||||
|
## During the initial :abbr:`SSH (Secure Shell)` key exchange, each
|
||||||
|
## endpoint lists the algorithms that it supports, in order of
|
||||||
|
## preference. This event is generated for each endpoint, when the
|
||||||
|
## SSH_MSG_KEXINIT message is seen. See :rfc:`4253#section-7.1` for
|
||||||
|
## details.
|
||||||
|
##
|
||||||
|
## c: The connection over which the :abbr:`SSH (Secure Shell)`
|
||||||
|
## connection took place.
|
||||||
|
##
|
||||||
|
## cookie: The SSH_MSG_KEXINIT cookie - a random value generated by
|
||||||
|
## the sender.
|
||||||
|
##
|
||||||
|
## capabilities: The list of algorithms and languages that the sender
|
||||||
|
## advertises support for, in order of preference.
|
||||||
|
##
|
||||||
|
## .. bro:see:: ssh_server_version ssh_client_version
|
||||||
|
## ssh_auth_successful ssh_auth_failed ssh2_server_host_key
|
||||||
|
## ssh1_server_host_key ssh_encrypted_packet ssh2_dh_server_params
|
||||||
|
## ssh2_gss_error ssh2_ecc_key
|
||||||
|
event ssh_capabilities%(c: connection, cookie: string, capabilities: SSH::Capabilities%);
|
||||||
|
|
||||||
|
## 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 SSH2.
|
||||||
|
##
|
||||||
|
## c: The connection over which the :abbr:`SSH (Secure Shell)`
|
||||||
|
## connection took place.
|
||||||
|
##
|
||||||
|
## key: The server's public host key. Note that this is the public key
|
||||||
|
## itself, and not just the fingerprint or hash.
|
||||||
|
##
|
||||||
|
## .. bro:see:: ssh_server_version ssh_client_version
|
||||||
|
## ssh_auth_successful ssh_auth_failed ssh_capabilities
|
||||||
|
## ssh1_server_host_key ssh_encrypted_packet ssh2_dh_server_params
|
||||||
|
## ssh2_gss_error ssh2_ecc_key
|
||||||
|
event ssh2_server_host_key%(c: connection, key: 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.
|
||||||
|
##
|
||||||
|
## c: The connection over which the :abbr:`SSH (Secure Shell)`
|
||||||
|
## connection took place.
|
||||||
|
##
|
||||||
|
## p: The prime for the server's public host key.
|
||||||
|
##
|
||||||
|
## e: The exponent for the serer's public host key.
|
||||||
|
##
|
||||||
|
## .. bro:see:: ssh_server_version ssh_client_version
|
||||||
|
## ssh_auth_successful ssh_auth_failed ssh_capabilities
|
||||||
|
## ssh2_server_host_key ssh_encrypted_packet ssh2_dh_server_params
|
||||||
|
## ssh2_gss_error ssh2_ecc_key
|
||||||
|
event ssh1_server_host_key%(c: connection, p: string, e: 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
|
||||||
|
## :bro:id:`SSH::skip_processing_after_detection` to false to use this
|
||||||
|
## event. This carries a performance penalty.
|
||||||
|
##
|
||||||
|
## c: The connection over which the :abbr:`SSH (Secure Shell)`
|
||||||
|
## connection took place.
|
||||||
|
##
|
||||||
|
## orig: Whether the packet was sent by the originator of the TCP
|
||||||
|
## connection.
|
||||||
|
##
|
||||||
|
## len: The length of the :abbr:`SSH (Secure Shell)` payload, in
|
||||||
|
## bytes. Note that this ignores reassembly, as this is unknown.
|
||||||
|
##
|
||||||
|
## .. bro:see:: ssh_server_version ssh_client_version
|
||||||
|
## ssh_auth_successful ssh_auth_failed ssh_capabilities
|
||||||
|
## ssh2_server_host_key ssh1_server_host_key ssh2_dh_server_params
|
||||||
|
## ssh2_gss_error ssh2_ecc_key
|
||||||
|
event ssh_encrypted_packet%(c: connection, orig: bool, len: count%);
|
||||||
|
|
||||||
|
## Generated if the connection uses a Diffie-Hellman Group Exchange
|
||||||
|
## key exchange method. This event contains the server DH parameters,
|
||||||
|
## which are sent in the SSH_MSG_KEY_DH_GEX_GROUP message as defined in
|
||||||
|
## :rfc:`4419#section-3`.
|
||||||
|
##
|
||||||
|
## c: The connection.
|
||||||
|
##
|
||||||
|
## p: The DH prime modulus.
|
||||||
|
##
|
||||||
|
## q: The DH generator.
|
||||||
|
##
|
||||||
|
## .. bro:see:: ssl_dh_server_params ssh_server_version
|
||||||
|
## ssh_client_version ssh_auth_successful ssh_auth_failed
|
||||||
|
## ssh_capabilities ssh2_server_host_key ssh1_server_host_key
|
||||||
|
## ssh_encrypted_packet ssh2_gss_error ssh2_ecc_key
|
||||||
|
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
|
||||||
|
## send an error message with some additional details. This event is
|
||||||
|
## generated when such an error message is seen. For more information,
|
||||||
|
## see :rfc:`4462#section-2.1`.
|
||||||
|
##
|
||||||
|
## c: The connection.
|
||||||
|
##
|
||||||
|
## major_status: GSS-API major status code.
|
||||||
|
##
|
||||||
|
## minor_status: GSS-API minor status code.
|
||||||
|
##
|
||||||
|
## err_msg: Detailed human-readable error message
|
||||||
|
##
|
||||||
|
## .. bro:see:: ssh_server_version ssh_client_version
|
||||||
|
## ssh_auth_successful ssh_auth_failed ssh_capabilities
|
||||||
|
## ssh2_server_host_key ssh1_server_host_key ssh_encrypted_packet
|
||||||
|
## ssh2_dh_server_params ssh2_ecc_key
|
||||||
|
event ssh2_gss_error%(c: connection, major_status: count, minor_status: count, err_msg: 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 client's or
|
||||||
|
## server's ephemeral public key is seen. For more information, see:
|
||||||
|
## :rfc:`5656#section-4`.
|
||||||
|
##
|
||||||
|
## c: The connection
|
||||||
|
##
|
||||||
|
## is_orig: Did this message come from the originator?
|
||||||
|
##
|
||||||
|
## q: The ephemeral public key
|
||||||
|
##
|
||||||
|
## .. bro:see:: ssh_server_version ssh_client_version
|
||||||
|
## ssh_auth_successful ssh_auth_failed ssh_capabilities
|
||||||
|
## ssh2_server_host_key ssh1_server_host_key ssh_encrypted_packet
|
||||||
|
## ssh2_dh_server_params ssh2_gss_error
|
||||||
|
event ssh2_ecc_key%(c: connection, is_orig: bool, q: string%);
|
||||||
|
|
221
src/analyzer/protocol/ssh/ssh-analyzer.pac
Normal file
221
src/analyzer/protocol/ssh/ssh-analyzer.pac
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
%extern{
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
%}
|
||||||
|
|
||||||
|
%header{
|
||||||
|
VectorVal* name_list_to_vector(const bytestring nl);
|
||||||
|
%}
|
||||||
|
|
||||||
|
%code{
|
||||||
|
// Copied from IRC_Analyzer::SplitWords
|
||||||
|
VectorVal* name_list_to_vector(const bytestring nl)
|
||||||
|
{
|
||||||
|
VectorVal* vv = new VectorVal(internal_type("string_vec")->AsVectorType());
|
||||||
|
|
||||||
|
string name_list = std_str(nl);
|
||||||
|
if ( name_list.size() < 1 )
|
||||||
|
return vv;
|
||||||
|
|
||||||
|
unsigned int start = 0;
|
||||||
|
unsigned int split_pos = 0;
|
||||||
|
|
||||||
|
while ( name_list[start] == ',' )
|
||||||
|
{
|
||||||
|
++start;
|
||||||
|
++split_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
string word;
|
||||||
|
while ( (split_pos = name_list.find(',', start)) < name_list.size() )
|
||||||
|
{
|
||||||
|
word = name_list.substr(start, split_pos - start);
|
||||||
|
if ( word.size() > 0 && word[0] != ',' )
|
||||||
|
vv->Assign(vv->Size(), new StringVal(word));
|
||||||
|
|
||||||
|
start = split_pos + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add line end if needed.
|
||||||
|
if ( start < name_list.size() )
|
||||||
|
{
|
||||||
|
word = name_list.substr(start, name_list.size() - start);
|
||||||
|
vv->Assign(vv->Size(), new StringVal(word));
|
||||||
|
}
|
||||||
|
return vv;
|
||||||
|
}
|
||||||
|
%}
|
||||||
|
|
||||||
|
refine flow SSH_Flow += {
|
||||||
|
function proc_ssh_version(msg: SSH_Version): bool
|
||||||
|
%{
|
||||||
|
if ( ssh_client_version && ${msg.is_orig } )
|
||||||
|
{
|
||||||
|
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_ssh2_kexinit(msg: SSH2_KEXINIT): bool
|
||||||
|
%{
|
||||||
|
if ( ! ssh_capabilities )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RecordVal* result = new RecordVal(BifType::Record::SSH::Capabilities);
|
||||||
|
result->Assign(0, name_list_to_vector(${msg.kex_algorithms.val}));
|
||||||
|
result->Assign(1, name_list_to_vector(${msg.server_host_key_algorithms.val}));
|
||||||
|
|
||||||
|
RecordVal* encryption_algs = new RecordVal(BifType::Record::SSH::Algorithm_Prefs);
|
||||||
|
encryption_algs->Assign(0, name_list_to_vector(${msg.encryption_algorithms_client_to_server.val}));
|
||||||
|
encryption_algs->Assign(1, name_list_to_vector(${msg.encryption_algorithms_server_to_client.val}));
|
||||||
|
result->Assign(2, encryption_algs);
|
||||||
|
|
||||||
|
RecordVal* mac_algs = new RecordVal(BifType::Record::SSH::Algorithm_Prefs);
|
||||||
|
mac_algs->Assign(0, name_list_to_vector(${msg.mac_algorithms_client_to_server.val}));
|
||||||
|
mac_algs->Assign(1, name_list_to_vector(${msg.mac_algorithms_server_to_client.val}));
|
||||||
|
result->Assign(3, mac_algs);
|
||||||
|
|
||||||
|
RecordVal* compression_algs = new RecordVal(BifType::Record::SSH::Algorithm_Prefs);
|
||||||
|
compression_algs->Assign(0, name_list_to_vector(${msg.compression_algorithms_client_to_server.val}));
|
||||||
|
compression_algs->Assign(1, name_list_to_vector(${msg.compression_algorithms_server_to_client.val}));
|
||||||
|
result->Assign(4, compression_algs);
|
||||||
|
|
||||||
|
if ( ${msg.languages_client_to_server.len} || ${msg.languages_server_to_client.len} )
|
||||||
|
{
|
||||||
|
RecordVal* languages = new RecordVal(BifType::Record::SSH::Algorithm_Prefs);
|
||||||
|
if ( ${msg.languages_client_to_server.len} )
|
||||||
|
languages->Assign(0, name_list_to_vector(${msg.languages_client_to_server.val}));
|
||||||
|
if ( ${msg.languages_server_to_client.len} )
|
||||||
|
languages->Assign(1, name_list_to_vector(${msg.languages_server_to_client.val}));
|
||||||
|
|
||||||
|
result->Assign(5, languages);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result->Assign(6, new Val(${msg.is_orig}, TYPE_BOOL));
|
||||||
|
|
||||||
|
BifEvent::generate_ssh_capabilities(connection()->bro_analyzer(),
|
||||||
|
connection()->bro_analyzer()->Conn(), bytestring_to_val(${msg.cookie}),
|
||||||
|
result);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
|
||||||
|
function proc_ssh2_dh_gex_group(msg: SSH2_DH_GEX_GROUP): bool
|
||||||
|
%{
|
||||||
|
if ( ssh2_dh_server_params )
|
||||||
|
{
|
||||||
|
BifEvent::generate_ssh2_dh_server_params(connection()->bro_analyzer(),
|
||||||
|
connection()->bro_analyzer()->Conn(),
|
||||||
|
bytestring_to_val(${msg.p.val}), bytestring_to_val(${msg.g.val}));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function proc_ssh2_ecc_key(q: bytestring, is_orig: bool): bool
|
||||||
|
%{
|
||||||
|
if ( ssh2_ecc_key )
|
||||||
|
{
|
||||||
|
BifEvent::generate_ssh2_ecc_key(connection()->bro_analyzer(),
|
||||||
|
connection()->bro_analyzer()->Conn(),
|
||||||
|
is_orig, bytestring_to_val(q));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function proc_ssh2_gss_error(msg: SSH2_GSS_ERROR): bool
|
||||||
|
%{
|
||||||
|
if ( ssh2_gss_error )
|
||||||
|
{
|
||||||
|
BifEvent::generate_ssh2_gss_error(connection()->bro_analyzer(),
|
||||||
|
connection()->bro_analyzer()->Conn(),
|
||||||
|
${msg.major_status}, ${msg.minor_status},
|
||||||
|
bytestring_to_val(${msg.message.val}));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function proc_ssh2_server_host_key(key: bytestring): bool
|
||||||
|
%{
|
||||||
|
if ( ssh2_server_host_key )
|
||||||
|
{
|
||||||
|
BifEvent::generate_ssh2_server_host_key(connection()->bro_analyzer(),
|
||||||
|
connection()->bro_analyzer()->Conn(),
|
||||||
|
bytestring_to_val(${key}));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function proc_ssh1_server_host_key(p: bytestring, e: bytestring): bool
|
||||||
|
%{
|
||||||
|
if ( ssh1_server_host_key )
|
||||||
|
{
|
||||||
|
BifEvent::generate_ssh1_server_host_key(connection()->bro_analyzer(),
|
||||||
|
connection()->bro_analyzer()->Conn(),
|
||||||
|
bytestring_to_val(${p}),
|
||||||
|
bytestring_to_val(${e}));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function proc_newkeys(): bool
|
||||||
|
%{
|
||||||
|
connection()->bro_analyzer()->ProtocolConfirmation();
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
};
|
||||||
|
|
||||||
|
refine typeattr SSH_Version += &let {
|
||||||
|
proc: bool = $context.flow.proc_ssh_version(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
refine typeattr SSH2_KEXINIT += &let {
|
||||||
|
proc: bool = $context.flow.proc_ssh2_kexinit(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
refine typeattr SSH1_Message += &let {
|
||||||
|
proc_newkeys: bool = $context.flow.proc_newkeys() &if(msg_type == SSH_CMSG_SESSION_KEY);
|
||||||
|
};
|
||||||
|
|
||||||
|
refine typeattr SSH2_Message += &let {
|
||||||
|
proc_newkeys: bool = $context.flow.proc_newkeys() &if(msg_type == MSG_NEWKEYS);
|
||||||
|
};
|
||||||
|
|
||||||
|
refine typeattr SSH2_DH_GEX_REPLY += &let {
|
||||||
|
proc: bool = $context.flow.proc_ssh2_server_host_key(k_s.val);
|
||||||
|
};
|
||||||
|
|
||||||
|
refine typeattr SSH2_GSS_HOSTKEY += &let {
|
||||||
|
proc: bool = $context.flow.proc_ssh2_server_host_key(k_s.val);
|
||||||
|
};
|
||||||
|
|
||||||
|
refine typeattr SSH2_GSS_ERROR += &let {
|
||||||
|
proc: bool = $context.flow.proc_ssh2_gss_error(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
refine typeattr SSH2_DH_GEX_GROUP += &let {
|
||||||
|
proc: bool = $context.flow.proc_ssh2_dh_gex_group(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
refine typeattr SSH2_ECC_REPLY += &let {
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
refine typeattr SSH2_ECC_INIT += &let {
|
||||||
|
proc: bool = $context.flow.proc_ssh2_ecc_key(q_c.val, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
refine typeattr SSH1_PUBLIC_KEY += &let {
|
||||||
|
proc: bool = $context.flow.proc_ssh1_server_host_key(host_key_p.val, host_key_e.val);
|
||||||
|
};
|
430
src/analyzer/protocol/ssh/ssh-protocol.pac
Normal file
430
src/analyzer/protocol/ssh/ssh-protocol.pac
Normal file
|
@ -0,0 +1,430 @@
|
||||||
|
%include consts.pac
|
||||||
|
|
||||||
|
# Common constructs across SSH1 and SSH2
|
||||||
|
########################################
|
||||||
|
|
||||||
|
# We have 3 basic types of messages:
|
||||||
|
#
|
||||||
|
# - SSH_Version messages just have a string with the banner string of the client or server
|
||||||
|
# - Encrypted messages have no usable data, so we'll just ignore them as best we can.
|
||||||
|
# - Finally, key exchange messages have a common format.
|
||||||
|
|
||||||
|
type SSH_PDU(is_orig: bool) = case $context.connection.get_state(is_orig) of {
|
||||||
|
VERSION_EXCHANGE -> version : SSH_Version(is_orig);
|
||||||
|
ENCRYPTED -> encrypted : bytestring &length=1 &transient;
|
||||||
|
default -> kex : SSH_Key_Exchange(is_orig);
|
||||||
|
} &byteorder=bigendian;
|
||||||
|
|
||||||
|
type SSH_Version(is_orig: bool) = record {
|
||||||
|
version : bytestring &oneline;
|
||||||
|
} &let {
|
||||||
|
update_state : bool = $context.connection.update_state(KEX_INIT, is_orig);
|
||||||
|
update_version : bool = $context.connection.update_version(version, is_orig);
|
||||||
|
};
|
||||||
|
|
||||||
|
type SSH_Key_Exchange(is_orig: bool) = case $context.connection.get_version() of {
|
||||||
|
SSH1 -> ssh1_msg : SSH1_Key_Exchange(is_orig);
|
||||||
|
SSH2 -> ssh2_msg : SSH2_Key_Exchange(is_orig);
|
||||||
|
};
|
||||||
|
|
||||||
|
# SSH1 constructs
|
||||||
|
#################
|
||||||
|
|
||||||
|
type SSH1_Key_Exchange(is_orig: bool) = record {
|
||||||
|
packet_length : uint32;
|
||||||
|
pad_fill : bytestring &length = 8 - (packet_length % 8);
|
||||||
|
msg_type : uint8;
|
||||||
|
message : SSH1_Message(is_orig, msg_type, packet_length - 5);
|
||||||
|
crc : uint32;
|
||||||
|
} &length = packet_length + 4 + 8 - (packet_length % 8);
|
||||||
|
|
||||||
|
type SSH1_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of {
|
||||||
|
SSH_SMSG_PUBLIC_KEY -> public_key : SSH1_PUBLIC_KEY(length);
|
||||||
|
SSH_CMSG_SESSION_KEY -> session_key : SSH1_SESSION_KEY(length);
|
||||||
|
} &let {
|
||||||
|
detach : bool=$context.connection.update_state(ENCRYPTED, is_orig);
|
||||||
|
};
|
||||||
|
|
||||||
|
type SSH1_PUBLIC_KEY(length: uint32) = record {
|
||||||
|
cookie : bytestring &length=8;
|
||||||
|
server_key : uint32;
|
||||||
|
server_key_p : ssh1_mp_int;
|
||||||
|
server_key_e : ssh1_mp_int;
|
||||||
|
host_key : uint32;
|
||||||
|
host_key_p : ssh1_mp_int;
|
||||||
|
host_key_e : ssh1_mp_int;
|
||||||
|
flags : uint32;
|
||||||
|
supported_ciphers : uint32;
|
||||||
|
supported_auths : uint32;
|
||||||
|
} &length=length;
|
||||||
|
|
||||||
|
type SSH1_SESSION_KEY(length: uint32) = record {
|
||||||
|
cipher : uint8;
|
||||||
|
cookie : bytestring &length=8;
|
||||||
|
session_key : ssh1_mp_int;
|
||||||
|
flags : uint32;
|
||||||
|
} &length=length;
|
||||||
|
|
||||||
|
type ssh1_mp_int = record {
|
||||||
|
len : uint16;
|
||||||
|
val : bytestring &length=(len+7)/8;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
## SSH2
|
||||||
|
|
||||||
|
type SSH2_Header(is_orig: bool) = record {
|
||||||
|
packet_length : uint32;
|
||||||
|
padding_length : uint8;
|
||||||
|
msg_type : uint8;
|
||||||
|
} &let {
|
||||||
|
payload_length : uint32 = packet_length - padding_length - 2;
|
||||||
|
detach : bool = $context.connection.update_state(ENCRYPTED, is_orig) &if(msg_type == MSG_NEWKEYS);
|
||||||
|
};
|
||||||
|
|
||||||
|
type SSH2_Key_Exchange(is_orig: bool) = record {
|
||||||
|
header : SSH2_Header(is_orig);
|
||||||
|
payload : SSH2_Message(is_orig, header.msg_type, header.payload_length);
|
||||||
|
pad : bytestring &length=header.padding_length;
|
||||||
|
} &length=header.packet_length + 4;
|
||||||
|
|
||||||
|
type SSH2_Message(is_orig: bool, msg_type: uint8, length: uint32) = case $context.connection.get_state(is_orig) of {
|
||||||
|
KEX_INIT -> kex : SSH2_KEXINIT(length, is_orig);
|
||||||
|
KEX_DH_GEX -> kex_dh_gex : SSH2_Key_Exchange_DH_GEX_Message(is_orig, msg_type, length);
|
||||||
|
KEX_DH -> kex_dh : SSH2_Key_Exchange_DH_Message(is_orig, msg_type, length);
|
||||||
|
KEX_ECC -> kex_ecc : SSH2_Key_Exchange_ECC_Message(is_orig, msg_type, length);
|
||||||
|
KEX_GSS -> kex_gss : SSH2_Key_Exchange_GSS_Message(is_orig, msg_type, length);
|
||||||
|
KEX_RSA -> kex_rsa : SSH2_Key_Exchange_RSA_Message(is_orig, msg_type, length);
|
||||||
|
default -> unknown : bytestring &length=length;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SSH2_KEXINIT(length: uint32, is_orig: bool) = record {
|
||||||
|
cookie : bytestring &length=16;
|
||||||
|
kex_algorithms : ssh_string;
|
||||||
|
server_host_key_algorithms : ssh_string;
|
||||||
|
encryption_algorithms_client_to_server : ssh_string;
|
||||||
|
encryption_algorithms_server_to_client : ssh_string;
|
||||||
|
mac_algorithms_client_to_server : ssh_string;
|
||||||
|
mac_algorithms_server_to_client : ssh_string;
|
||||||
|
compression_algorithms_client_to_server : ssh_string;
|
||||||
|
compression_algorithms_server_to_client : ssh_string;
|
||||||
|
languages_client_to_server : ssh_string;
|
||||||
|
languages_server_to_client : ssh_string;
|
||||||
|
first_kex_packet_follows : uint8;
|
||||||
|
reserved : uint32;
|
||||||
|
} &let {
|
||||||
|
proc_kex : bool= $context.connection.update_kex(kex_algorithms.val, is_orig);
|
||||||
|
} &length=length;
|
||||||
|
|
||||||
|
# KEX_DH exchanges
|
||||||
|
|
||||||
|
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_REPLY -> reply : SSH2_DH_GEX_REPLY(length);
|
||||||
|
default -> unknown: bytestring &length=length &transient;
|
||||||
|
};
|
||||||
|
|
||||||
|
# KEX_DH_GEX exchanges
|
||||||
|
|
||||||
|
type SSH2_Key_Exchange_DH_GEX_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of {
|
||||||
|
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_GROUP -> group : SSH2_DH_GEX_GROUP(length);
|
||||||
|
SSH_MSG_KEX_DH_GEX_INIT -> init : SSH2_DH_GEX_INIT(length);
|
||||||
|
SSH_MSG_KEX_DH_GEX_REPLY -> reply : SSH2_DH_GEX_REPLY(length);
|
||||||
|
default -> unknown : bytestring &length=length &transient;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SSH2_DH_GEX_REQUEST = record {
|
||||||
|
min : uint32;
|
||||||
|
n : uint32;
|
||||||
|
max : uint32;
|
||||||
|
} &length=12;
|
||||||
|
|
||||||
|
type SSH2_DH_GEX_REQUEST_OLD = record {
|
||||||
|
n : uint32;
|
||||||
|
} &length=4;
|
||||||
|
|
||||||
|
type SSH2_DH_GEX_GROUP(length: uint32) = record {
|
||||||
|
p : ssh_string;
|
||||||
|
g : ssh_string;
|
||||||
|
} &length=length;
|
||||||
|
|
||||||
|
type SSH2_DH_GEX_INIT(length: uint32) = record {
|
||||||
|
e : ssh_string;
|
||||||
|
} &length=length;
|
||||||
|
|
||||||
|
type SSH2_DH_GEX_REPLY(length: uint32) = record {
|
||||||
|
k_s : ssh_string;
|
||||||
|
f : ssh_string;
|
||||||
|
signature : ssh_string;
|
||||||
|
} &length=length;
|
||||||
|
|
||||||
|
# KEX_RSA exchanges
|
||||||
|
|
||||||
|
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_SECRET -> secret : SSH2_RSA_SECRET(length);
|
||||||
|
SSH_MSG_KEXRSA_DONE -> done : SSH2_RSA_DONE(length);
|
||||||
|
};
|
||||||
|
|
||||||
|
type SSH2_RSA_PUBKEY(length: uint32) = record {
|
||||||
|
k_s : ssh_string;
|
||||||
|
k_t : ssh_string;
|
||||||
|
} &length=length;
|
||||||
|
|
||||||
|
type SSH2_RSA_SECRET(length: uint32) = record {
|
||||||
|
encrypted_payload : ssh_string;
|
||||||
|
} &length=length;
|
||||||
|
|
||||||
|
type SSH2_RSA_DONE(length: uint32) = record {
|
||||||
|
signature : ssh_string;
|
||||||
|
} &length=length;
|
||||||
|
|
||||||
|
# KEX_GSS exchanges
|
||||||
|
|
||||||
|
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_CONTINUE -> cont : SSH2_GSS_CONTINUE(length);
|
||||||
|
SSH_MSG_KEXGSS_COMPLETE -> complete : SSH2_GSS_COMPLETE(length);
|
||||||
|
SSH_MSG_KEXGSS_HOSTKEY -> hostkey : SSH2_GSS_HOSTKEY(length);
|
||||||
|
SSH_MSG_KEXGSS_ERROR -> error : SSH2_GSS_ERROR(length);
|
||||||
|
SSH_MSG_KEXGSS_GROUPREQ -> groupreq : SSH2_DH_GEX_REQUEST;
|
||||||
|
SSH_MSG_KEXGSS_GROUP -> group : SSH2_DH_GEX_GROUP(length);
|
||||||
|
};
|
||||||
|
|
||||||
|
type SSH2_GSS_INIT(length: uint32) = record {
|
||||||
|
output_token : ssh_string;
|
||||||
|
e : ssh_string;
|
||||||
|
} &length=length;
|
||||||
|
|
||||||
|
type SSH2_GSS_CONTINUE(length: uint32) = record {
|
||||||
|
output_token : ssh_string;
|
||||||
|
} &length=length;
|
||||||
|
|
||||||
|
type SSH2_GSS_COMPLETE(length: uint32) = record {
|
||||||
|
f : ssh_string;
|
||||||
|
per_msg_token : ssh_string;
|
||||||
|
have_token : uint8;
|
||||||
|
parse_token : case have_token of {
|
||||||
|
0 -> no_token : empty;
|
||||||
|
default -> token : ssh_string;
|
||||||
|
};
|
||||||
|
} &length=length;
|
||||||
|
|
||||||
|
type SSH2_GSS_HOSTKEY(length: uint32) = record {
|
||||||
|
k_s : ssh_string;
|
||||||
|
} &length=length;
|
||||||
|
|
||||||
|
type SSH2_GSS_ERROR(length: uint32) = record {
|
||||||
|
major_status : uint32;
|
||||||
|
minor_status : uint32;
|
||||||
|
message : ssh_string;
|
||||||
|
language : ssh_string;
|
||||||
|
} &length=length;
|
||||||
|
|
||||||
|
# KEX_ECDH and KEX_ECMQV exchanges
|
||||||
|
|
||||||
|
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_REPLY -> reply : SSH2_ECC_REPLY(length);
|
||||||
|
};
|
||||||
|
|
||||||
|
# This deviates from the RFC. SSH_MSG_KEX_ECDH_INIT and
|
||||||
|
# SSH_MSG_KEX_ECMQV_INIT can be parsed the same way.
|
||||||
|
type SSH2_ECC_INIT(length: uint32) = record {
|
||||||
|
q_c : ssh_string;
|
||||||
|
};
|
||||||
|
|
||||||
|
# This deviates from the RFC. SSH_MSG_KEX_ECDH_REPLY and
|
||||||
|
# SSH_MSG_KEX_ECMQV_REPLY can be parsed the same way.
|
||||||
|
type SSH2_ECC_REPLY(length: uint32) = record {
|
||||||
|
k_s : ssh_string;
|
||||||
|
q_s : ssh_string;
|
||||||
|
signature : ssh_string;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Helper types
|
||||||
|
|
||||||
|
type ssh_string = record {
|
||||||
|
len : uint32;
|
||||||
|
val : bytestring &length=len;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ssh_host_key = record {
|
||||||
|
len : uint32;
|
||||||
|
key_type : ssh_string;
|
||||||
|
key : ssh_string;
|
||||||
|
} &length=(len + 4);
|
||||||
|
|
||||||
|
## Done with types
|
||||||
|
|
||||||
|
refine connection SSH_Conn += {
|
||||||
|
%member{
|
||||||
|
int state_up_;
|
||||||
|
int state_down_;
|
||||||
|
int version_;
|
||||||
|
|
||||||
|
bool kex_orig_;
|
||||||
|
bool kex_seen_;
|
||||||
|
bytestring kex_algs_cache_;
|
||||||
|
bytestring kex_algorithm_;
|
||||||
|
%}
|
||||||
|
|
||||||
|
%init{
|
||||||
|
state_up_ = VERSION_EXCHANGE;
|
||||||
|
state_down_ = VERSION_EXCHANGE;
|
||||||
|
version_ = UNK;
|
||||||
|
|
||||||
|
kex_seen_ = false;
|
||||||
|
kex_orig_ = false;
|
||||||
|
kex_algs_cache_ = bytestring();
|
||||||
|
kex_algorithm_ = bytestring();
|
||||||
|
%}
|
||||||
|
|
||||||
|
%cleanup{
|
||||||
|
kex_algorithm_.free();
|
||||||
|
kex_algs_cache_.free();
|
||||||
|
%}
|
||||||
|
|
||||||
|
function get_state(is_orig: bool) : int
|
||||||
|
%{
|
||||||
|
if ( is_orig )
|
||||||
|
{
|
||||||
|
return state_up_;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return state_down_;
|
||||||
|
}
|
||||||
|
%}
|
||||||
|
|
||||||
|
function update_state(s: state, is_orig: bool) : bool
|
||||||
|
%{
|
||||||
|
if ( is_orig )
|
||||||
|
state_up_ = s;
|
||||||
|
else
|
||||||
|
state_down_ = s;
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function get_version() : int
|
||||||
|
%{
|
||||||
|
return version_;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function update_version(v: bytestring, is_orig: bool) : bool
|
||||||
|
%{
|
||||||
|
if ( is_orig && ( v.length() >= 4 ) )
|
||||||
|
{
|
||||||
|
if ( v[4] == '2' )
|
||||||
|
version_ = SSH2;
|
||||||
|
if ( v[4] == '1' )
|
||||||
|
version_ = SSH1;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function update_kex_state_if_equal(s: string, to_state: state) : bool
|
||||||
|
%{
|
||||||
|
if ( std_str(kex_algorithm_).compare(s) == 0 )
|
||||||
|
{
|
||||||
|
update_state(to_state, true);
|
||||||
|
update_state(to_state, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function update_kex_state_if_startswith(s: string, to_state: state) : bool
|
||||||
|
%{
|
||||||
|
if ( (uint) kex_algorithm_.length() < s.length() )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ( std_str(kex_algorithm_).substr(0, s.length()).compare(s) == 0 )
|
||||||
|
{
|
||||||
|
update_state(to_state, true);
|
||||||
|
update_state(to_state, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function update_kex(algs: bytestring, orig: bool) : bool
|
||||||
|
%{
|
||||||
|
if ( !kex_seen_ )
|
||||||
|
{
|
||||||
|
kex_seen_ = true;
|
||||||
|
kex_orig_ = orig;
|
||||||
|
kex_algs_cache_.init(${algs}.data(), ${algs}.length());
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if ( kex_orig_ == orig )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
VectorVal* client_list = name_list_to_vector(orig ? algs : kex_algs_cache_);
|
||||||
|
VectorVal* server_list = name_list_to_vector(orig ? kex_algs_cache_ : algs);
|
||||||
|
|
||||||
|
for ( unsigned int i = 0; i < client_list->Size(); ++i )
|
||||||
|
{
|
||||||
|
for ( unsigned int j = 0; j < server_list->Size(); ++j )
|
||||||
|
{
|
||||||
|
if ( *(client_list->Lookup(i)->AsStringVal()->AsString()) == *(server_list->Lookup(j)->AsStringVal()->AsString()) )
|
||||||
|
{
|
||||||
|
kex_algorithm_.init((const uint8 *) client_list->Lookup(i)->AsStringVal()->Bytes(),
|
||||||
|
client_list->Lookup(i)->AsStringVal()->Len());
|
||||||
|
|
||||||
|
Unref(client_list);
|
||||||
|
Unref(server_list);
|
||||||
|
|
||||||
|
// UNTESTED
|
||||||
|
if ( update_kex_state_if_equal("rsa1024-sha1", KEX_RSA) )
|
||||||
|
return true;
|
||||||
|
// UNTESTED
|
||||||
|
if ( update_kex_state_if_equal("rsa2048-sha256", KEX_RSA) )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// UNTESTED
|
||||||
|
if ( update_kex_state_if_equal("diffie-hellman-group1-sha1", KEX_DH) )
|
||||||
|
return true;
|
||||||
|
// UNTESTED
|
||||||
|
if ( update_kex_state_if_equal("diffie-hellman-group14-sha1", KEX_DH) )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if ( update_kex_state_if_equal("diffie-hellman-group-exchange-sha1", KEX_DH_GEX) )
|
||||||
|
return true;
|
||||||
|
if ( update_kex_state_if_equal("diffie-hellman-group-exchange-sha256", KEX_DH_GEX) )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if ( update_kex_state_if_startswith("gss-group1-sha1-", KEX_GSS) )
|
||||||
|
return true;
|
||||||
|
if ( update_kex_state_if_startswith("gss-group14-sha1-", KEX_GSS) )
|
||||||
|
return true;
|
||||||
|
if ( update_kex_state_if_startswith("gss-gex-sha1-", KEX_GSS) )
|
||||||
|
return true;
|
||||||
|
if ( update_kex_state_if_startswith("gss-", KEX_GSS) )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if ( update_kex_state_if_startswith("ecdh-sha2-", KEX_ECC) )
|
||||||
|
return true;
|
||||||
|
if ( update_kex_state_if_equal("ecmqv-sha2", KEX_ECC) )
|
||||||
|
return true;
|
||||||
|
if ( update_kex_state_if_equal("curve25519-sha256@libssh.org", KEX_ECC) )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
|
||||||
|
bro_analyzer()->Weird(fmt("ssh_unknown_kex_algorithm=%s", c_str(kex_algorithm_)));
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Unref(client_list);
|
||||||
|
Unref(server_list);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
};
|
33
src/analyzer/protocol/ssh/ssh.pac
Normal file
33
src/analyzer/protocol/ssh/ssh.pac
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# Generated by binpac_quickstart
|
||||||
|
|
||||||
|
# Analyzer for Secure Shell
|
||||||
|
# - ssh-protocol.pac: describes the SSH protocol messages
|
||||||
|
# - ssh-analyzer.pac: describes the SSH analyzer code
|
||||||
|
|
||||||
|
%include binpac.pac
|
||||||
|
%include bro.pac
|
||||||
|
|
||||||
|
%extern{
|
||||||
|
#include "types.bif.h"
|
||||||
|
#include "events.bif.h"
|
||||||
|
%}
|
||||||
|
|
||||||
|
analyzer SSH withcontext {
|
||||||
|
connection: SSH_Conn;
|
||||||
|
flow: SSH_Flow;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Our connection consists of two flows, one in each direction.
|
||||||
|
connection SSH_Conn(bro_analyzer: BroAnalyzer) {
|
||||||
|
upflow = SSH_Flow(true);
|
||||||
|
downflow = SSH_Flow(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
%include ssh-protocol.pac
|
||||||
|
|
||||||
|
# Now we define the flow:
|
||||||
|
flow SSH_Flow(is_orig: bool) {
|
||||||
|
flowunit = SSH_PDU(is_orig) withcontext(connection, this);
|
||||||
|
};
|
||||||
|
|
||||||
|
%include ssh-analyzer.pac
|
6
src/analyzer/protocol/ssh/types.bif
Normal file
6
src/analyzer/protocol/ssh/types.bif
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
module SSH;
|
||||||
|
|
||||||
|
type Algorithm_Prefs: record;
|
||||||
|
type Capabilities: record;
|
||||||
|
|
||||||
|
module GLOBAL;
|
|
@ -3,8 +3,8 @@
|
||||||
#empty_field (empty)
|
#empty_field (empty)
|
||||||
#unset_field -
|
#unset_field -
|
||||||
#path ssh
|
#path ssh
|
||||||
#open 2014-01-16-21-51-12
|
#open 2015-03-17-17-42-58
|
||||||
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p status direction client server
|
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version auth_success direction client server cipher_alg mac_alg compression_alg kex_alg host_key_alg host_key
|
||||||
#types time string addr port addr port string enum string string
|
#types time string addr port addr port count bool enum string string string string string string string string
|
||||||
1055289978.855137 CsRx2w45OKnoww6xl4 66.59.111.190 40264 172.28.2.3 22 failure INBOUND SSH-2.0-OpenSSH_3.6.1p1 SSH-1.99-OpenSSH_3.1p1
|
1055289978.855543 CsRx2w45OKnoww6xl4 66.59.111.190 40264 172.28.2.3 22 2 - - SSH-2.0-OpenSSH_3.6.1p1 SSH-1.99-OpenSSH_3.1p1 aes128-cbc hmac-md5 none diffie-hellman-group-exchange-sha1 ssh-rsa 20:7c:e5:96:b0:4e:ce:a4:db:e4:aa:29:e8:90:98:07
|
||||||
#close 2014-01-16-21-51-12
|
#close 2015-03-17-17-42-59
|
||||||
|
|
|
@ -94,6 +94,7 @@ scripts/base/init-bare.bro
|
||||||
build/scripts/base/bif/plugins/Bro_SMTP.events.bif.bro
|
build/scripts/base/bif/plugins/Bro_SMTP.events.bif.bro
|
||||||
build/scripts/base/bif/plugins/Bro_SMTP.functions.bif.bro
|
build/scripts/base/bif/plugins/Bro_SMTP.functions.bif.bro
|
||||||
build/scripts/base/bif/plugins/Bro_SOCKS.events.bif.bro
|
build/scripts/base/bif/plugins/Bro_SOCKS.events.bif.bro
|
||||||
|
build/scripts/base/bif/plugins/Bro_SSH.types.bif.bro
|
||||||
build/scripts/base/bif/plugins/Bro_SSH.events.bif.bro
|
build/scripts/base/bif/plugins/Bro_SSH.events.bif.bro
|
||||||
build/scripts/base/bif/plugins/Bro_SSL.events.bif.bro
|
build/scripts/base/bif/plugins/Bro_SSL.events.bif.bro
|
||||||
build/scripts/base/bif/plugins/Bro_SteppingStone.events.bif.bro
|
build/scripts/base/bif/plugins/Bro_SteppingStone.events.bif.bro
|
||||||
|
|
|
@ -94,6 +94,7 @@ scripts/base/init-bare.bro
|
||||||
build/scripts/base/bif/plugins/Bro_SMTP.events.bif.bro
|
build/scripts/base/bif/plugins/Bro_SMTP.events.bif.bro
|
||||||
build/scripts/base/bif/plugins/Bro_SMTP.functions.bif.bro
|
build/scripts/base/bif/plugins/Bro_SMTP.functions.bif.bro
|
||||||
build/scripts/base/bif/plugins/Bro_SOCKS.events.bif.bro
|
build/scripts/base/bif/plugins/Bro_SOCKS.events.bif.bro
|
||||||
|
build/scripts/base/bif/plugins/Bro_SSH.types.bif.bro
|
||||||
build/scripts/base/bif/plugins/Bro_SSH.events.bif.bro
|
build/scripts/base/bif/plugins/Bro_SSH.events.bif.bro
|
||||||
build/scripts/base/bif/plugins/Bro_SSL.events.bif.bro
|
build/scripts/base/bif/plugins/Bro_SSL.events.bif.bro
|
||||||
build/scripts/base/bif/plugins/Bro_SteppingStone.events.bif.bro
|
build/scripts/base/bif/plugins/Bro_SteppingStone.events.bif.bro
|
||||||
|
|
|
@ -196,7 +196,7 @@
|
||||||
0.000000 MetaHookPost CallFunction(Log::__create_stream, <frame>, (Weird::LOG, [columns=<no value description>, ev=Weird::log_weird, path=weird])) -> <no result>
|
0.000000 MetaHookPost CallFunction(Log::__create_stream, <frame>, (Weird::LOG, [columns=<no value description>, ev=Weird::log_weird, path=weird])) -> <no result>
|
||||||
0.000000 MetaHookPost CallFunction(Log::__create_stream, <frame>, (X509::LOG, [columns=<no value description>, ev=X509::log_x509, path=x509])) -> <no result>
|
0.000000 MetaHookPost CallFunction(Log::__create_stream, <frame>, (X509::LOG, [columns=<no value description>, ev=X509::log_x509, path=x509])) -> <no result>
|
||||||
0.000000 MetaHookPost CallFunction(Log::__create_stream, <frame>, (mysql::LOG, [columns=<no value description>, ev=MySQL::log_mysql, path=mysql])) -> <no result>
|
0.000000 MetaHookPost CallFunction(Log::__create_stream, <frame>, (mysql::LOG, [columns=<no value description>, ev=MySQL::log_mysql, path=mysql])) -> <no result>
|
||||||
0.000000 MetaHookPost CallFunction(Log::__write, <frame>, (PacketFilter::LOG, [ts=1427139545.064324, node=bro, filter=ip or not ip, init=T, success=T])) -> <no result>
|
0.000000 MetaHookPost CallFunction(Log::__write, <frame>, (PacketFilter::LOG, [ts=1427306548.357084, node=bro, filter=ip or not ip, init=T, success=T])) -> <no result>
|
||||||
0.000000 MetaHookPost CallFunction(Log::add_default_filter, <frame>, (Cluster::LOG)) -> <no result>
|
0.000000 MetaHookPost CallFunction(Log::add_default_filter, <frame>, (Cluster::LOG)) -> <no result>
|
||||||
0.000000 MetaHookPost CallFunction(Log::add_default_filter, <frame>, (Communication::LOG)) -> <no result>
|
0.000000 MetaHookPost CallFunction(Log::add_default_filter, <frame>, (Communication::LOG)) -> <no result>
|
||||||
0.000000 MetaHookPost CallFunction(Log::add_default_filter, <frame>, (Conn::LOG)) -> <no result>
|
0.000000 MetaHookPost CallFunction(Log::add_default_filter, <frame>, (Conn::LOG)) -> <no result>
|
||||||
|
@ -290,7 +290,7 @@
|
||||||
0.000000 MetaHookPost CallFunction(Log::create_stream, <frame>, (Weird::LOG, [columns=<no value description>, ev=Weird::log_weird, path=weird])) -> <no result>
|
0.000000 MetaHookPost CallFunction(Log::create_stream, <frame>, (Weird::LOG, [columns=<no value description>, ev=Weird::log_weird, path=weird])) -> <no result>
|
||||||
0.000000 MetaHookPost CallFunction(Log::create_stream, <frame>, (X509::LOG, [columns=<no value description>, ev=X509::log_x509, path=x509])) -> <no result>
|
0.000000 MetaHookPost CallFunction(Log::create_stream, <frame>, (X509::LOG, [columns=<no value description>, ev=X509::log_x509, path=x509])) -> <no result>
|
||||||
0.000000 MetaHookPost CallFunction(Log::create_stream, <frame>, (mysql::LOG, [columns=<no value description>, ev=MySQL::log_mysql, path=mysql])) -> <no result>
|
0.000000 MetaHookPost CallFunction(Log::create_stream, <frame>, (mysql::LOG, [columns=<no value description>, ev=MySQL::log_mysql, path=mysql])) -> <no result>
|
||||||
0.000000 MetaHookPost CallFunction(Log::write, <frame>, (PacketFilter::LOG, [ts=1427139545.064324, node=bro, filter=ip or not ip, init=T, success=T])) -> <no result>
|
0.000000 MetaHookPost CallFunction(Log::write, <frame>, (PacketFilter::LOG, [ts=1427306548.357084, node=bro, filter=ip or not ip, init=T, success=T])) -> <no result>
|
||||||
0.000000 MetaHookPost CallFunction(Notice::want_pp, <frame>, ()) -> <no result>
|
0.000000 MetaHookPost CallFunction(Notice::want_pp, <frame>, ()) -> <no result>
|
||||||
0.000000 MetaHookPost CallFunction(PacketFilter::build, <frame>, ()) -> <no result>
|
0.000000 MetaHookPost CallFunction(PacketFilter::build, <frame>, ()) -> <no result>
|
||||||
0.000000 MetaHookPost CallFunction(PacketFilter::combine_filters, <frame>, (ip or not ip, and, )) -> <no result>
|
0.000000 MetaHookPost CallFunction(PacketFilter::combine_filters, <frame>, (ip or not ip, and, )) -> <no result>
|
||||||
|
@ -378,6 +378,7 @@
|
||||||
0.000000 MetaHookPost LoadFile(./Bro_SQLiteReader.sqlite.bif.bro) -> -1
|
0.000000 MetaHookPost LoadFile(./Bro_SQLiteReader.sqlite.bif.bro) -> -1
|
||||||
0.000000 MetaHookPost LoadFile(./Bro_SQLiteWriter.sqlite.bif.bro) -> -1
|
0.000000 MetaHookPost LoadFile(./Bro_SQLiteWriter.sqlite.bif.bro) -> -1
|
||||||
0.000000 MetaHookPost LoadFile(./Bro_SSH.events.bif.bro) -> -1
|
0.000000 MetaHookPost LoadFile(./Bro_SSH.events.bif.bro) -> -1
|
||||||
|
0.000000 MetaHookPost LoadFile(./Bro_SSH.types.bif.bro) -> -1
|
||||||
0.000000 MetaHookPost LoadFile(./Bro_SSL.events.bif.bro) -> -1
|
0.000000 MetaHookPost LoadFile(./Bro_SSL.events.bif.bro) -> -1
|
||||||
0.000000 MetaHookPost LoadFile(./Bro_SteppingStone.events.bif.bro) -> -1
|
0.000000 MetaHookPost LoadFile(./Bro_SteppingStone.events.bif.bro) -> -1
|
||||||
0.000000 MetaHookPost LoadFile(./Bro_Syslog.events.bif.bro) -> -1
|
0.000000 MetaHookPost LoadFile(./Bro_Syslog.events.bif.bro) -> -1
|
||||||
|
@ -737,7 +738,7 @@
|
||||||
0.000000 MetaHookPre CallFunction(Log::__create_stream, <frame>, (Weird::LOG, [columns=<no value description>, ev=Weird::log_weird, path=weird]))
|
0.000000 MetaHookPre CallFunction(Log::__create_stream, <frame>, (Weird::LOG, [columns=<no value description>, ev=Weird::log_weird, path=weird]))
|
||||||
0.000000 MetaHookPre CallFunction(Log::__create_stream, <frame>, (X509::LOG, [columns=<no value description>, ev=X509::log_x509, path=x509]))
|
0.000000 MetaHookPre CallFunction(Log::__create_stream, <frame>, (X509::LOG, [columns=<no value description>, ev=X509::log_x509, path=x509]))
|
||||||
0.000000 MetaHookPre CallFunction(Log::__create_stream, <frame>, (mysql::LOG, [columns=<no value description>, ev=MySQL::log_mysql, path=mysql]))
|
0.000000 MetaHookPre CallFunction(Log::__create_stream, <frame>, (mysql::LOG, [columns=<no value description>, ev=MySQL::log_mysql, path=mysql]))
|
||||||
0.000000 MetaHookPre CallFunction(Log::__write, <frame>, (PacketFilter::LOG, [ts=1427139545.064324, node=bro, filter=ip or not ip, init=T, success=T]))
|
0.000000 MetaHookPre CallFunction(Log::__write, <frame>, (PacketFilter::LOG, [ts=1427306548.357084, node=bro, filter=ip or not ip, init=T, success=T]))
|
||||||
0.000000 MetaHookPre CallFunction(Log::add_default_filter, <frame>, (Cluster::LOG))
|
0.000000 MetaHookPre CallFunction(Log::add_default_filter, <frame>, (Cluster::LOG))
|
||||||
0.000000 MetaHookPre CallFunction(Log::add_default_filter, <frame>, (Communication::LOG))
|
0.000000 MetaHookPre CallFunction(Log::add_default_filter, <frame>, (Communication::LOG))
|
||||||
0.000000 MetaHookPre CallFunction(Log::add_default_filter, <frame>, (Conn::LOG))
|
0.000000 MetaHookPre CallFunction(Log::add_default_filter, <frame>, (Conn::LOG))
|
||||||
|
@ -831,7 +832,7 @@
|
||||||
0.000000 MetaHookPre CallFunction(Log::create_stream, <frame>, (Weird::LOG, [columns=<no value description>, ev=Weird::log_weird, path=weird]))
|
0.000000 MetaHookPre CallFunction(Log::create_stream, <frame>, (Weird::LOG, [columns=<no value description>, ev=Weird::log_weird, path=weird]))
|
||||||
0.000000 MetaHookPre CallFunction(Log::create_stream, <frame>, (X509::LOG, [columns=<no value description>, ev=X509::log_x509, path=x509]))
|
0.000000 MetaHookPre CallFunction(Log::create_stream, <frame>, (X509::LOG, [columns=<no value description>, ev=X509::log_x509, path=x509]))
|
||||||
0.000000 MetaHookPre CallFunction(Log::create_stream, <frame>, (mysql::LOG, [columns=<no value description>, ev=MySQL::log_mysql, path=mysql]))
|
0.000000 MetaHookPre CallFunction(Log::create_stream, <frame>, (mysql::LOG, [columns=<no value description>, ev=MySQL::log_mysql, path=mysql]))
|
||||||
0.000000 MetaHookPre CallFunction(Log::write, <frame>, (PacketFilter::LOG, [ts=1427139545.064324, node=bro, filter=ip or not ip, init=T, success=T]))
|
0.000000 MetaHookPre CallFunction(Log::write, <frame>, (PacketFilter::LOG, [ts=1427306548.357084, node=bro, filter=ip or not ip, init=T, success=T]))
|
||||||
0.000000 MetaHookPre CallFunction(Notice::want_pp, <frame>, ())
|
0.000000 MetaHookPre CallFunction(Notice::want_pp, <frame>, ())
|
||||||
0.000000 MetaHookPre CallFunction(PacketFilter::build, <frame>, ())
|
0.000000 MetaHookPre CallFunction(PacketFilter::build, <frame>, ())
|
||||||
0.000000 MetaHookPre CallFunction(PacketFilter::combine_filters, <frame>, (ip or not ip, and, ))
|
0.000000 MetaHookPre CallFunction(PacketFilter::combine_filters, <frame>, (ip or not ip, and, ))
|
||||||
|
@ -919,6 +920,7 @@
|
||||||
0.000000 MetaHookPre LoadFile(./Bro_SQLiteReader.sqlite.bif.bro)
|
0.000000 MetaHookPre LoadFile(./Bro_SQLiteReader.sqlite.bif.bro)
|
||||||
0.000000 MetaHookPre LoadFile(./Bro_SQLiteWriter.sqlite.bif.bro)
|
0.000000 MetaHookPre LoadFile(./Bro_SQLiteWriter.sqlite.bif.bro)
|
||||||
0.000000 MetaHookPre LoadFile(./Bro_SSH.events.bif.bro)
|
0.000000 MetaHookPre LoadFile(./Bro_SSH.events.bif.bro)
|
||||||
|
0.000000 MetaHookPre LoadFile(./Bro_SSH.types.bif.bro)
|
||||||
0.000000 MetaHookPre LoadFile(./Bro_SSL.events.bif.bro)
|
0.000000 MetaHookPre LoadFile(./Bro_SSL.events.bif.bro)
|
||||||
0.000000 MetaHookPre LoadFile(./Bro_SteppingStone.events.bif.bro)
|
0.000000 MetaHookPre LoadFile(./Bro_SteppingStone.events.bif.bro)
|
||||||
0.000000 MetaHookPre LoadFile(./Bro_Syslog.events.bif.bro)
|
0.000000 MetaHookPre LoadFile(./Bro_Syslog.events.bif.bro)
|
||||||
|
@ -1277,7 +1279,7 @@
|
||||||
0.000000 | HookCallFunction Log::__create_stream(Weird::LOG, [columns=<no value description>, ev=Weird::log_weird, path=weird])
|
0.000000 | HookCallFunction Log::__create_stream(Weird::LOG, [columns=<no value description>, ev=Weird::log_weird, path=weird])
|
||||||
0.000000 | HookCallFunction Log::__create_stream(X509::LOG, [columns=<no value description>, ev=X509::log_x509, path=x509])
|
0.000000 | HookCallFunction Log::__create_stream(X509::LOG, [columns=<no value description>, ev=X509::log_x509, path=x509])
|
||||||
0.000000 | HookCallFunction Log::__create_stream(mysql::LOG, [columns=<no value description>, ev=MySQL::log_mysql, path=mysql])
|
0.000000 | HookCallFunction Log::__create_stream(mysql::LOG, [columns=<no value description>, ev=MySQL::log_mysql, path=mysql])
|
||||||
0.000000 | HookCallFunction Log::__write(PacketFilter::LOG, [ts=1427139545.064324, node=bro, filter=ip or not ip, init=T, success=T])
|
0.000000 | HookCallFunction Log::__write(PacketFilter::LOG, [ts=1427306548.357084, node=bro, filter=ip or not ip, init=T, success=T])
|
||||||
0.000000 | HookCallFunction Log::add_default_filter(Cluster::LOG)
|
0.000000 | HookCallFunction Log::add_default_filter(Cluster::LOG)
|
||||||
0.000000 | HookCallFunction Log::add_default_filter(Communication::LOG)
|
0.000000 | HookCallFunction Log::add_default_filter(Communication::LOG)
|
||||||
0.000000 | HookCallFunction Log::add_default_filter(Conn::LOG)
|
0.000000 | HookCallFunction Log::add_default_filter(Conn::LOG)
|
||||||
|
@ -1371,7 +1373,7 @@
|
||||||
0.000000 | HookCallFunction Log::create_stream(Weird::LOG, [columns=<no value description>, ev=Weird::log_weird, path=weird])
|
0.000000 | HookCallFunction Log::create_stream(Weird::LOG, [columns=<no value description>, ev=Weird::log_weird, path=weird])
|
||||||
0.000000 | HookCallFunction Log::create_stream(X509::LOG, [columns=<no value description>, ev=X509::log_x509, path=x509])
|
0.000000 | HookCallFunction Log::create_stream(X509::LOG, [columns=<no value description>, ev=X509::log_x509, path=x509])
|
||||||
0.000000 | HookCallFunction Log::create_stream(mysql::LOG, [columns=<no value description>, ev=MySQL::log_mysql, path=mysql])
|
0.000000 | HookCallFunction Log::create_stream(mysql::LOG, [columns=<no value description>, ev=MySQL::log_mysql, path=mysql])
|
||||||
0.000000 | HookCallFunction Log::write(PacketFilter::LOG, [ts=1427139545.064324, node=bro, filter=ip or not ip, init=T, success=T])
|
0.000000 | HookCallFunction Log::write(PacketFilter::LOG, [ts=1427306548.357084, node=bro, filter=ip or not ip, init=T, success=T])
|
||||||
0.000000 | HookCallFunction Notice::want_pp()
|
0.000000 | HookCallFunction Notice::want_pp()
|
||||||
0.000000 | HookCallFunction PacketFilter::build()
|
0.000000 | HookCallFunction PacketFilter::build()
|
||||||
0.000000 | HookCallFunction PacketFilter::combine_filters(ip or not ip, and, )
|
0.000000 | HookCallFunction PacketFilter::combine_filters(ip or not ip, and, )
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
#separator \x09
|
||||||
|
#set_separator ,
|
||||||
|
#empty_field (empty)
|
||||||
|
#unset_field -
|
||||||
|
#path ssh
|
||||||
|
#open 2015-03-17-17-44-34
|
||||||
|
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version auth_success direction client server cipher_alg mac_alg compression_alg kex_alg host_key_alg host_key
|
||||||
|
#types time string addr port addr port count bool enum string string string string string string string string
|
||||||
|
1324071333.793037 CXWv6p3arKYeMETxOg 192.168.1.79 51880 131.159.21.1 22 2 F - SSH-2.0-OpenSSH_5.9 SSH-2.0-OpenSSH_5.8 aes128-ctr hmac-md5 none ecdh-sha2-nistp256 ssh-rsa a7:26:62:3f:75:1f:33:8a:f3:32:90:8b:73:fd:2c:83
|
||||||
|
1409516196.413240 CjhGID4nQcgTWjvg4c 10.0.0.18 40184 128.2.6.88 41644 2 T - SSH-2.0-OpenSSH_6.6 SSH-2.0-OpenSSH_5.9p1 Debian-5ubuntu1.1 aes128-ctr hmac-md5 none ecdh-sha2-nistp256 ssh-rsa 8a:8d:55:28:1e:71:04:99:94:43:22:89:e5:ff:e9:03
|
||||||
|
1419870189.491788 CCvvfg3TEfuqmmG4bh 192.168.2.1 57189 192.168.2.158 22 2 T - SSH-2.0-OpenSSH_6.2 SSH-1.99-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2 aes128-ctr hmac-md5-etm@openssh.com none diffie-hellman-group-exchange-sha256 ssh-rsa 28:78:65:c1:c3:26:f7:1b:65:6a:44:14:d0:04:8f:b3
|
||||||
|
1419870206.112061 CsRx2w45OKnoww6xl4 192.168.2.1 57191 192.168.2.158 22 1 - - SSH-1.5-OpenSSH_6.2 SSH-1.99-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2 - - - - - a1:73:d1:e1:25:72:79:71:56:56:65:ed:81:bf:67:98
|
||||||
|
1419996264.344957 CRJuHdVW0XPVINV8a 192.168.2.1 55179 192.168.2.158 2200 2 T - SSH-2.0-OpenSSH_6.2 SSH-2.0-paramiko_1.15.2 aes128-ctr hmac-sha1 none diffie-hellman-group14-sha1 ssh-rsa 60:73:38:44:cb:51:86:65:7f:de:da:a2:2b:5a:57:d5
|
||||||
|
1420588548.729724 CPbrpk1qSsw6ESzHV4 192.168.2.1 56594 192.168.2.158 22 1 - - SSH-1.5-OpenSSH_5.3 SSH-1.99-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2 - - - - - a1:73:d1:e1:25:72:79:71:56:56:65:ed:81:bf:67:98
|
||||||
|
1420590124.886029 C6pKV8GSxOnSLghOa 192.168.2.1 56821 192.168.2.158 22 1 - - SSH-1.5-OpenSSH_6.2 SSH-1.99-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2 - - - - - a1:73:d1:e1:25:72:79:71:56:56:65:ed:81:bf:67:98
|
||||||
|
1420590308.781417 CIPOse170MGiRM1Qf4 192.168.2.1 56837 192.168.2.158 22 1 - - SSH-1.5-OpenSSH_6.2 SSH-1.99-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2 - - - - - a1:73:d1:e1:25:72:79:71:56:56:65:ed:81:bf:67:98
|
||||||
|
1420590322.682734 C7XEbhP654jzLoe3a 192.168.2.1 56845 192.168.2.158 22 1 - - SSH-1.5-OpenSSH_6.2 SSH-1.99-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2 - - - - - a1:73:d1:e1:25:72:79:71:56:56:65:ed:81:bf:67:98
|
||||||
|
1420590636.482870 CJ3xTn1c4Zw9TmAE05 192.168.2.1 56875 192.168.2.158 22 1 - - SSH-1.5-OpenSSH_6.2 SSH-1.99-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2 - - - - - a1:73:d1:e1:25:72:79:71:56:56:65:ed:81:bf:67:98
|
||||||
|
1420590659.429753 CMXxB5GvmoxJFXdTa 192.168.2.1 56878 192.168.2.158 22 1 - - SSH-1.5-OpenSSH_6.2 SSH-1.99-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2 - - - - - a1:73:d1:e1:25:72:79:71:56:56:65:ed:81:bf:67:98
|
||||||
|
1420591379.658841 Caby8b1slFea8xwSmb 192.168.2.1 56940 192.168.2.158 22 1 - - SSH-1.5-OpenSSH_6.2 SSH-1.99-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2 - - - - - a1:73:d1:e1:25:72:79:71:56:56:65:ed:81:bf:67:98
|
||||||
|
1420599430.828624 Che1bq3i2rO3KD1Syg 192.168.2.1 57831 192.168.2.158 22 1 - - SSH-1.5-OpenSSH_6.2 SSH-1.99-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2 - - - - - a1:73:d1:e1:25:72:79:71:56:56:65:ed:81:bf:67:98
|
||||||
|
1420851448.317515 C3SfNE4BWaU4aSuwkc 192.168.2.1 59246 192.168.2.158 22 2 T - SSH-2.0-OpenSSH_6.2 SSH-1.99-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2 arcfour256 hmac-md5-etm@openssh.com none diffie-hellman-group-exchange-sha256 ssh-rsa 28:78:65:c1:c3:26:f7:1b:65:6a:44:14:d0:04:8f:b3
|
||||||
|
1420860283.083659 CEle3f3zno26fFZkrh 192.168.1.32 41164 128.2.10.238 22 2 T - SSH-2.0-OpenSSH_6.6p1-hpn14v4 SSH-1.99-OpenSSH_3.4+p1+gssapi+OpenSSH_3.7.1buf_fix+2006100301 aes128-cbc hmac-md5 none diffie-hellman-group-exchange-sha1 ssh-rsa 7f:e5:81:92:26:77:05:44:c4:60:fb:cd:89:c8:81:ee
|
||||||
|
1420860616.459806 CwSkQu4eWZCH7OONC1 192.168.1.32 33910 128.2.13.133 22 2 T - SSH-2.0-OpenSSH_6.6p1-hpn14v4 SSH-2.0-OpenSSH_5.3 aes128-ctr hmac-md5 none diffie-hellman-group-exchange-sha256 ssh-rsa 93:d8:4c:0d:b2:c3:2e:da:b9:c0:67:db:e4:8f:95:04
|
||||||
|
1420868281.691929 CfTOmO0HKorjr8Zp7 192.168.1.32 41268 128.2.10.238 22 2 F - SSH-2.0-OpenSSH_6.6 SSH-1.99-OpenSSH_3.4+p1+gssapi+OpenSSH_3.7.1buf_fix+2006100301 aes128-cbc hmac-md5 none diffie-hellman-group-exchange-sha1 ssh-rsa 7f:e5:81:92:26:77:05:44:c4:60:fb:cd:89:c8:81:ee
|
||||||
|
1420917487.230379 Cab0vO1xNYSS2hJkle 192.168.1.31 52294 192.168.1.32 22 2 T - SSH-2.0-OpenSSH_6.7 SSH-2.0-OpenSSH_6.7 chacha20-poly1305@openssh.com hmac-sha2-512-etm@openssh.com none curve25519-sha256@libssh.org ssh-ed25519 e4:b1:8e:ca:6e:0e:e5:3c:7e:a4:0e:70:34:9d:b2:b1
|
||||||
|
1421006072.225176 Cx2FqO23omNawSNrxj 192.168.1.31 51489 192.168.1.32 22 2 T - SSH-2.0-OpenSSH_6.7 SSH-2.0-OpenSSH_6.7 chacha20-poly1305@openssh.com hmac-sha2-512-etm@openssh.com none curve25519-sha256@libssh.org ssh-ed25519 e4:b1:8e:ca:6e:0e:e5:3c:7e:a4:0e:70:34:9d:b2:b1
|
||||||
|
1421041177.043845 CkDsfG2YIeWJmXWNWj 192.168.1.32 58641 131.103.20.168 22 2 F - SSH-2.0-OpenSSH_6.7 SSH-2.0-OpenSSH_5.3 aes128-ctr hmac-md5 none diffie-hellman-group-exchange-sha256 ssh-rsa 97:8c:1b:f2:6f:14:6b:5c:3b:ec:aa:46:46:74:7c:40
|
||||||
|
1421041299.824707 CUKS0W3HFYOnBqSE5e 192.168.1.32 58646 131.103.20.168 22 2 T - SSH-2.0-OpenSSH_6.7 SSH-2.0-OpenSSH_5.3 aes128-ctr hmac-md5 none diffie-hellman-group-exchange-sha256 ssh-rsa 97:8c:1b:f2:6f:14:6b:5c:3b:ec:aa:46:46:74:7c:40
|
||||||
|
1421041526.397714 CRrfvP2lalMAYOCLhj 192.168.1.32 58649 131.103.20.168 22 2 T - SSH-2.0-OpenSSH_6.7 SSH-2.0-OpenSSH_5.3 aes128-ctr hmac-md5 none diffie-hellman-group-exchange-sha256 ssh-rsa 97:8c:1b:f2:6f:14:6b:5c:3b:ec:aa:46:46:74:7c:40
|
||||||
|
#close 2015-03-17-17-44-34
|
BIN
testing/btest/Traces/ssh/ssh.trace
Normal file
BIN
testing/btest/Traces/ssh/ssh.trace
Normal file
Binary file not shown.
10
testing/btest/core/leaks/ssh.test
Normal file
10
testing/btest/core/leaks/ssh.test
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# Needs perftools support.
|
||||||
|
#
|
||||||
|
# @TEST-REQUIRES: bro --help 2>&1 | grep -q mem-leaks
|
||||||
|
#
|
||||||
|
# @TEST-GROUP: leaks
|
||||||
|
#
|
||||||
|
# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local btest-bg-run bro bro -b -m -r $TRACES/ssh/ssh.trace %INPUT
|
||||||
|
# @TEST-EXEC: btest-bg-wait 30
|
||||||
|
|
||||||
|
@load base/protocols/ssh
|
|
@ -1,8 +1,8 @@
|
||||||
#
|
#
|
||||||
# @TEST-EXEC: bro -r ${TRACES}/ssh-on-port-80.trace %INPUT dpd_buffer_size=0;
|
# @TEST-EXEC: bro -r ${TRACES}/ssh/ssh-on-port-80.trace %INPUT dpd_buffer_size=0;
|
||||||
# @TEST-EXEC: cat conn.log | bro-cut service | grep -q ssh
|
# @TEST-EXEC: cat conn.log | bro-cut service | grep -q ssh
|
||||||
#
|
#
|
||||||
# @TEST-EXEC: bro -r ${TRACES}/ssh-on-port-80.trace dpd_buffer_size=0;
|
# @TEST-EXEC: bro -r ${TRACES}/ssh/ssh-on-port-80.trace dpd_buffer_size=0;
|
||||||
# @TEST-EXEC: cat conn.log | bro-cut service | grep -vq ssh
|
# @TEST-EXEC: cat conn.log | bro-cut service | grep -vq ssh
|
||||||
|
|
||||||
event bro_init()
|
event bro_init()
|
||||||
|
|
|
@ -21,6 +21,7 @@ T -43 SSH::LOG 21 123 10.0.0.0/24 1.2.3.4 3.14 1315801931.273616 100.000000 hurz
|
||||||
F -43 SSH::LOG 21 123 10.0.0.0/24 1.2.3.4 3.14 1315801931.273616 100.000000 hurz 2,4,1,3 CC,AA,BB EMPTY 10,20,30 EMPTY SSH::foo\x0a{ \x0aif (0 < SSH::i) \x0a\x09return (Foo);\x0aelse\x0a\x09return (Bar);\x0a\x0a}
|
F -43 SSH::LOG 21 123 10.0.0.0/24 1.2.3.4 3.14 1315801931.273616 100.000000 hurz 2,4,1,3 CC,AA,BB EMPTY 10,20,30 EMPTY SSH::foo\x0a{ \x0aif (0 < SSH::i) \x0a\x09return (Foo);\x0aelse\x0a\x09return (Bar);\x0a\x0a}
|
||||||
@TEST-END-FILE
|
@TEST-END-FILE
|
||||||
|
|
||||||
|
@load base/frameworks/communication # keep network time running
|
||||||
@load base/protocols/ssh
|
@load base/protocols/ssh
|
||||||
redef exit_only_after_terminate = T;
|
redef exit_only_after_terminate = T;
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ T -43 SSH::LOG 21 123 10.0.0.0/24 1.2.3.4 3.14 1315801931.273616 100.000000 hurz
|
||||||
F -44 SSH::LOG 21 123 10.0.0.0/24 1.2.3.4 3.14 1315801931.273616 100.000000 hurz 2,4,1,3 CC,AA,BB EMPTY 10,20,30 EMPTY SSH::foo\x0a{ \x0aif (0 < SSH::i) \x0a\x09return (Foo);\x0aelse\x0a\x09return (Bar);\x0a\x0a}
|
F -44 SSH::LOG 21 123 10.0.0.0/24 1.2.3.4 3.14 1315801931.273616 100.000000 hurz 2,4,1,3 CC,AA,BB EMPTY 10,20,30 EMPTY SSH::foo\x0a{ \x0aif (0 < SSH::i) \x0a\x09return (Foo);\x0aelse\x0a\x09return (Bar);\x0a\x0a}
|
||||||
@TEST-END-FILE
|
@TEST-END-FILE
|
||||||
|
|
||||||
|
@load base/frameworks/communication # keep network time running
|
||||||
@load base/protocols/ssh
|
@load base/protocols/ssh
|
||||||
redef exit_only_after_terminate = T;
|
redef exit_only_after_terminate = T;
|
||||||
|
|
||||||
|
|
4
testing/btest/scripts/base/protocols/ssh/basic.test
Normal file
4
testing/btest/scripts/base/protocols/ssh/basic.test
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# This tests some SSH connections and the output log.
|
||||||
|
|
||||||
|
# @TEST-EXEC: bro -r $TRACES/ssh/ssh.trace %INPUT
|
||||||
|
# @TEST-EXEC: btest-diff ssh.log
|
Loading…
Add table
Add a link
Reference in a new issue