diff --git a/scripts/base/frameworks/notice/weird.zeek b/scripts/base/frameworks/notice/weird.zeek index 47a6b6dde0..919f683123 100644 --- a/scripts/base/frameworks/notice/weird.zeek +++ b/scripts/base/frameworks/notice/weird.zeek @@ -210,6 +210,7 @@ export { ["spontaneous_FIN"] = ACTION_IGNORE, ["spontaneous_RST"] = ACTION_IGNORE, ["SMB_parsing_error"] = ACTION_LOG, + ["SMB_discarded_messages_state"] = ACTION_LOG, ["no_smb_session_using_parsesambamsg"] = ACTION_LOG, ["smb_andx_command_failed_to_parse"] = ACTION_LOG, ["smb_tree_connect_andx_response_without_tree"] = ACTION_LOG_PER_CONN, diff --git a/scripts/base/protocols/smb/main.zeek b/scripts/base/protocols/smb/main.zeek index ed129f8ce1..91e27acffa 100644 --- a/scripts/base/protocols/smb/main.zeek +++ b/scripts/base/protocols/smb/main.zeek @@ -44,6 +44,13 @@ export { PRINT_CLOSE, }; + ## Whether to reset a connection's SMB script state whenever a + ## :zeek:see:`smb2_discarded_messages_state` event is raised. + ## + ## This setting protects from unbounded script state growth in + ## environments with high capture loss or traffic anomalies. + option enable_clear_script_state = T; + ## This record is for the smb_files.log type FileInfo: record { ## Time when the file was first discovered. diff --git a/scripts/base/protocols/smb/smb2-main.zeek b/scripts/base/protocols/smb/smb2-main.zeek index 8ccaa2df84..31f0a7704a 100644 --- a/scripts/base/protocols/smb/smb2-main.zeek +++ b/scripts/base/protocols/smb/smb2-main.zeek @@ -1,3 +1,5 @@ +@load base/frameworks/notice/weird + @load ./main module SMB2; @@ -344,3 +346,25 @@ event smb2_close_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID) #Reporter::warning("attempting to close an unknown file!"); } } + +event smb2_discarded_messages_state(c: connection, state: string) + { + if ( ! c?$smb_state ) + return; + + local addl = fmt("state=%s fid_map=%s tid_map=%s pending_cmds=%s pipe_map=%s", + state, |c$smb_state$fid_map|, |c$smb_state$tid_map|, + |c$smb_state$pending_cmds|, |c$smb_state$pipe_map|); + Reporter::conn_weird("SMB_discarded_messages_state", c, addl, "SMB2"); + + if ( ! SMB::enable_clear_script_state ) + return; + + # Wipe out script-level state for this connection. + c$smb_state$fid_map = table(); + c$smb_state$pending_cmds = table(); + # Not expected to grow overly large and the original + # zeek-smb-clear-state package didn't reset these either. + # c$smb_state$tid_map = table(); + # c$smb_state$pipe_map = table(); + } diff --git a/testing/btest/Baseline/plugins.hooks/output b/testing/btest/Baseline/plugins.hooks/output index 97b230190e..8ab256052c 100644 --- a/testing/btest/Baseline/plugins.hooks/output +++ b/testing/btest/Baseline/plugins.hooks/output @@ -575,6 +575,7 @@ 0.000000 MetaHookPost CallFunction(Option::set_change_handler, , (RDP::disable_analyzer_after_detection, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100)) -> 0.000000 MetaHookPost CallFunction(Option::set_change_handler, , (RDP::rdp_check_interval, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100)) -> 0.000000 MetaHookPost CallFunction(Option::set_change_handler, , (SIP::sip_methods, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100)) -> +0.000000 MetaHookPost CallFunction(Option::set_change_handler, , (SMB::enable_clear_script_state, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100)) -> 0.000000 MetaHookPost CallFunction(Option::set_change_handler, , (SMB::logged_file_actions, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100)) -> 0.000000 MetaHookPost CallFunction(Option::set_change_handler, , (SMTP::mail_path_capture, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100)) -> 0.000000 MetaHookPost CallFunction(Option::set_change_handler, , (SMTP::mail_transaction_validation, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100)) -> @@ -2194,6 +2195,7 @@ 0.000000 MetaHookPre CallFunction(Option::set_change_handler, , (RDP::disable_analyzer_after_detection, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100)) 0.000000 MetaHookPre CallFunction(Option::set_change_handler, , (RDP::rdp_check_interval, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100)) 0.000000 MetaHookPre CallFunction(Option::set_change_handler, , (SIP::sip_methods, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100)) +0.000000 MetaHookPre CallFunction(Option::set_change_handler, , (SMB::enable_clear_script_state, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100)) 0.000000 MetaHookPre CallFunction(Option::set_change_handler, , (SMB::logged_file_actions, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100)) 0.000000 MetaHookPre CallFunction(Option::set_change_handler, , (SMTP::mail_path_capture, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100)) 0.000000 MetaHookPre CallFunction(Option::set_change_handler, , (SMTP::mail_transaction_validation, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100)) @@ -3812,6 +3814,7 @@ 0.000000 | HookCallFunction Option::set_change_handler(RDP::disable_analyzer_after_detection, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100) 0.000000 | HookCallFunction Option::set_change_handler(RDP::rdp_check_interval, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100) 0.000000 | HookCallFunction Option::set_change_handler(SIP::sip_methods, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100) +0.000000 | HookCallFunction Option::set_change_handler(SMB::enable_clear_script_state, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100) 0.000000 | HookCallFunction Option::set_change_handler(SMB::logged_file_actions, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100) 0.000000 | HookCallFunction Option::set_change_handler(SMTP::mail_path_capture, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100) 0.000000 | HookCallFunction Option::set_change_handler(SMTP::mail_transaction_validation, Config::config_option_changed{ if ( == Config::location) return (Config::new_value)Config::log = Config::Info($ts=network_time(), $id=Config::ID, $old_value=Config::format_value(lookup_ID(Config::ID)), $new_value=Config::format_value(Config::new_value))if ( != Config::location) Config::log$location = Config::locationLog::write(Config::LOG, to_any_coerceConfig::log)return (Config::new_value)}, -100) diff --git a/testing/btest/Baseline/scripts.base.protocols.smb.smb2-max-pending-messages/out b/testing/btest/Baseline/scripts.base.protocols.smb.smb2-max-pending-messages/out new file mode 100644 index 0000000000..524f00fa21 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.smb.smb2-max-pending-messages/out @@ -0,0 +1,25 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +smb2_discarded_messages_state before, tree, 20 +smb2_discarded_messages_state after, tree, 0 +smb2_discarded_messages_state before, tree, 20 +smb2_discarded_messages_state after, tree, 0 +smb2_discarded_messages_state before, tree, 20 +smb2_discarded_messages_state after, tree, 0 +smb2_discarded_messages_state before, tree, 20 +smb2_discarded_messages_state after, tree, 0 +smb2_discarded_messages_state before, tree, 20 +smb2_discarded_messages_state after, tree, 0 +smb2_discarded_messages_state before, read, 15 +smb2_discarded_messages_state after, read, 0 +smb2_discarded_messages_state before, tree, 5 +smb2_discarded_messages_state after, tree, 0 +smb2_discarded_messages_state before, tree, 20 +smb2_discarded_messages_state after, tree, 0 +smb2_discarded_messages_state before, tree, 20 +smb2_discarded_messages_state after, tree, 0 +smb2_discarded_messages_state before, read, 15 +smb2_discarded_messages_state after, read, 0 +smb2_discarded_messages_state before, tree, 5 +smb2_discarded_messages_state after, tree, 0 +smb2_discarded_messages_state before, tree, 20 +smb2_discarded_messages_state after, tree, 0 diff --git a/testing/btest/Baseline/scripts.base.protocols.smb.smb2-max-pending-messages/weird.log b/testing/btest/Baseline/scripts.base.protocols.smb.smb2-max-pending-messages/weird.log new file mode 100644 index 0000000000..f7d257af1b --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.smb.smb2-max-pending-messages/weird.log @@ -0,0 +1,11 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path weird +#open XXXX-XX-XX-XX-XX-XX +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p name addl notice peer source +#types time string addr port addr port string string bool string string +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 34884 127.0.0.1 445 SMB_discarded_messages_state state=tree fid_map=0 tid_map=2 pending_cmds=20 pipe_map=0 F zeek SMB2 +#close XXXX-XX-XX-XX-XX-XX diff --git a/testing/btest/Traces/smb/smb2_100_small_files.pcap b/testing/btest/Traces/smb/smb2_100_small_files.pcap new file mode 100644 index 0000000000..4bc38dc36a Binary files /dev/null and b/testing/btest/Traces/smb/smb2_100_small_files.pcap differ diff --git a/testing/btest/scripts/base/protocols/smb/smb2-max-pending-messages.test b/testing/btest/scripts/base/protocols/smb/smb2-max-pending-messages.test new file mode 100644 index 0000000000..e2558ec83d --- /dev/null +++ b/testing/btest/scripts/base/protocols/smb/smb2-max-pending-messages.test @@ -0,0 +1,18 @@ +# @TEST-DOC: Pcap contains 100 file transfers (read requests), force BPF filtering such that the responses aren't seen and we have state growth. Verify a low SMB::max_pending_messages triggers, logs a weird and that script-land message state is reset. +# @TEST-EXEC: zeek -b -C -r $TRACES/smb/smb2_100_small_files.pcap -f 'src port not 445 or tcp[tcpflags] & (tcp-syn|tcp-fin|tcp-rst) != 0' %INPUT >out +# @TEST-EXEC: btest-diff weird.log +# @TEST-EXEC: btest-diff out + +@load base/protocols/smb + +redef SMB::max_pending_messages = 20; + +event smb2_discarded_messages_state(c: connection, request: string) &priority=10 + { + print "smb2_discarded_messages_state before", request, |c$smb_state$pending_cmds|; + } + +event smb2_discarded_messages_state(c: connection, request: string) &priority=-10 + { + print "smb2_discarded_messages_state after", request, |c$smb_state$pending_cmds|; + }