mirror of
https://github.com/zeek/zeek.git
synced 2025-10-07 17:18:20 +00:00
Merge remote-tracking branch 'origin/master' into topic/johanna/spicy-tls
* origin/master: (386 commits) Normalize version strings in test Update doc submodule [nomail] [skip ci] Update external testing baseline hashes fuzzers: Add DTLS fuzzer generic-analyzer-fuzzer: Support NextPacket() fuzzing Require `truncate` for a test using it Bump outdated baseline Fix tests so they work both with GNU and BSD tools Install libmaxminddb in macOS CI Bump auxil/spicy to latest release Supervisor: Handle EAGAIN error on stem pipe fuzzer-setup: Allow customization without recompiling ssl: Prevent unbounded ssl_history growth ssl: Cap number of alerts parsed from SSL record subdir-btest: Allow setting build_dir Update doc submodule [nomail] [skip ci] CI: Pass -A flag to btest for cluster-testing builds Update doc submodule [nomail] [skip ci] Update baselines ftp: Do not base seq on number of pending commands ...
This commit is contained in:
commit
0afe94154d
800 changed files with 109788 additions and 98811 deletions
|
@ -88,8 +88,6 @@ function set_state(c: connection, state_x: BackingState)
|
|||
c$dce_rpc$endpoint = uuid_endpoint_map[c$dce_rpc_state$uuid];
|
||||
if ( c$dce_rpc_state?$named_pipe )
|
||||
c$dce_rpc$named_pipe = c$dce_rpc_state$named_pipe;
|
||||
|
||||
Conn::register_removal_hook(c, finalize_dce_rpc);
|
||||
}
|
||||
|
||||
function set_session(c: connection, fid: count)
|
||||
|
@ -97,7 +95,9 @@ function set_session(c: connection, fid: count)
|
|||
if ( ! c?$dce_rpc_backing )
|
||||
{
|
||||
c$dce_rpc_backing = table();
|
||||
Conn::register_removal_hook(c, finalize_dce_rpc);
|
||||
}
|
||||
|
||||
if ( fid !in c$dce_rpc_backing )
|
||||
{
|
||||
local info = Info($ts=network_time(),$id=c$id,$uid=c$uid);
|
||||
|
@ -216,6 +216,23 @@ event dce_rpc_response(c: connection, fid: count, ctx_id: count, opnum: count, s
|
|||
}
|
||||
}
|
||||
|
||||
event smb_discarded_dce_rpc_analyzers(c: connection)
|
||||
{
|
||||
# This event is raised when the DCE-RPC analyzers table
|
||||
# grew too large. Assume things are broken and wipe
|
||||
# the backing table.
|
||||
delete c$dce_rpc_backing;
|
||||
Reporter::conn_weird("SMB_discarded_dce_rpc_analyzers", c, "", "SMB");
|
||||
}
|
||||
|
||||
# If a fid representing a pipe was closed, remove it from dce_rpc_backing.
|
||||
event smb2_close_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID) &priority=-5
|
||||
{
|
||||
local fid = file_id$persistent + file_id$volatile;
|
||||
if ( c?$dce_rpc_backing )
|
||||
delete c$dce_rpc_backing[fid];
|
||||
}
|
||||
|
||||
hook finalize_dce_rpc(c: connection)
|
||||
{
|
||||
if ( ! c?$dce_rpc )
|
||||
|
|
|
@ -204,11 +204,16 @@ event DHCP::aggregate_msgs(ts: time, id: conn_id, uid: string, is_orig: bool, ms
|
|||
|
||||
log_info$msg_types += DHCP::message_types[msg$m_type];
|
||||
|
||||
# The is_orig flag is T for "connections" initiated by servers
|
||||
# to broadcast addresses, otherwise is_orig indicates that this
|
||||
# is a DHCP client.
|
||||
local is_client = is_orig && (id$orig_h == 0.0.0.0 || id$orig_p == 68/udp || id$resp_p == 67/udp);
|
||||
|
||||
# Let's watch for messages in any DHCP message type
|
||||
# and split them out based on client and server.
|
||||
if ( options?$message )
|
||||
{
|
||||
if ( is_orig )
|
||||
if ( is_client )
|
||||
log_info$client_message = options$message;
|
||||
else
|
||||
log_info$server_message = options$message;
|
||||
|
@ -218,7 +223,7 @@ event DHCP::aggregate_msgs(ts: time, id: conn_id, uid: string, is_orig: bool, ms
|
|||
# expiration handling.
|
||||
log_info$last_message_ts = ts;
|
||||
|
||||
if ( is_orig ) # client requests
|
||||
if ( is_client ) # client requests
|
||||
{
|
||||
# Assign the client addr in case this is a session
|
||||
# of only INFORM messages (no lease handed out).
|
||||
|
@ -246,12 +251,27 @@ event DHCP::aggregate_msgs(ts: time, id: conn_id, uid: string, is_orig: bool, ms
|
|||
{
|
||||
# Only log the address of the server if it handed out
|
||||
# an IP address.
|
||||
if ( msg$yiaddr != 0.0.0.0 &&
|
||||
id$resp_h != 255.255.255.255 )
|
||||
if ( msg$yiaddr != 0.0.0.0 )
|
||||
{
|
||||
log_info$server_addr = id$resp_h;
|
||||
log_info$server_port = id$resp_p;
|
||||
log_info$client_port = id$orig_p;
|
||||
if ( is_orig )
|
||||
{
|
||||
# This is a server message and is_orig is T.
|
||||
# This means it's a DHCP server broadcasting
|
||||
# and the server is the originator.
|
||||
log_info$server_addr = id$orig_h;
|
||||
log_info$server_port = id$orig_p;
|
||||
log_info$client_port = id$resp_p;
|
||||
}
|
||||
else
|
||||
{
|
||||
# When a server sends to a non-broadcast
|
||||
# address, Zeek's connection flipping is
|
||||
# in effect and the server is the responder
|
||||
# instead.
|
||||
log_info$server_addr = id$resp_h;
|
||||
log_info$server_port = id$resp_p;
|
||||
log_info$client_port = id$orig_p;
|
||||
}
|
||||
}
|
||||
|
||||
# Only use the client hardware address from the server
|
||||
|
|
|
@ -64,6 +64,9 @@ export {
|
|||
## to are tracked here.
|
||||
pending_commands: PendingCmds;
|
||||
|
||||
## Sequence number of previous command.
|
||||
command_seq: count &default=0;
|
||||
|
||||
## Indicates if the session is in active or passive mode.
|
||||
passive: bool &default=F;
|
||||
|
||||
|
|
|
@ -165,7 +165,7 @@ function set_ftp_session(c: connection)
|
|||
Conn::register_removal_hook(c, finalize_ftp);
|
||||
|
||||
# Add a shim command so the server can respond with some init response.
|
||||
add_pending_cmd(c$ftp$pending_commands, "<init>", "");
|
||||
add_pending_cmd(c$ftp$pending_commands, ++c$ftp$command_seq, "<init>", "");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -261,8 +261,8 @@ event ftp_request(c: connection, command: string, arg: string) &priority=5
|
|||
# attackers.
|
||||
if ( c?$ftp && c$ftp?$cmdarg && c$ftp?$reply_code )
|
||||
{
|
||||
remove_pending_cmd(c$ftp$pending_commands, c$ftp$cmdarg);
|
||||
ftp_message(c);
|
||||
if ( remove_pending_cmd(c$ftp$pending_commands, c$ftp$cmdarg) )
|
||||
ftp_message(c);
|
||||
}
|
||||
|
||||
local id = c$id;
|
||||
|
@ -270,7 +270,7 @@ event ftp_request(c: connection, command: string, arg: string) &priority=5
|
|||
|
||||
# Queue up the new command and argument
|
||||
if ( |c$ftp$pending_commands| < max_pending_commands )
|
||||
add_pending_cmd(c$ftp$pending_commands, command, arg);
|
||||
add_pending_cmd(c$ftp$pending_commands, ++c$ftp$command_seq, command, arg);
|
||||
else
|
||||
Reporter::conn_weird("FTP_too_many_pending_commands", c,
|
||||
cat(|c$ftp$pending_commands|), "FTP");
|
||||
|
|
|
@ -78,9 +78,9 @@ export {
|
|||
};
|
||||
}
|
||||
|
||||
function add_pending_cmd(pc: PendingCmds, cmd: string, arg: string): CmdArg
|
||||
function add_pending_cmd(pc: PendingCmds, seq: count, cmd: string, arg: string): CmdArg
|
||||
{
|
||||
local ca = [$cmd = cmd, $arg = arg, $seq=|pc|+1, $ts=network_time()];
|
||||
local ca = [$cmd = cmd, $arg = arg, $seq=seq, $ts=network_time()];
|
||||
pc[ca$seq] = ca;
|
||||
|
||||
return ca;
|
||||
|
|
|
@ -103,7 +103,7 @@ event http_header(c: connection, is_orig: bool, name: string, value: string) &pr
|
|||
c$http$current_entity$filename = extract_filename_from_content_disposition(value);
|
||||
}
|
||||
else if ( name == "CONTENT-TYPE" &&
|
||||
/[nN][aA][mM][eE][:blank:]*=/ in value )
|
||||
/[nN][aA][mM][eE][[:blank:]]*=/ in value )
|
||||
{
|
||||
c$http$current_entity$filename = extract_filename_from_content_disposition(value);
|
||||
}
|
||||
|
|
|
@ -133,6 +133,12 @@ export {
|
|||
|
||||
## HTTP finalization hook. Remaining HTTP info may get logged when it's called.
|
||||
global finalize_http: Conn::RemovalHook;
|
||||
|
||||
## Only allow that many pending requests on a single connection.
|
||||
## If this number is exceeded, all pending requests are flushed
|
||||
## out and request/response tracking reset to prevent unbounded
|
||||
## state growth.
|
||||
option max_pending_requests = 100;
|
||||
}
|
||||
|
||||
# Add the http state tracking fields to the connection record.
|
||||
|
@ -205,6 +211,47 @@ event http_request(c: connection, method: string, original_URI: string,
|
|||
Conn::register_removal_hook(c, finalize_http);
|
||||
}
|
||||
|
||||
# Request/response tracking exists to account for HTTP pipelining.
|
||||
# It fails if more responses have been seen than requests. If that
|
||||
# happens, just fast-forward current_request such that the next
|
||||
# response matches the in-flight request.
|
||||
if ( c$http_state$current_request < c$http_state$current_response )
|
||||
{
|
||||
Reporter::conn_weird("HTTP_response_before_request", c);
|
||||
c$http_state$current_request = c$http_state$current_response;
|
||||
}
|
||||
|
||||
# Too many requests are pending for which we have not yet observed a
|
||||
# reply. This might be due to excessive HTTP pipelining, one-sided
|
||||
# traffic capture, or the responder side of the HTTP analyzer having
|
||||
# been disabled. In any case, we simply log out all pending requests
|
||||
# to make room for a new one. Any matching of pipelined requests and
|
||||
# responses is most likely totally off anyhow.
|
||||
if ( max_pending_requests > 0 && |c$http_state$pending| > max_pending_requests )
|
||||
{
|
||||
Reporter::conn_weird("HTTP_excessive_pipelining", c);
|
||||
|
||||
if ( c$http_state$current_response == 0 )
|
||||
++c$http_state$current_response;
|
||||
|
||||
while ( c$http_state$current_response < c$http_state$current_request )
|
||||
{
|
||||
local cr = c$http_state$current_response;
|
||||
if ( cr in c$http_state$pending )
|
||||
{
|
||||
Log::write(HTTP::LOG, c$http_state$pending[cr]);
|
||||
delete c$http_state$pending[cr];
|
||||
}
|
||||
else
|
||||
{
|
||||
# The above should have been true...
|
||||
# Reporter::error(fmt("Expected pending request at %d", cr));
|
||||
}
|
||||
|
||||
++c$http_state$current_response;
|
||||
}
|
||||
}
|
||||
|
||||
++c$http_state$current_request;
|
||||
set_state(c, T);
|
||||
|
||||
|
@ -290,7 +337,7 @@ event http_header(c: connection, is_orig: bool, name: string, value: string) &pr
|
|||
{
|
||||
if ( /^[bB][aA][sS][iI][cC] / in value )
|
||||
{
|
||||
local userpass = decode_base64_conn(c$id, sub(value, /[bB][aA][sS][iI][cC][[:blank:]]/, ""));
|
||||
local userpass = decode_base64_conn(c$id, sub(value, /[bB][aA][sS][iI][cC][[:blank:]]+/, ""));
|
||||
local up = split_string(userpass, /:/);
|
||||
if ( |up| >= 2 )
|
||||
{
|
||||
|
|
6
scripts/base/protocols/ldap/__load__.zeek
Normal file
6
scripts/base/protocols/ldap/__load__.zeek
Normal file
|
@ -0,0 +1,6 @@
|
|||
@if ( have_spicy_analyzers() )
|
||||
@load ./spicy-events.zeek
|
||||
@load-sigs ./dpd.sig
|
||||
@load ./consts
|
||||
@load ./main.zeek
|
||||
@endif
|
123
scripts/base/protocols/ldap/consts.zeek
Normal file
123
scripts/base/protocols/ldap/consts.zeek
Normal file
|
@ -0,0 +1,123 @@
|
|||
module LDAP;
|
||||
|
||||
export {
|
||||
const PROTOCOL_OPCODES = { [ LDAP::ProtocolOpcode_BIND_REQUEST ] = "bind", [
|
||||
LDAP::ProtocolOpcode_BIND_RESPONSE ] = "bind", [
|
||||
LDAP::ProtocolOpcode_UNBIND_REQUEST ] = "unbind", [
|
||||
LDAP::ProtocolOpcode_SEARCH_REQUEST ] = "search", [
|
||||
LDAP::ProtocolOpcode_SEARCH_RESULT_ENTRY ] = "search", [
|
||||
LDAP::ProtocolOpcode_SEARCH_RESULT_DONE ] = "search", [
|
||||
LDAP::ProtocolOpcode_MODIFY_REQUEST ] = "modify", [
|
||||
LDAP::ProtocolOpcode_MODIFY_RESPONSE ] = "modify", [
|
||||
LDAP::ProtocolOpcode_ADD_REQUEST ] = "add", [
|
||||
LDAP::ProtocolOpcode_ADD_RESPONSE ] = "add", [
|
||||
LDAP::ProtocolOpcode_DEL_REQUEST ] = "delete", [
|
||||
LDAP::ProtocolOpcode_DEL_RESPONSE ] = "delete", [
|
||||
LDAP::ProtocolOpcode_MOD_DN_REQUEST ] = "modify", [
|
||||
LDAP::ProtocolOpcode_MOD_DN_RESPONSE ] = "modify", [
|
||||
LDAP::ProtocolOpcode_COMPARE_REQUEST ] = "compare", [
|
||||
LDAP::ProtocolOpcode_COMPARE_RESPONSE ] = "compare", [
|
||||
LDAP::ProtocolOpcode_ABANDON_REQUEST ] = "abandon", [
|
||||
LDAP::ProtocolOpcode_SEARCH_RESULT_REFERENCE ] = "search", [
|
||||
LDAP::ProtocolOpcode_EXTENDED_REQUEST ] = "extended", [
|
||||
LDAP::ProtocolOpcode_EXTENDED_RESPONSE ] = "extended", [
|
||||
LDAP::ProtocolOpcode_INTERMEDIATE_RESPONSE ] = "intermediate" }
|
||||
&default="unknown";
|
||||
|
||||
const BIND_SIMPLE = "bind simple";
|
||||
const BIND_SASL = "bind SASL";
|
||||
|
||||
const RESULT_CODES = { [ LDAP::ResultCode_SUCCESS ] = "success", [
|
||||
LDAP::ResultCode_OPERATIONS_ERROR ] = "operations error", [
|
||||
LDAP::ResultCode_PROTOCOL_ERROR ] = "protocol error", [
|
||||
LDAP::ResultCode_TIME_LIMIT_EXCEEDED ] = "time limit exceeded", [
|
||||
LDAP::ResultCode_SIZE_LIMIT_EXCEEDED ] = "size limit exceeded", [
|
||||
LDAP::ResultCode_COMPARE_FALSE ] = "compare false", [
|
||||
LDAP::ResultCode_COMPARE_TRUE ] = "compare true", [
|
||||
LDAP::ResultCode_AUTH_METHOD_NOT_SUPPORTED ] =
|
||||
"auth method not supported", [
|
||||
LDAP::ResultCode_STRONGER_AUTH_REQUIRED ] =
|
||||
"stronger auth required", [ LDAP::ResultCode_PARTIAL_RESULTS ] =
|
||||
"partial results", [ LDAP::ResultCode_REFERRAL ] = "referral", [
|
||||
LDAP::ResultCode_ADMIN_LIMIT_EXCEEDED ] = "admin limit exceeded", [
|
||||
LDAP::ResultCode_UNAVAILABLE_CRITICAL_EXTENSION ] =
|
||||
"unavailable critical extension", [
|
||||
LDAP::ResultCode_CONFIDENTIALITY_REQUIRED ] =
|
||||
"confidentiality required", [ LDAP::ResultCode_SASL_BIND_IN_PROGRESS ] =
|
||||
"SASL bind in progress", [ LDAP::ResultCode_NO_SUCH_ATTRIBUTE ] =
|
||||
"no such attribute", [ LDAP::ResultCode_UNDEFINED_ATTRIBUTE_TYPE ] =
|
||||
"undefined attribute type", [
|
||||
LDAP::ResultCode_INAPPROPRIATE_MATCHING ] =
|
||||
"inappropriate matching", [ LDAP::ResultCode_CONSTRAINT_VIOLATION ] =
|
||||
"constraint violation", [ LDAP::ResultCode_ATTRIBUTE_OR_VALUE_EXISTS ] =
|
||||
"attribute or value exists", [
|
||||
LDAP::ResultCode_INVALID_ATTRIBUTE_SYNTAX ] =
|
||||
"invalid attribute syntax", [ LDAP::ResultCode_NO_SUCH_OBJECT ] =
|
||||
"no such object", [ LDAP::ResultCode_ALIAS_PROBLEM ] =
|
||||
"alias problem", [ LDAP::ResultCode_INVALID_DNSYNTAX ] =
|
||||
"invalid DN syntax", [ LDAP::ResultCode_ALIAS_DEREFERENCING_PROBLEM ] =
|
||||
"alias dereferencing problem", [
|
||||
LDAP::ResultCode_INAPPROPRIATE_AUTHENTICATION ] =
|
||||
"inappropriate authentication", [
|
||||
LDAP::ResultCode_INVALID_CREDENTIALS ] = "invalid credentials", [
|
||||
LDAP::ResultCode_INSUFFICIENT_ACCESS_RIGHTS ] =
|
||||
"insufficient access rights", [ LDAP::ResultCode_BUSY ] = "busy", [
|
||||
LDAP::ResultCode_UNAVAILABLE ] = "unavailable", [
|
||||
LDAP::ResultCode_UNWILLING_TO_PERFORM ] = "unwilling to perform", [
|
||||
LDAP::ResultCode_LOOP_DETECT ] = "loop detect", [
|
||||
LDAP::ResultCode_SORT_CONTROL_MISSING ] = "sort control missing", [
|
||||
LDAP::ResultCode_OFFSET_RANGE_ERROR ] = "offset range error", [
|
||||
LDAP::ResultCode_NAMING_VIOLATION ] = "naming violation", [
|
||||
LDAP::ResultCode_OBJECT_CLASS_VIOLATION ] =
|
||||
"object class violation", [ LDAP::ResultCode_NOT_ALLOWED_ON_NON_LEAF ] =
|
||||
"not allowed on non-leaf", [ LDAP::ResultCode_NOT_ALLOWED_ON_RDN ] =
|
||||
"not allowed on RDN", [ LDAP::ResultCode_ENTRY_ALREADY_EXISTS ] =
|
||||
"entry already exists", [
|
||||
LDAP::ResultCode_OBJECT_CLASS_MODS_PROHIBITED ] =
|
||||
"object class mods prohibited", [ LDAP::ResultCode_RESULTS_TOO_LARGE ] =
|
||||
"results too large", [ LDAP::ResultCode_AFFECTS_MULTIPLE_DSAS ] =
|
||||
"affects multiple DSAs", [ LDAP::ResultCode_CONTROL_ERROR ] =
|
||||
"control error", [ LDAP::ResultCode_OTHER ] = "other", [
|
||||
LDAP::ResultCode_SERVER_DOWN ] = "server down", [
|
||||
LDAP::ResultCode_LOCAL_ERROR ] = "local error", [
|
||||
LDAP::ResultCode_ENCODING_ERROR ] = "encoding error", [
|
||||
LDAP::ResultCode_DECODING_ERROR ] = "decoding error", [
|
||||
LDAP::ResultCode_TIMEOUT ] = "timeout", [
|
||||
LDAP::ResultCode_AUTH_UNKNOWN ] = "auth unknown", [
|
||||
LDAP::ResultCode_FILTER_ERROR ] = "filter error", [
|
||||
LDAP::ResultCode_USER_CANCELED ] = "user canceled", [
|
||||
LDAP::ResultCode_PARAM_ERROR ] = "param error", [
|
||||
LDAP::ResultCode_NO_MEMORY ] = "no memory", [
|
||||
LDAP::ResultCode_CONNECT_ERROR ] = "connect error", [
|
||||
LDAP::ResultCode_NOT_SUPPORTED ] = "not supported", [
|
||||
LDAP::ResultCode_CONTROL_NOT_FOUND ] = "control not found", [
|
||||
LDAP::ResultCode_NO_RESULTS_RETURNED ] = "no results returned", [
|
||||
LDAP::ResultCode_MORE_RESULTS_TO_RETURN ] =
|
||||
"more results to return", [ LDAP::ResultCode_CLIENT_LOOP ] =
|
||||
"client loop", [ LDAP::ResultCode_REFERRAL_LIMIT_EXCEEDED ] =
|
||||
"referral limit exceeded", [ LDAP::ResultCode_INVALID_RESPONSE ] =
|
||||
"invalid response", [ LDAP::ResultCode_AMBIGUOUS_RESPONSE ] =
|
||||
"ambiguous response", [ LDAP::ResultCode_TLS_NOT_SUPPORTED ] =
|
||||
"TLS not supported", [ LDAP::ResultCode_INTERMEDIATE_RESPONSE ] =
|
||||
"intermediate response", [ LDAP::ResultCode_UNKNOWN_TYPE ] =
|
||||
"unknown type", [ LDAP::ResultCode_LCUP_INVALID_DATA ] =
|
||||
"LCUP invalid data", [ LDAP::ResultCode_LCUP_UNSUPPORTED_SCHEME ] =
|
||||
"LCUP unsupported scheme", [ LDAP::ResultCode_LCUP_RELOAD_REQUIRED ] =
|
||||
"LCUP reload required", [ LDAP::ResultCode_CANCELED ] =
|
||||
"canceled", [ LDAP::ResultCode_NO_SUCH_OPERATION ] =
|
||||
"no such operation", [ LDAP::ResultCode_TOO_LATE ] = "too late", [
|
||||
LDAP::ResultCode_CANNOT_CANCEL ] = "cannot cancel", [
|
||||
LDAP::ResultCode_ASSERTION_FAILED ] = "assertion failed", [
|
||||
LDAP::ResultCode_AUTHORIZATION_DENIED ] = "authorization denied" }
|
||||
&default="unknown";
|
||||
|
||||
const SEARCH_SCOPES = { [ LDAP::SearchScope_SEARCH_BASE ] = "base", [
|
||||
LDAP::SearchScope_SEARCH_SINGLE ] = "single", [
|
||||
LDAP::SearchScope_SEARCH_TREE ] = "tree", } &default="unknown";
|
||||
|
||||
const SEARCH_DEREF_ALIASES = { [ LDAP::SearchDerefAlias_DEREF_NEVER ] =
|
||||
"never", [ LDAP::SearchDerefAlias_DEREF_IN_SEARCHING ] =
|
||||
"searching", [ LDAP::SearchDerefAlias_DEREF_FINDING_BASE ] =
|
||||
"finding", [ LDAP::SearchDerefAlias_DEREF_ALWAYS ] = "always", }
|
||||
&default="unknown";
|
||||
}
|
23
scripts/base/protocols/ldap/dpd.sig
Normal file
23
scripts/base/protocols/ldap/dpd.sig
Normal file
|
@ -0,0 +1,23 @@
|
|||
signature dpd_ldap_client_udp {
|
||||
ip-proto == udp
|
||||
payload /^\x30.\x02\x01.\x60/
|
||||
}
|
||||
|
||||
signature dpd_ldap_server_udp {
|
||||
ip-proto == udp
|
||||
payload /^\x30/
|
||||
requires-reverse-signature dpd_ldap_client_udp
|
||||
enable "LDAP_UDP"
|
||||
}
|
||||
|
||||
signature dpd_ldap_client_tcp {
|
||||
ip-proto == tcp
|
||||
payload /^\x30.\x02\x01.\x60/
|
||||
}
|
||||
|
||||
signature dpd_ldap_server_tcp {
|
||||
ip-proto == tcp
|
||||
payload /^\x30/
|
||||
requires-reverse-signature dpd_ldap_client_tcp
|
||||
enable "LDAP_TCP"
|
||||
}
|
358
scripts/base/protocols/ldap/main.zeek
Normal file
358
scripts/base/protocols/ldap/main.zeek
Normal file
|
@ -0,0 +1,358 @@
|
|||
# Copyright (c) 2021 by the Zeek Project. See LICENSE for details.
|
||||
|
||||
@load base/protocols/conn/removal-hooks
|
||||
|
||||
@load ./consts
|
||||
|
||||
module LDAP;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { LDAP_LOG, LDAP_SEARCH_LOG };
|
||||
|
||||
## TCP ports which should be considered for analysis.
|
||||
const ports_tcp = { 389/tcp, 3268/tcp } &redef;
|
||||
|
||||
## UDP ports which should be considered for analysis.
|
||||
const ports_udp = { 389/udp } &redef;
|
||||
|
||||
## Whether clear text passwords are captured or not.
|
||||
option default_capture_password = F;
|
||||
|
||||
## Whether to log LDAP search attributes or not.
|
||||
option default_log_search_attributes = F;
|
||||
|
||||
## Default logging policy hook for LDAP_LOG.
|
||||
global log_policy: Log::PolicyHook;
|
||||
|
||||
## Default logging policy hook for LDAP_SEARCH_LOG.
|
||||
global log_policy_search: Log::PolicyHook;
|
||||
|
||||
## LDAP finalization hook.
|
||||
global finalize_ldap: Conn::RemovalHook;
|
||||
|
||||
#############################################################################
|
||||
# This is the format of ldap.log (ldap operations minus search-related)
|
||||
# Each line represents a unique connection+message_id (requests/responses)
|
||||
type MessageInfo: record {
|
||||
# Timestamp for when the event happened.
|
||||
ts: time &log;
|
||||
|
||||
# Unique ID for the connection.
|
||||
uid: string &log;
|
||||
|
||||
# The connection's 4-tuple of endpoint addresses/ports.
|
||||
id: conn_id &log;
|
||||
|
||||
# Message ID
|
||||
message_id: int &log &optional;
|
||||
|
||||
# LDAP version
|
||||
version: int &log &optional;
|
||||
|
||||
# normalized operations (e.g., bind_request and bind_response to "bind")
|
||||
opcodes: set[string] &log &optional;
|
||||
|
||||
# Result code(s)
|
||||
results: set[string] &log &optional;
|
||||
|
||||
# result diagnostic message(s)
|
||||
diagnostic_messages: vector of string &log &optional;
|
||||
|
||||
# object(s)
|
||||
objects: vector of string &log &optional;
|
||||
|
||||
# argument(s)
|
||||
arguments: vector of string &log &optional;
|
||||
};
|
||||
|
||||
#############################################################################
|
||||
# This is the format of ldap_search.log (search-related messages only)
|
||||
# Each line represents a unique connection+message_id (requests/responses)
|
||||
type SearchInfo: record {
|
||||
# Timestamp for when the event happened.
|
||||
ts: time &log;
|
||||
|
||||
# Unique ID for the connection.
|
||||
uid: string &log;
|
||||
|
||||
# The connection's 4-tuple of endpoint addresses/ports.
|
||||
id: conn_id &log;
|
||||
|
||||
# Message ID
|
||||
message_id: int &log &optional;
|
||||
|
||||
# sets of search scope and deref alias
|
||||
scopes: set[string] &log &optional;
|
||||
derefs: set[string] &log &optional;
|
||||
|
||||
# base search objects
|
||||
base_objects: vector of string &log &optional;
|
||||
|
||||
# number of results returned
|
||||
result_count: count &log &optional;
|
||||
|
||||
# Result code (s)
|
||||
results: set[string] &log &optional;
|
||||
|
||||
# result diagnostic message(s)
|
||||
diagnostic_messages: vector of string &log &optional;
|
||||
|
||||
# a string representation of the search filter used in the query
|
||||
filter: string &log &optional;
|
||||
|
||||
# a list of attributes that were returned in the search
|
||||
attributes: vector of string &log &optional;
|
||||
};
|
||||
|
||||
type State: record {
|
||||
messages: table[int] of MessageInfo &optional;
|
||||
searches: table[int] of SearchInfo &optional;
|
||||
};
|
||||
|
||||
# Event that can be handled to access the ldap record as it is sent on
|
||||
# to the logging framework.
|
||||
global log_ldap: event(rec: LDAP::MessageInfo);
|
||||
global log_ldap_search: event(rec: LDAP::SearchInfo);
|
||||
}
|
||||
|
||||
redef record connection += {
|
||||
ldap: State &optional;
|
||||
};
|
||||
|
||||
redef likely_server_ports += { LDAP::ports_tcp, LDAP::ports_udp };
|
||||
|
||||
#############################################################################
|
||||
global OPCODES_FINISHED: set[LDAP::ProtocolOpcode] = { LDAP::ProtocolOpcode_BIND_RESPONSE,
|
||||
LDAP::ProtocolOpcode_UNBIND_REQUEST,
|
||||
LDAP::ProtocolOpcode_SEARCH_RESULT_DONE,
|
||||
LDAP::ProtocolOpcode_MODIFY_RESPONSE,
|
||||
LDAP::ProtocolOpcode_ADD_RESPONSE,
|
||||
LDAP::ProtocolOpcode_DEL_RESPONSE,
|
||||
LDAP::ProtocolOpcode_MOD_DN_RESPONSE,
|
||||
LDAP::ProtocolOpcode_COMPARE_RESPONSE,
|
||||
LDAP::ProtocolOpcode_ABANDON_REQUEST,
|
||||
LDAP::ProtocolOpcode_EXTENDED_RESPONSE };
|
||||
|
||||
global OPCODES_SEARCH: set[LDAP::ProtocolOpcode] = { LDAP::ProtocolOpcode_SEARCH_REQUEST,
|
||||
LDAP::ProtocolOpcode_SEARCH_RESULT_ENTRY,
|
||||
LDAP::ProtocolOpcode_SEARCH_RESULT_DONE,
|
||||
LDAP::ProtocolOpcode_SEARCH_RESULT_REFERENCE };
|
||||
|
||||
#############################################################################
|
||||
event zeek_init() &priority=5 {
|
||||
Analyzer::register_for_ports(Analyzer::ANALYZER_LDAP_TCP, LDAP::ports_tcp);
|
||||
Analyzer::register_for_ports(Analyzer::ANALYZER_LDAP_UDP, LDAP::ports_udp);
|
||||
|
||||
Log::create_stream(LDAP::LDAP_LOG, [$columns=MessageInfo, $ev=log_ldap, $path="ldap", $policy=log_policy]);
|
||||
Log::create_stream(LDAP::LDAP_SEARCH_LOG, [$columns=SearchInfo, $ev=log_ldap_search, $path="ldap_search", $policy=log_policy_search]);
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
function set_session(c: connection, message_id: int, opcode: LDAP::ProtocolOpcode) {
|
||||
|
||||
if (! c?$ldap ) {
|
||||
c$ldap = State();
|
||||
Conn::register_removal_hook(c, finalize_ldap);
|
||||
}
|
||||
|
||||
if (! c$ldap?$messages )
|
||||
c$ldap$messages = table();
|
||||
|
||||
if (! c$ldap?$searches )
|
||||
c$ldap$searches = table();
|
||||
|
||||
if ((opcode in OPCODES_SEARCH) && (message_id !in c$ldap$searches)) {
|
||||
c$ldap$searches[message_id] = [$ts=network_time(),
|
||||
$uid=c$uid,
|
||||
$id=c$id,
|
||||
$message_id=message_id,
|
||||
$result_count=0];
|
||||
|
||||
} else if ((opcode !in OPCODES_SEARCH) && (message_id !in c$ldap$messages)) {
|
||||
c$ldap$messages[message_id] = [$ts=network_time(),
|
||||
$uid=c$uid,
|
||||
$id=c$id,
|
||||
$message_id=message_id];
|
||||
}
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
event LDAP::message(c: connection,
|
||||
message_id: int,
|
||||
opcode: LDAP::ProtocolOpcode,
|
||||
result: LDAP::ResultCode,
|
||||
matched_dn: string,
|
||||
diagnostic_message: string,
|
||||
object: string,
|
||||
argument: string) {
|
||||
|
||||
if (opcode == LDAP::ProtocolOpcode_SEARCH_RESULT_DONE) {
|
||||
set_session(c, message_id, opcode);
|
||||
|
||||
local searches = c$ldap$searches[message_id];
|
||||
|
||||
if ( result != LDAP::ResultCode_Undef ) {
|
||||
if ( ! searches?$results )
|
||||
searches$results = set();
|
||||
add searches$results[RESULT_CODES[result]];
|
||||
}
|
||||
|
||||
if ( diagnostic_message != "" ) {
|
||||
if ( ! searches?$diagnostic_messages )
|
||||
searches$diagnostic_messages = vector();
|
||||
searches$diagnostic_messages += diagnostic_message;
|
||||
}
|
||||
|
||||
Log::write(LDAP::LDAP_SEARCH_LOG, searches);
|
||||
delete c$ldap$searches[message_id];
|
||||
|
||||
} else if (opcode !in OPCODES_SEARCH) {
|
||||
set_session(c, message_id, opcode);
|
||||
|
||||
local messages = c$ldap$messages[message_id];
|
||||
|
||||
if ( ! messages?$opcodes )
|
||||
messages$opcodes = set();
|
||||
add messages$opcodes[PROTOCOL_OPCODES[opcode]];
|
||||
|
||||
if ( result != LDAP::ResultCode_Undef ) {
|
||||
if ( ! messages?$results )
|
||||
messages$results = set();
|
||||
add messages$results[RESULT_CODES[result]];
|
||||
}
|
||||
|
||||
if ( diagnostic_message != "" ) {
|
||||
if ( ! messages?$diagnostic_messages )
|
||||
messages$diagnostic_messages = vector();
|
||||
messages$diagnostic_messages += diagnostic_message;
|
||||
}
|
||||
|
||||
if ( object != "" ) {
|
||||
if ( ! messages?$objects )
|
||||
messages$objects = vector();
|
||||
messages$objects += object;
|
||||
}
|
||||
|
||||
if ( argument != "" ) {
|
||||
if ( ! messages?$arguments )
|
||||
messages$arguments = vector();
|
||||
if ("bind simple" in messages$opcodes && !default_capture_password)
|
||||
messages$arguments += "REDACTED";
|
||||
else
|
||||
messages$arguments += argument;
|
||||
}
|
||||
|
||||
if (opcode in OPCODES_FINISHED) {
|
||||
|
||||
if ((BIND_SIMPLE in messages$opcodes) ||
|
||||
(BIND_SASL in messages$opcodes)) {
|
||||
# don't have both "bind" and "bind <method>" in the operations list
|
||||
delete messages$opcodes[PROTOCOL_OPCODES[LDAP::ProtocolOpcode_BIND_REQUEST]];
|
||||
}
|
||||
|
||||
Log::write(LDAP::LDAP_LOG, messages);
|
||||
delete c$ldap$messages[message_id];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
event LDAP::search_request(c: connection,
|
||||
message_id: int,
|
||||
base_object: string,
|
||||
scope: LDAP::SearchScope,
|
||||
deref: LDAP::SearchDerefAlias,
|
||||
size_limit: int,
|
||||
time_limit: int,
|
||||
types_only: bool,
|
||||
filter: string,
|
||||
attributes: vector of string) {
|
||||
|
||||
set_session(c, message_id, LDAP::ProtocolOpcode_SEARCH_REQUEST);
|
||||
|
||||
if ( scope != LDAP::SearchScope_Undef ) {
|
||||
if ( ! c$ldap$searches[message_id]?$scopes )
|
||||
c$ldap$searches[message_id]$scopes = set();
|
||||
add c$ldap$searches[message_id]$scopes[SEARCH_SCOPES[scope]];
|
||||
}
|
||||
|
||||
if ( deref != LDAP::SearchDerefAlias_Undef ) {
|
||||
if ( ! c$ldap$searches[message_id]?$derefs )
|
||||
c$ldap$searches[message_id]$derefs = set();
|
||||
add c$ldap$searches[message_id]$derefs[SEARCH_DEREF_ALIASES[deref]];
|
||||
}
|
||||
|
||||
if ( base_object != "" ) {
|
||||
if ( ! c$ldap$searches[message_id]?$base_objects )
|
||||
c$ldap$searches[message_id]$base_objects = vector();
|
||||
c$ldap$searches[message_id]$base_objects += base_object;
|
||||
}
|
||||
c$ldap$searches[message_id]$filter = filter;
|
||||
|
||||
if ( default_log_search_attributes ) {
|
||||
c$ldap$searches[message_id]$attributes = attributes;
|
||||
}
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
event LDAP::search_result(c: connection,
|
||||
message_id: int,
|
||||
object_name: string) {
|
||||
|
||||
set_session(c, message_id, LDAP::ProtocolOpcode_SEARCH_RESULT_ENTRY);
|
||||
|
||||
c$ldap$searches[message_id]$result_count += 1;
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
event LDAP::bind_request(c: connection,
|
||||
message_id: int,
|
||||
version: int,
|
||||
name: string,
|
||||
authType: LDAP::BindAuthType,
|
||||
authInfo: string) {
|
||||
set_session(c, message_id, LDAP::ProtocolOpcode_BIND_REQUEST);
|
||||
|
||||
if ( ! c$ldap$messages[message_id]?$version )
|
||||
c$ldap$messages[message_id]$version = version;
|
||||
|
||||
if ( ! c$ldap$messages[message_id]?$opcodes )
|
||||
c$ldap$messages[message_id]$opcodes = set();
|
||||
|
||||
if (authType == LDAP::BindAuthType_BIND_AUTH_SIMPLE) {
|
||||
add c$ldap$messages[message_id]$opcodes[BIND_SIMPLE];
|
||||
} else if (authType == LDAP::BindAuthType_BIND_AUTH_SASL) {
|
||||
add c$ldap$messages[message_id]$opcodes[BIND_SASL];
|
||||
}
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
hook finalize_ldap(c: connection) {
|
||||
# log any "pending" unlogged LDAP messages/searches
|
||||
|
||||
if ( c$ldap?$messages && (|c$ldap$messages| > 0) ) {
|
||||
for ( [mid], m in c$ldap$messages ) {
|
||||
if (mid > 0) {
|
||||
|
||||
if ((BIND_SIMPLE in m$opcodes) || (BIND_SASL in m$opcodes)) {
|
||||
# don't have both "bind" and "bind <method>" in the operations list
|
||||
delete m$opcodes[PROTOCOL_OPCODES[LDAP::ProtocolOpcode_BIND_REQUEST]];
|
||||
}
|
||||
|
||||
Log::write(LDAP::LDAP_LOG, m);
|
||||
}
|
||||
}
|
||||
delete c$ldap$messages;
|
||||
}
|
||||
|
||||
if ( c$ldap?$searches && (|c$ldap$searches| > 0) ) {
|
||||
for ( [mid], s in c$ldap$searches ) {
|
||||
if (mid > 0) {
|
||||
Log::write(LDAP::LDAP_SEARCH_LOG, s);
|
||||
}
|
||||
}
|
||||
delete c$ldap$searches;
|
||||
}
|
||||
|
||||
}
|
100
scripts/base/protocols/ldap/spicy-events.zeek
Normal file
100
scripts/base/protocols/ldap/spicy-events.zeek
Normal file
|
@ -0,0 +1,100 @@
|
|||
##! Events generated by the LDAP analyzer.
|
||||
##!
|
||||
##! See See `RFC4511 <https://tools.ietf.org/html/rfc4511>`__.
|
||||
|
||||
## Event generated for each LDAPMessage (either direction).
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## message_id: The messageID element.
|
||||
##
|
||||
## opcode: The protocolOp field in the message.
|
||||
##
|
||||
## result: The result code if the message contains a result.
|
||||
##
|
||||
## matched_dn: The DN if the message contains a result.
|
||||
##
|
||||
## diagnostic_message: Diagnostic message if the LDAP message contains a result.
|
||||
##
|
||||
## object: The object name this message refers to.
|
||||
##
|
||||
## argument: Additional arguments this message includes.
|
||||
global LDAP::message: event(
|
||||
c: connection,
|
||||
message_id: int,
|
||||
opcode: LDAP::ProtocolOpcode,
|
||||
result: LDAP::ResultCode,
|
||||
matched_dn: string,
|
||||
diagnostic_message: string,
|
||||
object: string,
|
||||
argument: string
|
||||
);
|
||||
|
||||
## Event generated for each LDAPMessage containing a BindRequest.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## message_id: The messageID element.
|
||||
##
|
||||
## version: The version field in the BindRequest.
|
||||
##
|
||||
## name: The name field in the BindRequest.
|
||||
##
|
||||
## auth_type: The auth type field in the BindRequest.
|
||||
##
|
||||
## auth_info: Additional information related to the used auth type.
|
||||
global LDAP::bind_request: event(
|
||||
c: connection,
|
||||
message_id: int,
|
||||
version: int,
|
||||
name: string,
|
||||
auth_type: LDAP::BindAuthType,
|
||||
auth_info: string
|
||||
);
|
||||
|
||||
## Event generated for each LDAPMessage containing a SearchRequest.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## message_id: The messageID element.
|
||||
##
|
||||
## base_object: The baseObject field in the SearchRequest.
|
||||
##
|
||||
## scope: The scope field in the SearchRequest.
|
||||
##
|
||||
## deref_alias: The derefAlias field in the SearchRequest
|
||||
##
|
||||
## size_limit: The sizeLimit field in the SearchRequest.
|
||||
##
|
||||
## time_limit: The timeLimit field in the SearchRequest.
|
||||
##
|
||||
## types_only: The typesOnly field in the SearchRequest.
|
||||
##
|
||||
## filter: The string representation of the filter field in the SearchRequest.
|
||||
##
|
||||
## attributes: Additional attributes of the SearchRequest.
|
||||
global LDAP::search_request: event (
|
||||
c: connection,
|
||||
message_id: int,
|
||||
base_object: string,
|
||||
scope: LDAP::SearchScope,
|
||||
deref: LDAP::SearchDerefAlias,
|
||||
size_limit: int,
|
||||
time_limit: int,
|
||||
types_only: bool,
|
||||
filter: string,
|
||||
attributes: vector of string
|
||||
);
|
||||
|
||||
## Event generated for each SearchResultEntry in LDAP messages.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## message_id: The messageID element.
|
||||
##
|
||||
## object_name: The object name in the SearchResultEntry.
|
||||
global LDAP::search_result: event (
|
||||
c: connection,
|
||||
message_id: int,
|
||||
object_name: string
|
||||
);
|
|
@ -79,4 +79,3 @@ event modbus_exception(c: connection, headers: ModbusHeaders, code: count) &prio
|
|||
Log::write(LOG, c$modbus);
|
||||
delete c$modbus$exception;
|
||||
}
|
||||
|
||||
|
|
5
scripts/base/protocols/quic/__load__.zeek
Normal file
5
scripts/base/protocols/quic/__load__.zeek
Normal file
|
@ -0,0 +1,5 @@
|
|||
@ifdef ( Analyzer::ANALYZER_QUIC )
|
||||
@load ./spicy-events
|
||||
@load ./consts
|
||||
@load ./main
|
||||
@endif
|
7
scripts/base/protocols/quic/consts.zeek
Normal file
7
scripts/base/protocols/quic/consts.zeek
Normal file
|
@ -0,0 +1,7 @@
|
|||
module QUIC;
|
||||
|
||||
export {
|
||||
const version_strings: table[count] of string = {
|
||||
[0x00000001] = "1",
|
||||
} &default=function(version: count): string { return fmt("unknown-%x", version); };
|
||||
}
|
221
scripts/base/protocols/quic/main.zeek
Normal file
221
scripts/base/protocols/quic/main.zeek
Normal file
|
@ -0,0 +1,221 @@
|
|||
##! Initial idea for a quic.log.
|
||||
|
||||
@load base/frameworks/notice/weird
|
||||
@load base/protocols/conn/removal-hooks
|
||||
|
||||
@load ./consts
|
||||
|
||||
module QUIC;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { LOG };
|
||||
|
||||
type Info: record {
|
||||
## Timestamp of first QUIC packet for this entry.
|
||||
ts: time &log;
|
||||
## Unique ID for the connection.
|
||||
uid: string &log;
|
||||
## The connection's 4-tuple of endpoint addresses/ports.
|
||||
id: conn_id &log;
|
||||
|
||||
## QUIC version as found in the first INITIAL packet from
|
||||
## the client.
|
||||
version: string &log;
|
||||
|
||||
## First Destination Connection ID used by client. This is
|
||||
## random and unpredictable, but used for packet protection
|
||||
## by client and server.
|
||||
client_initial_dcid: string &log &optional;
|
||||
|
||||
## Server chosen Connection ID usually from server's first
|
||||
## INITIAL packet. This is to be used by the client in
|
||||
## subsequent packets.
|
||||
server_scid: string &log &optional;
|
||||
|
||||
## Server name extracted from SNI extension in ClientHello
|
||||
## packet if available.
|
||||
server_name: string &log &optional;
|
||||
|
||||
## First protocol extracted from ALPN extension in ClientHello
|
||||
## packet if available.
|
||||
client_protocol: string &log &optional;
|
||||
|
||||
## Experimental QUIC history.
|
||||
##
|
||||
## Letters have the following meaning with client-sent
|
||||
## letters being capitalized:
|
||||
##
|
||||
## ====== ====================================================
|
||||
## Letter Meaning
|
||||
## ====== ====================================================
|
||||
## I INIT packet
|
||||
## H HANDSHAKE packet
|
||||
## Z 0RTT packet
|
||||
## R RETRY packet
|
||||
## C CONNECTION_CLOSE packet
|
||||
## S SSL Client/Server Hello
|
||||
## ====== ====================================================
|
||||
history: string &log &default="";
|
||||
|
||||
# Internal state for the history field.
|
||||
history_state: vector of string;
|
||||
|
||||
# Internal state if this record has already been logged.
|
||||
logged: bool &default=F;
|
||||
};
|
||||
|
||||
global log_quic: event(rec: Info);
|
||||
|
||||
global log_policy: Log::PolicyHook;
|
||||
|
||||
global finalize_quic: Conn::RemovalHook;
|
||||
|
||||
## The maximum length of the history field.
|
||||
option max_history_length = 100;
|
||||
}
|
||||
|
||||
redef record connection += {
|
||||
# XXX: We may have multiple QUIC connections with different
|
||||
# Connection ID over the same UDP connection.
|
||||
quic: Info &optional;
|
||||
};
|
||||
|
||||
# Faster to modify here than re-compiling .evt files.
|
||||
const quic_ports = {
|
||||
443/udp, # HTTP3-over-QUIC
|
||||
853/udp, # DNS-over-QUIC
|
||||
784/udp, # DNS-over-QUIC early
|
||||
};
|
||||
|
||||
function add_to_history(c: connection, is_orig: bool, what: string)
|
||||
{
|
||||
if ( |c$quic$history_state| == max_history_length )
|
||||
return;
|
||||
|
||||
c$quic$history_state += is_orig ? to_upper(what[0]) : to_lower(what[0]);
|
||||
|
||||
if ( |c$quic$history_state| == max_history_length )
|
||||
Reporter::conn_weird("QUIC_max_history_length_reached", c);
|
||||
}
|
||||
|
||||
function log_record(quic: Info)
|
||||
{
|
||||
quic$history = join_string_vec(quic$history_state, "");
|
||||
Log::write(LOG, quic);
|
||||
quic$logged = T;
|
||||
}
|
||||
|
||||
function set_conn(c: connection, is_orig: bool, version: count, dcid: string, scid: string)
|
||||
{
|
||||
if ( ! c?$quic )
|
||||
{
|
||||
c$quic = Info(
|
||||
$ts=network_time(),
|
||||
$uid=c$uid,
|
||||
$id=c$id,
|
||||
$version=version_strings[version],
|
||||
);
|
||||
|
||||
Conn::register_removal_hook(c, finalize_quic);
|
||||
}
|
||||
|
||||
if ( is_orig && |dcid| > 0 && ! c$quic?$client_initial_dcid )
|
||||
c$quic$client_initial_dcid = bytestring_to_hexstr(dcid);
|
||||
|
||||
if ( ! is_orig && |scid| > 0 )
|
||||
c$quic$server_scid = bytestring_to_hexstr(scid);
|
||||
}
|
||||
|
||||
event QUIC::initial_packet(c: connection, is_orig: bool, version: count, dcid: string, scid: string)
|
||||
{
|
||||
set_conn(c, is_orig, version, dcid, scid);
|
||||
add_to_history(c, is_orig, "INIT");
|
||||
}
|
||||
|
||||
event QUIC::handshake_packet(c: connection, is_orig: bool, version: count, dcid: string, scid: string)
|
||||
{
|
||||
set_conn(c, is_orig, version, dcid, scid);
|
||||
add_to_history(c, is_orig, "HANDSHAKE");
|
||||
}
|
||||
|
||||
event QUIC::zero_rtt_packet(c: connection, is_orig: bool, version: count, dcid: string, scid: string)
|
||||
{
|
||||
set_conn(c, is_orig, version, dcid, scid);
|
||||
add_to_history(c, is_orig, "ZeroRTT");
|
||||
}
|
||||
|
||||
# RETRY packets trigger a log entry and state reset.
|
||||
event QUIC::retry_packet(c: connection, is_orig: bool, version: count, dcid: string, scid: string, retry_token: string, integrity_tag: string)
|
||||
{
|
||||
if ( ! c?$quic )
|
||||
set_conn(c, is_orig, version, dcid, scid);
|
||||
|
||||
add_to_history(c, is_orig, "RETRY");
|
||||
|
||||
log_record(c$quic);
|
||||
|
||||
delete c$quic;
|
||||
}
|
||||
|
||||
# Upon a connection_close_frame(), if any c$quic state is pending to be logged, do so
|
||||
# now and prepare for a new entry.
|
||||
event QUIC::connection_close_frame(c: connection, is_orig: bool, version: count, dcid: string, scid: string, error_code: count, reason_phrase: string)
|
||||
{
|
||||
if ( ! c?$quic )
|
||||
return;
|
||||
|
||||
add_to_history(c, is_orig, "CONNECTION_CLOSE");
|
||||
|
||||
log_record(c$quic);
|
||||
|
||||
delete c$quic;
|
||||
}
|
||||
|
||||
event ssl_extension_server_name(c: connection, is_client: bool, names: string_vec) &priority=5
|
||||
{
|
||||
if ( is_client && c?$quic && |names| > 0 )
|
||||
c$quic$server_name = names[0];
|
||||
}
|
||||
|
||||
event ssl_extension_application_layer_protocol_negotiation(c: connection, is_client: bool, protocols: string_vec)
|
||||
{
|
||||
if ( c?$quic && is_client )
|
||||
{
|
||||
c$quic$client_protocol = protocols[0];
|
||||
if ( |protocols| > 1 )
|
||||
# Probably not overly weird, but the quic.log only
|
||||
# works with the first one in the hope to avoid
|
||||
# vector or concatenation.
|
||||
Reporter::conn_weird("QUIC_many_protocols", c, cat(protocols));
|
||||
}
|
||||
}
|
||||
|
||||
event ssl_client_hello(c: connection, version: count, record_version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec, comp_methods: index_vec)
|
||||
{
|
||||
if ( ! c?$quic )
|
||||
return;
|
||||
|
||||
add_to_history(c, T, "SSL");
|
||||
}
|
||||
|
||||
event ssl_server_hello(c: connection, version: count, record_version: count, possible_ts: time, server_random: string, session_id: string, cipher: count, comp_method: count) &priority=-5
|
||||
{
|
||||
if ( ! c?$quic )
|
||||
return;
|
||||
|
||||
add_to_history(c, F, "SSL");
|
||||
}
|
||||
|
||||
hook finalize_quic(c: connection)
|
||||
{
|
||||
if ( ! c?$quic || c$quic$logged )
|
||||
return;
|
||||
|
||||
log_record(c$quic);
|
||||
}
|
||||
|
||||
event zeek_init()
|
||||
{
|
||||
Log::create_stream(LOG, [$columns=Info, $ev=log_quic, $path="quic", $policy=log_policy]);
|
||||
Analyzer::register_for_ports(Analyzer::ANALYZER_QUIC, quic_ports);
|
||||
}
|
82
scripts/base/protocols/quic/spicy-events.zeek
Normal file
82
scripts/base/protocols/quic/spicy-events.zeek
Normal file
|
@ -0,0 +1,82 @@
|
|||
##! Events generated by the QUIC analyzer.
|
||||
##!
|
||||
##! See See `RFC9000 <https://tools.ietf.org/html/rfc9000>`__.
|
||||
|
||||
## Generated for a QUIC Initial packet.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## is_orig: True if the packet is from the the connection's originator.
|
||||
##
|
||||
## version: The Version field.
|
||||
##
|
||||
## dcid: The Destination Connection ID field.
|
||||
##
|
||||
## scid: The Source Connection ID field.
|
||||
##
|
||||
global QUIC::initial_packet: event(c: connection, is_orig: bool, version: count, dcid: string, scid: string);
|
||||
|
||||
|
||||
## Generated for a QUIC Retry packet.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## is_orig: True if the packet is from the the connection's originator.
|
||||
##
|
||||
## version: The Version field.
|
||||
##
|
||||
## dcid: The Destination Connection ID field.
|
||||
##
|
||||
## scid: The Source Connection ID field.
|
||||
##
|
||||
## retry_token: The Retry Token field.
|
||||
##
|
||||
## integrity_tag: The Retry Integrity Tag field.
|
||||
global QUIC::retry_packet: event(c: connection, is_orig: bool, version: count, dcid: string, scid: string, retry_token: string, retry_integrity_tag: string);
|
||||
|
||||
|
||||
## Generated for a QUIC Handshake packet.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## is_orig: True if the packet is from the the connection's originator.
|
||||
##
|
||||
## version: The Version field.
|
||||
##
|
||||
## dcid: The Destination Connection ID field.
|
||||
##
|
||||
## scid: The Source Connection ID field.
|
||||
global QUIC::handshake_packet: event(c: connection, is_orig: bool, version: count, dcid: string, scid: string);
|
||||
|
||||
## Generated for a QUIC 0-RTT packet.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## is_orig: True if the packet is from the the connection's originator.
|
||||
##
|
||||
## version: The Version field.
|
||||
##
|
||||
## dcid: The Destination Connection ID field.
|
||||
##
|
||||
## scid: The Source Connection ID field.
|
||||
global QUIC::zero_rtt_packet: event(c: connection, is_orig: bool, version: count, dcid: string, scid: string);
|
||||
|
||||
|
||||
## Generated for a QUIC CONNECTION_CLOSE frame.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## is_orig: True if the packet is from the the connection's originator.
|
||||
##
|
||||
## version: The Version field.
|
||||
##
|
||||
## dcid: The Destination Connection ID field.
|
||||
##
|
||||
## scid: The Source Connection ID field.
|
||||
##
|
||||
## error_code: Count indicating the reason for closing this connection.
|
||||
##
|
||||
## reason_phrase: Additional diagnostic information for the closure.
|
||||
##
|
||||
## .. note:: Packets with CONNECTION_CLOSE frames are usually encrypted after connection establishment and not visible to Zeek.
|
||||
global QUIC::connection_close_frame: event(c: connection, is_orig: bool, version: count, dcid: string, scid: string, error_code: count, reason_phrase: string);
|
|
@ -67,7 +67,7 @@ event mime_one_header(c: connection, h: mime_header_rec) &priority=5
|
|||
c$smtp$entity$filename = extract_filename_from_content_disposition(h$value);
|
||||
|
||||
if ( h$name == "CONTENT-TYPE" &&
|
||||
/[nN][aA][mM][eE][:blank:]*=/ in h$value )
|
||||
/[nN][aA][mM][eE][[:blank:]]*=/ in h$value )
|
||||
c$smtp$entity$filename = extract_filename_from_content_disposition(h$value);
|
||||
}
|
||||
|
||||
|
|
|
@ -143,6 +143,10 @@ export {
|
|||
## (especially with large file transfers).
|
||||
option disable_analyzer_after_detection = T;
|
||||
|
||||
## Maximum length of the ssl_history field to prevent unbounded
|
||||
## growth when the parser is running into unexpected situations.
|
||||
option max_ssl_history_length = 100;
|
||||
|
||||
## Delays an SSL record for a specific token: the record will not be
|
||||
## logged as long as the token exists or until 15 seconds elapses.
|
||||
global delay_log: function(info: Info, token: string);
|
||||
|
@ -208,10 +212,16 @@ function set_session(c: connection)
|
|||
|
||||
function add_to_history(c: connection, is_client: bool, char: string)
|
||||
{
|
||||
if ( |c$ssl$ssl_history| == max_ssl_history_length )
|
||||
return;
|
||||
|
||||
if ( is_client )
|
||||
c$ssl$ssl_history = c$ssl$ssl_history+to_upper(char);
|
||||
else
|
||||
c$ssl$ssl_history = c$ssl$ssl_history+to_lower(char);
|
||||
|
||||
if ( |c$ssl$ssl_history| == max_ssl_history_length )
|
||||
Reporter::conn_weird("SSL_max_ssl_history_length_reached", c);
|
||||
}
|
||||
|
||||
function delay_log(info: Info, token: string)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue