diff --git a/scripts/base/frameworks/notice/weird.zeek b/scripts/base/frameworks/notice/weird.zeek index 919f683123..0511fc1e22 100644 --- a/scripts/base/frameworks/notice/weird.zeek +++ b/scripts/base/frameworks/notice/weird.zeek @@ -211,6 +211,7 @@ export { ["spontaneous_RST"] = ACTION_IGNORE, ["SMB_parsing_error"] = ACTION_LOG, ["SMB_discarded_messages_state"] = ACTION_LOG, + ["SMB_discarded_dce_rpc_analyzers"] = 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/init-bare.zeek b/scripts/base/init-bare.zeek index 2352566129..ddf32ba2bb 100644 --- a/scripts/base/init-bare.zeek +++ b/scripts/base/init-bare.zeek @@ -3054,6 +3054,12 @@ export { ## ## .. zeek:see:: smb2_discarded_messages_state const SMB::max_pending_messages = 1000 &redef; + + ## Maximum number of DCE-RPC analyzers per connection + ## before discarding them to avoid unbounded state growth. + ## + ## .. zeek:see:: smb_discarded_dce_rpc_analyzers + const max_dce_rpc_analyzers = 1000 &redef; } module SMB1; diff --git a/scripts/base/protocols/dce-rpc/main.zeek b/scripts/base/protocols/dce-rpc/main.zeek index 75b3299206..a98314f8fb 100644 --- a/scripts/base/protocols/dce-rpc/main.zeek +++ b/scripts/base/protocols/dce-rpc/main.zeek @@ -216,6 +216,15 @@ 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"); + } + hook finalize_dce_rpc(c: connection) { if ( ! c?$dce_rpc ) diff --git a/src/analyzer/protocol/smb/consts.bif b/src/analyzer/protocol/smb/consts.bif index 6acd464f6a..6da0f83f3c 100644 --- a/src/analyzer/protocol/smb/consts.bif +++ b/src/analyzer/protocol/smb/consts.bif @@ -1,2 +1,3 @@ const SMB::pipe_filenames: string_set; const SMB::max_pending_messages: count; +const SMB::max_dce_rpc_analyzers: count; diff --git a/src/analyzer/protocol/smb/events.bif b/src/analyzer/protocol/smb/events.bif index 77746c2a09..fce37d9440 100644 --- a/src/analyzer/protocol/smb/events.bif +++ b/src/analyzer/protocol/smb/events.bif @@ -8,3 +8,13 @@ ## ## c: The connection. event smb_pipe_connect_heuristic%(c: connection%); + +## Generated for :abbr:`SMB (Server Message Block)` when the number of +## :abbr:`DCE-RPC (Distributed Computing Environment/Remote Procedure Calls)` +## analyzers exceeds :zeek:see:`SMB::max_dce_rpc_analyzers`. +## Occurrence of this event may indicate traffic loss, traffic load-balancing +## issues or abnormal SMB protocol usage. +## +## c: The connection. +## +event smb_discarded_dce_rpc_analyzers%(c: connection%); diff --git a/src/analyzer/protocol/smb/smb-pipe.pac b/src/analyzer/protocol/smb/smb-pipe.pac index c1500cdbb8..b7d7bb8aeb 100644 --- a/src/analyzer/protocol/smb/smb-pipe.pac +++ b/src/analyzer/protocol/smb/smb-pipe.pac @@ -10,7 +10,7 @@ refine connection SMB_Conn += { %cleanup{ // Iterate all of the analyzers and destroy them. - for ( auto kv : fid_to_analyzer_map ) + for ( const auto& kv : fid_to_analyzer_map ) { if ( kv.second ) { @@ -49,6 +49,22 @@ refine connection SMB_Conn += { if ( it == fid_to_analyzer_map.end() ) { + // Too many analyzers? + if ( zeek::BifConst::SMB::max_dce_rpc_analyzers > 0 && + fid_to_analyzer_map.size() >= zeek::BifConst::SMB::max_dce_rpc_analyzers ) + { + if ( smb_discarded_dce_rpc_analyzers ) + zeek::BifEvent::enqueue_smb_discarded_dce_rpc_analyzers(zeek_analyzer(), zeek_analyzer()->Conn()); + + for ( const auto& kv : fid_to_analyzer_map ) + { + kv.second->Done(); + delete kv.second; + } + + fid_to_analyzer_map.clear(); + } + auto tmp_analyzer = zeek::analyzer_mgr->InstantiateAnalyzer("DCE_RPC", zeek_analyzer()->Conn()); pipe_dcerpc = static_cast(tmp_analyzer); @@ -68,4 +84,19 @@ refine connection SMB_Conn += { return true; %} + + function forward_dce_rpc_close(fid: uint64): bool + %{ + auto it = fid_to_analyzer_map.find(fid); + + if ( it != fid_to_analyzer_map.end() ) + { + it->second->Done(); + delete it->second; + fid_to_analyzer_map.erase(it); + return true; + } + + return false; + %} }; diff --git a/src/analyzer/protocol/smb/smb2-com-close.pac b/src/analyzer/protocol/smb/smb2-com-close.pac index 1efc7ec4e7..e7e6f5a6f5 100644 --- a/src/analyzer/protocol/smb/smb2-com-close.pac +++ b/src/analyzer/protocol/smb/smb2-com-close.pac @@ -46,7 +46,9 @@ type SMB2_close_request(header: SMB2_Header) = record { reserved : uint32; file_id : SMB2_guid; } &let { + fid: uint64 = file_id.persistent + file_id._volatile; proc: bool = $context.connection.proc_smb2_close_request(header, this); + maybe_pipe_close: bool = $context.connection.forward_dce_rpc_close(fid); }; type SMB2_close_response(header: SMB2_Header) = record {