From 7c48aad5826738502765b2f079782ec2549d7b1c Mon Sep 17 00:00:00 2001 From: Johanna Amann Date: Thu, 4 Apr 2019 12:27:42 -0700 Subject: [PATCH] Update DTLS error handling DTLS now only outputs protocol violations once it saw something that looked like a DTLS connection (at least a client hello). Before the danger that it misinterprets something is too high. It has a configurable number of invalid packets that it can skip over (because other protocols might be interleaved with the connection) and a maximum amount of Protocol violations that it outputs because of wrong packet versions. --- doc | 2 +- scripts/base/init-bare.bro | 11 ++++++++ src/analyzer/protocol/ssl/CMakeLists.txt | 3 ++- src/analyzer/protocol/ssl/consts.bif | 2 ++ src/analyzer/protocol/ssl/dtls-protocol.pac | 27 ++++++++++++++++++- src/analyzer/protocol/ssl/dtls.pac | 1 + .../canonified_loaded_scripts.log | 5 ++-- .../canonified_loaded_scripts.log | 5 ++-- testing/btest/Baseline/plugins.hooks/output | 17 +++++++----- .../.stdout | 0 .../base/protocols/ssl/dtls-no-dtls.test | 15 +++++++++++ 11 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 src/analyzer/protocol/ssl/consts.bif create mode 100644 testing/btest/Baseline/scripts.base.protocols.ssl.dtls-no-dtls/.stdout create mode 100644 testing/btest/scripts/base/protocols/ssl/dtls-no-dtls.test diff --git a/doc b/doc index 5aa921f0f6..2036846610 160000 --- a/doc +++ b/doc @@ -1 +1 @@ -Subproject commit 5aa921f0f6ce2931e446a11f2a10cffb7f0dbc09 +Subproject commit 203684661040089877830eb98e12a6d4c18a4675 diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index 0c32cebcc5..e94efd07df 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -4169,6 +4169,17 @@ export { HashAlgorithm: count; ##< Hash algorithm number SignatureAlgorithm: count; ##< Signature algorithm number }; + + +## Number of non-DTLS frames that can occur in a DTLS connection before +## parsing of the connection is suspended. +## DTLS does not immediately stop parsing a connection because other protocols +## might be interleaved in the same UDP "connection". +const SSL::dtls_max_version_errors = 10 &redef; + +## Maximum number of invalid version errors to report in one DTLS connection. +const SSL::dtls_max_reported_version_errors = 1 &redef; + } module GLOBAL; diff --git a/src/analyzer/protocol/ssl/CMakeLists.txt b/src/analyzer/protocol/ssl/CMakeLists.txt index 14e41892c8..3193470635 100644 --- a/src/analyzer/protocol/ssl/CMakeLists.txt +++ b/src/analyzer/protocol/ssl/CMakeLists.txt @@ -8,6 +8,7 @@ bro_plugin_cc(SSL.cc DTLS.cc Plugin.cc) bro_plugin_bif(types.bif) bro_plugin_bif(events.bif) bro_plugin_bif(functions.bif) +bro_plugin_bif(consts.bif) bro_plugin_pac(tls-handshake.pac tls-handshake-protocol.pac tls-handshake-analyzer.pac ssl-defs.pac proc-client-hello.pac proc-server-hello.pac @@ -16,7 +17,7 @@ bro_plugin_pac(tls-handshake.pac tls-handshake-protocol.pac tls-handshake-analyz ) bro_plugin_pac(ssl.pac ssl-dtls-analyzer.pac ssl-analyzer.pac ssl-dtls-protocol.pac ssl-protocol.pac ssl-defs.pac proc-client-hello.pac - proc-server-hello.pac + proc-server-hello.pac proc-certificate.pac ) bro_plugin_pac(dtls.pac ssl-dtls-analyzer.pac dtls-analyzer.pac ssl-dtls-protocol.pac dtls-protocol.pac ssl-defs.pac) diff --git a/src/analyzer/protocol/ssl/consts.bif b/src/analyzer/protocol/ssl/consts.bif new file mode 100644 index 0000000000..9dcbaa65d5 --- /dev/null +++ b/src/analyzer/protocol/ssl/consts.bif @@ -0,0 +1,2 @@ +const SSL::dtls_max_version_errors: count; +const SSL::dtls_max_reported_version_errors: count; diff --git a/src/analyzer/protocol/ssl/dtls-protocol.pac b/src/analyzer/protocol/ssl/dtls-protocol.pac index 771aa267b3..70897a585c 100644 --- a/src/analyzer/protocol/ssl/dtls-protocol.pac +++ b/src/analyzer/protocol/ssl/dtls-protocol.pac @@ -45,15 +45,40 @@ type Handshake(rec: SSLRecord) = record { refine connection SSL_Conn += { + %member{ + uint16 invalid_version_count_; + uint16 reported_errors_; + %} + + %init{ + invalid_version_count_ = 0; + reported_errors_ = 0; + %} + function dtls_version_ok(version: uint16): uint16 %{ switch ( version ) { case DTLSv10: case DTLSv12: + // Reset only to 0 once we have seen a client hello. + // This means the connection gets a limited amount of valid/invalid + // packets before a client hello has to be seen - which seems reasonable. + if ( bro_analyzer()->ProtocolConfirmed() ) + invalid_version_count_ = 0; return true; default: - bro_analyzer()->ProtocolViolation(fmt("Invalid version in DTLS connection. Packet reported version: %d", version)); + invalid_version_count_++; + + if ( bro_analyzer()->ProtocolConfirmed() ) + { + reported_errors_++; + if ( reported_errors_ <= BifConst::SSL::dtls_max_reported_version_errors ) + bro_analyzer()->ProtocolViolation(fmt("Invalid version in DTLS connection. Packet reported version: %d", version)); + } + + if ( invalid_version_count_ > BifConst::SSL::dtls_max_version_errors ) + bro_analyzer()->SetSkip(true); return false; } %} diff --git a/src/analyzer/protocol/ssl/dtls.pac b/src/analyzer/protocol/ssl/dtls.pac index b08dd61f8f..b2aa34d5c5 100644 --- a/src/analyzer/protocol/ssl/dtls.pac +++ b/src/analyzer/protocol/ssl/dtls.pac @@ -10,6 +10,7 @@ namespace analyzer { namespace dtls { class DTLS_Analyzer; } } typedef analyzer::dtls::DTLS_Analyzer* DTLSAnalyzer; #include "DTLS.h" +#include "consts.bif.h" %} extern type DTLSAnalyzer; diff --git a/testing/btest/Baseline/coverage.bare-load-baseline/canonified_loaded_scripts.log b/testing/btest/Baseline/coverage.bare-load-baseline/canonified_loaded_scripts.log index 4eeaa4b07b..bd24bf02aa 100644 --- a/testing/btest/Baseline/coverage.bare-load-baseline/canonified_loaded_scripts.log +++ b/testing/btest/Baseline/coverage.bare-load-baseline/canonified_loaded_scripts.log @@ -3,7 +3,7 @@ #empty_field (empty) #unset_field - #path loaded_scripts -#open 2018-06-08-16-37-15 +#open 2019-04-04-19-22-03 #fields name #types string scripts/base/init-bare.bro @@ -149,6 +149,7 @@ scripts/base/init-frameworks-and-bifs.bro build/scripts/base/bif/plugins/Bro_SSL.types.bif.bro build/scripts/base/bif/plugins/Bro_SSL.events.bif.bro build/scripts/base/bif/plugins/Bro_SSL.functions.bif.bro + build/scripts/base/bif/plugins/Bro_SSL.consts.bif.bro build/scripts/base/bif/plugins/Bro_SteppingStone.events.bif.bro build/scripts/base/bif/plugins/Bro_Syslog.events.bif.bro build/scripts/base/bif/plugins/Bro_TCP.events.bif.bro @@ -179,4 +180,4 @@ scripts/base/init-frameworks-and-bifs.bro build/scripts/base/bif/plugins/Bro_SQLiteWriter.sqlite.bif.bro scripts/policy/misc/loaded-scripts.bro scripts/base/utils/paths.bro -#close 2018-06-08-16-37-15 +#close 2019-04-04-19-22-03 diff --git a/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log b/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log index eaca1c489a..540910b350 100644 --- a/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log +++ b/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log @@ -3,7 +3,7 @@ #empty_field (empty) #unset_field - #path loaded_scripts -#open 2018-09-05-20-33-08 +#open 2019-04-04-19-22-06 #fields name #types string scripts/base/init-bare.bro @@ -149,6 +149,7 @@ scripts/base/init-frameworks-and-bifs.bro build/scripts/base/bif/plugins/Bro_SSL.types.bif.bro build/scripts/base/bif/plugins/Bro_SSL.events.bif.bro build/scripts/base/bif/plugins/Bro_SSL.functions.bif.bro + build/scripts/base/bif/plugins/Bro_SSL.consts.bif.bro build/scripts/base/bif/plugins/Bro_SteppingStone.events.bif.bro build/scripts/base/bif/plugins/Bro_Syslog.events.bif.bro build/scripts/base/bif/plugins/Bro_TCP.events.bif.bro @@ -373,4 +374,4 @@ scripts/base/init-default.bro scripts/base/misc/find-filtered-trace.bro scripts/base/misc/version.bro scripts/policy/misc/loaded-scripts.bro -#close 2018-09-05-20-33-08 +#close 2019-04-04-19-22-06 diff --git a/testing/btest/Baseline/plugins.hooks/output b/testing/btest/Baseline/plugins.hooks/output index d4a84a5223..04908bed0b 100644 --- a/testing/btest/Baseline/plugins.hooks/output +++ b/testing/btest/Baseline/plugins.hooks/output @@ -277,7 +277,7 @@ 0.000000 MetaHookPost CallFunction(Log::__create_stream, , (Weird::LOG, [columns=Weird::Info, ev=Weird::log_weird, path=weird])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, , (X509::LOG, [columns=X509::Info, ev=X509::log_x509, path=x509])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, , (mysql::LOG, [columns=MySQL::Info, ev=MySQL::log_mysql, path=mysql])) -> -0.000000 MetaHookPost CallFunction(Log::__write, , (PacketFilter::LOG, [ts=1552701731.192609, node=bro, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::__write, , (PacketFilter::LOG, [ts=1554405757.770254, node=bro, filter=ip or not ip, init=T, success=T])) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, , (Broker::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, , (Cluster::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, , (Config::LOG)) -> @@ -462,7 +462,7 @@ 0.000000 MetaHookPost CallFunction(Log::create_stream, , (Weird::LOG, [columns=Weird::Info, ev=Weird::log_weird, path=weird])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, , (X509::LOG, [columns=X509::Info, ev=X509::log_x509, path=x509])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, , (mysql::LOG, [columns=MySQL::Info, ev=MySQL::log_mysql, path=mysql])) -> -0.000000 MetaHookPost CallFunction(Log::write, , (PacketFilter::LOG, [ts=1552701731.192609, node=bro, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::write, , (PacketFilter::LOG, [ts=1554405757.770254, node=bro, filter=ip or not ip, init=T, success=T])) -> 0.000000 MetaHookPost CallFunction(NetControl::check_plugins, , ()) -> 0.000000 MetaHookPost CallFunction(NetControl::init, , ()) -> 0.000000 MetaHookPost CallFunction(Notice::want_pp, , ()) -> @@ -678,6 +678,7 @@ 0.000000 MetaHookPost LoadFile(0, .<...>/Bro_SQLiteWriter.sqlite.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/Bro_SSH.events.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/Bro_SSH.types.bif.bro) -> -1 +0.000000 MetaHookPost LoadFile(0, .<...>/Bro_SSL.consts.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/Bro_SSL.events.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/Bro_SSL.functions.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/Bro_SSL.types.bif.bro) -> -1 @@ -1179,7 +1180,7 @@ 0.000000 MetaHookPre CallFunction(Log::__create_stream, , (Weird::LOG, [columns=Weird::Info, ev=Weird::log_weird, path=weird])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, , (X509::LOG, [columns=X509::Info, ev=X509::log_x509, path=x509])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, , (mysql::LOG, [columns=MySQL::Info, ev=MySQL::log_mysql, path=mysql])) -0.000000 MetaHookPre CallFunction(Log::__write, , (PacketFilter::LOG, [ts=1552701731.192609, node=bro, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::__write, , (PacketFilter::LOG, [ts=1554405757.770254, node=bro, filter=ip or not ip, init=T, success=T])) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, , (Broker::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, , (Cluster::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, , (Config::LOG)) @@ -1364,7 +1365,7 @@ 0.000000 MetaHookPre CallFunction(Log::create_stream, , (Weird::LOG, [columns=Weird::Info, ev=Weird::log_weird, path=weird])) 0.000000 MetaHookPre CallFunction(Log::create_stream, , (X509::LOG, [columns=X509::Info, ev=X509::log_x509, path=x509])) 0.000000 MetaHookPre CallFunction(Log::create_stream, , (mysql::LOG, [columns=MySQL::Info, ev=MySQL::log_mysql, path=mysql])) -0.000000 MetaHookPre CallFunction(Log::write, , (PacketFilter::LOG, [ts=1552701731.192609, node=bro, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::write, , (PacketFilter::LOG, [ts=1554405757.770254, node=bro, filter=ip or not ip, init=T, success=T])) 0.000000 MetaHookPre CallFunction(NetControl::check_plugins, , ()) 0.000000 MetaHookPre CallFunction(NetControl::init, , ()) 0.000000 MetaHookPre CallFunction(Notice::want_pp, , ()) @@ -1580,6 +1581,7 @@ 0.000000 MetaHookPre LoadFile(0, .<...>/Bro_SQLiteWriter.sqlite.bif.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/Bro_SSH.events.bif.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/Bro_SSH.types.bif.bro) +0.000000 MetaHookPre LoadFile(0, .<...>/Bro_SSL.consts.bif.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/Bro_SSL.events.bif.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/Bro_SSL.functions.bif.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/Bro_SSL.types.bif.bro) @@ -2080,7 +2082,7 @@ 0.000000 | HookCallFunction Log::__create_stream(Weird::LOG, [columns=Weird::Info, ev=Weird::log_weird, path=weird]) 0.000000 | HookCallFunction Log::__create_stream(X509::LOG, [columns=X509::Info, ev=X509::log_x509, path=x509]) 0.000000 | HookCallFunction Log::__create_stream(mysql::LOG, [columns=MySQL::Info, ev=MySQL::log_mysql, path=mysql]) -0.000000 | HookCallFunction Log::__write(PacketFilter::LOG, [ts=1552701731.192609, node=bro, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::__write(PacketFilter::LOG, [ts=1554405757.770254, node=bro, filter=ip or not ip, init=T, success=T]) 0.000000 | HookCallFunction Log::add_default_filter(Broker::LOG) 0.000000 | HookCallFunction Log::add_default_filter(Cluster::LOG) 0.000000 | HookCallFunction Log::add_default_filter(Config::LOG) @@ -2265,7 +2267,7 @@ 0.000000 | HookCallFunction Log::create_stream(Weird::LOG, [columns=Weird::Info, ev=Weird::log_weird, path=weird]) 0.000000 | HookCallFunction Log::create_stream(X509::LOG, [columns=X509::Info, ev=X509::log_x509, path=x509]) 0.000000 | HookCallFunction Log::create_stream(mysql::LOG, [columns=MySQL::Info, ev=MySQL::log_mysql, path=mysql]) -0.000000 | HookCallFunction Log::write(PacketFilter::LOG, [ts=1552701731.192609, node=bro, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::write(PacketFilter::LOG, [ts=1554405757.770254, node=bro, filter=ip or not ip, init=T, success=T]) 0.000000 | HookCallFunction NetControl::check_plugins() 0.000000 | HookCallFunction NetControl::init() 0.000000 | HookCallFunction Notice::want_pp() @@ -2481,6 +2483,7 @@ 0.000000 | HookLoadFile .<...>/Bro_SQLiteWriter.sqlite.bif.bro 0.000000 | HookLoadFile .<...>/Bro_SSH.events.bif.bro 0.000000 | HookLoadFile .<...>/Bro_SSH.types.bif.bro +0.000000 | HookLoadFile .<...>/Bro_SSL.consts.bif.bro 0.000000 | HookLoadFile .<...>/Bro_SSL.events.bif.bro 0.000000 | HookLoadFile .<...>/Bro_SSL.functions.bif.bro 0.000000 | HookLoadFile .<...>/Bro_SSL.types.bif.bro @@ -2699,7 +2702,7 @@ 0.000000 | HookLoadFile base<...>/x509 0.000000 | HookLoadFile base<...>/xmpp 0.000000 | HookLogInit packet_filter 1/1 {ts (time), node (string), filter (string), init (bool), success (bool)} -0.000000 | HookLogWrite packet_filter [ts=1552701731.192609, node=bro, filter=ip or not ip, init=T, success=T] +0.000000 | HookLogWrite packet_filter [ts=1554405757.770254, node=bro, filter=ip or not ip, init=T, success=T] 0.000000 | HookQueueEvent NetControl::init() 0.000000 | HookQueueEvent bro_init() 0.000000 | HookQueueEvent filter_change_tracking() diff --git a/testing/btest/Baseline/scripts.base.protocols.ssl.dtls-no-dtls/.stdout b/testing/btest/Baseline/scripts.base.protocols.ssl.dtls-no-dtls/.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testing/btest/scripts/base/protocols/ssl/dtls-no-dtls.test b/testing/btest/scripts/base/protocols/ssl/dtls-no-dtls.test new file mode 100644 index 0000000000..c8721529c9 --- /dev/null +++ b/testing/btest/scripts/base/protocols/ssl/dtls-no-dtls.test @@ -0,0 +1,15 @@ +# This tests checks that non-dtls connections to which we attach don't trigger tons of errors. + +# @TEST-EXEC: bro -C -r $TRACES/dns-txt-multiple.trace %INPUT +# @TEST-EXEC: btest-diff .stdout + +event bro_init() + { + const add_ports = { 53/udp }; + Analyzer::register_for_ports(Analyzer::ANALYZER_DTLS, add_ports); + } + +event protocol_violation(c: connection, atype: Analyzer::Tag, aid: count, reason: string) + { + print c$id, atype, reason; + }