diff --git a/CHANGES b/CHANGES index 8000736a08..d2b1aedf7f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,17 @@ +3.3.0-dev.381 | 2020-10-12 11:15:29 -0700 + + * GH-779: Add "udp-state" signature condition (Jon Siwek, Corelight) + + It accepts "originator" or "responder" states as a way to enforce that + the signature only matches packets in the associated direction. + The "established" state is rejected as an error since it doesn't + have a useful meaning like it does for the "tcp-state" condition. + + * Rename RuleConditionTCPState::TCPState enum values (Jon Siwek, Corelight) + + * Rename "tcp-state" signature parser tokens to not be TCP-specific (Jon Siwek, Corelight) + 3.3.0-dev.377 | 2020-10-08 12:07:42 -0700 * GH-1200: ignore a maybe-uninitialized warning (Jon Siwek, Corelight) diff --git a/NEWS b/NEWS index 8e183219db..986f6b0736 100644 --- a/NEWS +++ b/NEWS @@ -34,6 +34,9 @@ New Functionality See https://docs.zeek.org/en/master/frameworks/logging.html#filter-log-records for more details. +- Added a ``udp-state`` signature condition to enforce matching against + either "originator" or "responder" flow direction of UDP packets. + Changed Functionality --------------------- diff --git a/VERSION b/VERSION index f9fe94395c..674616d996 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.3.0-dev.377 +3.3.0-dev.381 diff --git a/doc b/doc index 2960bd2ee1..fd3e22bf5e 160000 --- a/doc +++ b/doc @@ -1 +1 @@ -Subproject commit 2960bd2ee131e1c565da3e02983bbe4b719bc607 +Subproject commit fd3e22bf5efc42a572e83346031aec4493b3c803 diff --git a/src/RuleCondition.cc b/src/RuleCondition.cc index 977eb9f8e2..789c94cd88 100644 --- a/src/RuleCondition.cc +++ b/src/RuleCondition.cc @@ -32,16 +32,16 @@ bool RuleConditionTCPState::DoMatch(Rule* rule, RuleEndpointState* state, auto* ta = static_cast(root); - if ( tcpstates & STATE_STATELESS ) + if ( tcpstates & RULE_STATE_STATELESS ) return true; - if ( (tcpstates & STATE_ORIG) && ! state->IsOrig() ) + if ( (tcpstates & RULE_STATE_ORIG) && ! state->IsOrig() ) return false; - if ( (tcpstates & STATE_RESP) && state->IsOrig() ) + if ( (tcpstates & RULE_STATE_RESP) && state->IsOrig() ) return false; - if ( (tcpstates & STATE_ESTABLISHED ) && + if ( (tcpstates & RULE_STATE_ESTABLISHED ) && ! (is_established(ta->Orig()) && is_established(ta->Resp()))) return false; @@ -54,6 +54,31 @@ void RuleConditionTCPState::PrintDebug() fprintf(stderr, " RuleConditionTCPState: 0x%x\n", tcpstates); } +bool RuleConditionUDPState::DoMatch(Rule* rule, RuleEndpointState* state, + const u_char* data, int len) + { + analyzer::Analyzer* root = state->GetAnalyzer()->Conn()->GetRootAnalyzer(); + + if ( ! root || ! root->IsAnalyzer("UDP") ) + return false; + + if ( states & RULE_STATE_STATELESS ) + return true; + + if ( (states & RULE_STATE_ORIG) && ! state->IsOrig() ) + return false; + + if ( (states & RULE_STATE_RESP) && state->IsOrig() ) + return false; + + return true; + } + +void RuleConditionUDPState::PrintDebug() + { + fprintf(stderr, " RuleConditionUDPState: 0x%x\n", states); + } + void RuleConditionIPOptions::PrintDebug() { fprintf(stderr, " RuleConditionIPOptions: 0x%x\n", options); diff --git a/src/RuleCondition.h b/src/RuleCondition.h index aa735cf854..6e73da4d1d 100644 --- a/src/RuleCondition.h +++ b/src/RuleCondition.h @@ -22,16 +22,16 @@ public: virtual void PrintDebug() = 0; }; +enum RuleStateKind { + RULE_STATE_ESTABLISHED = 1, + RULE_STATE_ORIG = 2, + RULE_STATE_RESP = 4, + RULE_STATE_STATELESS = 8 +}; + // Implements the "tcp-state" keyword. class RuleConditionTCPState : public RuleCondition { public: - enum TCPState { - STATE_ESTABLISHED = 1, - STATE_ORIG = 2, - STATE_RESP = 4, - STATE_STATELESS = 8 - }; - explicit RuleConditionTCPState(int arg_tcpstates) { tcpstates = arg_tcpstates; } @@ -46,6 +46,20 @@ private: int tcpstates; }; +// Implements the "udp-state" keyword. +class RuleConditionUDPState : public RuleCondition { +public: + explicit RuleConditionUDPState(int arg_states) + { states = arg_states; } + + bool DoMatch(Rule* rule, RuleEndpointState* state, const u_char* data, + int len) override; + + void PrintDebug() override; + +private: + int states; +}; // Implements "ip-options". class RuleConditionIPOptions : public RuleCondition { diff --git a/src/rule-parse.y b/src/rule-parse.y index eb79250bf5..cb555c0c6d 100644 --- a/src/rule-parse.y +++ b/src/rule-parse.y @@ -58,15 +58,16 @@ static uint8_t ip4_mask_to_len(uint32_t mask) %token TOK_SRC_IP %token TOK_SRC_PORT %token TOK_TCP_STATE +%token TOK_UDP_STATE %token TOK_STRING -%token TOK_TCP_STATE_SYM +%token TOK_STATE_SYM %token TOK_ACTIVE %token TOK_BOOL %token TOK_POLICY_SYMBOL %type TOK_STRING TOK_IDENT TOK_POLICY_SYMBOL TOK_PATTERN pattern string -%type TOK_INT TOK_TCP_STATE_SYM TOK_IP_OPTION_SYM TOK_COMP -%type integer ipoption_list tcpstate_list opt_strength +%type TOK_INT TOK_STATE_SYM TOK_IP_OPTION_SYM TOK_COMP +%type integer ipoption_list state_list opt_strength %type rule %type TOK_BOOL opt_negate %type hdr_expr @@ -246,11 +247,19 @@ rule_attr: (zeek::detail::RuleHdrTest::Comp) $2, $3)); } - | TOK_TCP_STATE tcpstate_list + | TOK_TCP_STATE state_list { current_rule->AddCondition(new zeek::detail::RuleConditionTCPState($2)); } + | TOK_UDP_STATE state_list + { + if ( $2 & zeek::detail::RULE_STATE_ESTABLISHED ) + rules_error("'established' is not a valid 'udp-state'"); + + current_rule->AddCondition(new zeek::detail::RuleConditionUDPState($2)); + } + | TOK_ACTIVE TOK_BOOL { current_rule->SetActiveStatus($2); } ; @@ -382,10 +391,10 @@ ipoption_list: { $$ = $1; } ; -tcpstate_list: - tcpstate_list ',' TOK_TCP_STATE_SYM +state_list: + state_list ',' TOK_STATE_SYM { $$ = $1 | $3; } - | TOK_TCP_STATE_SYM + | TOK_STATE_SYM { $$ = $1; } ; diff --git a/src/rule-scan.l b/src/rule-scan.l index ff4c6b0dfa..8cf698563e 100644 --- a/src/rule-scan.l +++ b/src/rule-scan.l @@ -75,23 +75,23 @@ true { rules_lval.val = true; return TOK_BOOL; } false { rules_lval.val = false; return TOK_BOOL; } established { - rules_lval.val = zeek::detail::RuleConditionTCPState::STATE_ESTABLISHED; - return TOK_TCP_STATE_SYM; + rules_lval.val = zeek::detail::RULE_STATE_ESTABLISHED; + return TOK_STATE_SYM; } originator { - rules_lval.val = zeek::detail::RuleConditionTCPState::STATE_ORIG; - return TOK_TCP_STATE_SYM; + rules_lval.val = zeek::detail::RULE_STATE_ORIG; + return TOK_STATE_SYM; } responder { - rules_lval.val = zeek::detail::RuleConditionTCPState::STATE_RESP; - return TOK_TCP_STATE_SYM; + rules_lval.val = zeek::detail::RULE_STATE_RESP; + return TOK_STATE_SYM; } stateless { - rules_lval.val = zeek::detail::RuleConditionTCPState::STATE_STATELESS; - return TOK_TCP_STATE_SYM; + rules_lval.val = zeek::detail::RULE_STATE_STATELESS; + return TOK_STATE_SYM; } lsrr { @@ -132,6 +132,7 @@ same-ip return TOK_SAME_IP; src-ip return TOK_SRC_IP; src-port return TOK_SRC_PORT; tcp-state return TOK_TCP_STATE; +udp-state return TOK_UDP_STATE; active return TOK_ACTIVE; file-magic { rules_lval.val = zeek::detail::Rule::FILE_MAGIC; return TOK_PATTERN_TYPE; } diff --git a/testing/btest/Baseline/signatures.udp-state/out b/testing/btest/Baseline/signatures.udp-state/out new file mode 100644 index 0000000000..eb4d4bc78a --- /dev/null +++ b/testing/btest/Baseline/signatures.udp-state/out @@ -0,0 +1,10 @@ +signature_match [orig_h=192.168.17.58, orig_p=58755/udp, resp_h=8.8.8.8, resp_p=53/udp] - my_sig_udp_orig +0000 35 5e 01 00 00 01 00 00 00 00 00 00 06 67 6f 6f 5^...... .....goo +0010 67 6c 65 03 63 6f 6d 00 01 01 00 01 gle.com. .... + +signature_match [orig_h=192.168.17.58, orig_p=58755/udp, resp_h=8.8.8.8, resp_p=53/udp] - my_sig_udp_resp +0000 35 5e 81 80 00 01 00 01 00 00 00 00 06 67 6f 6f 5^...... .....goo +0010 67 6c 65 03 63 6f 6d 00 01 01 00 01 c0 0c 01 01 gle.com. ........ +0020 00 01 00 00 54 49 00 13 00 05 69 73 73 75 65 73 ....TI.. ..issues +0030 79 6d 61 6e 74 65 63 2e 63 6f 6d ymantec. com + diff --git a/testing/btest/Baseline/signatures.udp-state/reject b/testing/btest/Baseline/signatures.udp-state/reject new file mode 100644 index 0000000000..b495e79aa4 --- /dev/null +++ b/testing/btest/Baseline/signatures.udp-state/reject @@ -0,0 +1,2 @@ +error: Error in signature (udp-established.sig:5): 'established' is not a valid 'udp-state' + diff --git a/testing/btest/signatures/udp-state.zeek b/testing/btest/signatures/udp-state.zeek new file mode 100644 index 0000000000..13cb3d1b8a --- /dev/null +++ b/testing/btest/signatures/udp-state.zeek @@ -0,0 +1,36 @@ +# @TEST-EXEC: zeek -b -s udp-states.sig -r $TRACES/dns-caa.pcap %INPUT >out +# @TEST-EXEC-FAIL: zeek -b -s udp-established.sig -r $TRACES/dns-caa.pcap %INPUT >reject 2>&1 +# @TEST-EXEC: btest-diff out +# @TEST-EXEC: btest-diff reject + +@TEST-START-FILE udp-states.sig +signature my_sig_udp_orig { + ip-proto == udp + payload /.+/ + udp-state originator + event "my_sig_udp_orig" +} + +signature my_sig_udp_resp { + ip-proto == udp + payload /.+/ + udp-state responder + event "my_sig_udp_resp" +} +@TEST-END-FILE + +@TEST-START-FILE udp-established.sig +signature my_sig_udp_est { + ip-proto == udp + payload /.+/ + udp-state established + event "my_sig_udp_est" +} +@TEST-END-FILE + +event signature_match(state: signature_state, msg: string, data: string) + { + print fmt("signature_match %s - %s", state$conn$id, msg); + local s = split_string(hexdump(data), /\n/); + for ( i in s ) print s[i]; + }