From 8703f9480861243cb343542ecbe114b06939011d Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Tue, 8 Oct 2024 12:39:25 +0530 Subject: [PATCH 1/2] detect/flow: allow match on bytes and pkts in either dir Add an extension to keywords flow.bytes.. and flow.pkts.. to allow matching on bytes or pkts in either direction. The syntax for this operation would look like the following: flow.bytes_either:1000 flow.pkts_either:20 These are implemented as generic uint types and thus allow all basic ops in the syntax like greater than, less than, etc alongwith the exact match. Feature 5646 --- src/detect-engine-register.c | 2 + src/detect-engine-register.h | 2 + src/detect-flow-pkts.c | 138 ++++++++++++++++++++++++++++++++++- src/detect-flow-pkts.h | 2 + 4 files changed, 143 insertions(+), 1 deletion(-) diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 794f680dd46f..15191694b1fe 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -571,8 +571,10 @@ void SigTableSetup(void) DetectFlowAgeRegister(); DetectFlowPktsToClientRegister(); DetectFlowPktsToServerRegister(); + DetectFlowPktsEitherRegister(); DetectFlowBytesToClientRegister(); DetectFlowBytesToServerRegister(); + DetectFlowBytesEitherRegister(); DetectRequiresRegister(); DetectWindowRegister(); DetectRpcRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index 7c3b5b4514b0..ddf348e31860 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -126,8 +126,10 @@ enum DetectKeywordId { DETECT_FLOW_AGE, DETECT_FLOW_PKTS_TO_CLIENT, DETECT_FLOW_PKTS_TO_SERVER, + DETECT_FLOW_PKTS_EITHER, DETECT_FLOW_BYTES_TO_CLIENT, DETECT_FLOW_BYTES_TO_SERVER, + DETECT_FLOW_BYTES_EITHER, DETECT_REQUIRES, diff --git a/src/detect-flow-pkts.c b/src/detect-flow-pkts.c index ef8fed369cd9..2a6686151237 100644 --- a/src/detect-flow-pkts.c +++ b/src/detect-flow-pkts.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Open Information Security Foundation +/* Copyright (C) 2023-2024 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -23,6 +23,89 @@ #include "detect-engine-uint.h" #include "detect-parse.h" +static int DetectFlowPktsEitherMatch( + DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) +{ + if (p->flow == NULL) { + return 0; + } + uint32_t nb = p->flow->tosrcpktcnt; + + const DetectU32Data *du32 = (const DetectU32Data *)ctx; + int ret1 = DetectU32Match(nb, du32); + if (ret1 == 1) + return 1; + + nb = p->flow->todstpktcnt; + int ret2 = DetectU32Match(nb, du32); + if (ret2 == 1) + return 1; + + return 0; +} + +static void DetectFlowPktsEitherFree(DetectEngineCtx *de_ctx, void *ptr) +{ + rs_detect_u32_free(ptr); +} + +static int DetectFlowPktsEitherSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) +{ + DetectU32Data *du32 = DetectU32Parse(rawstr); + if (du32 == NULL) + return -1; + + if (SigMatchAppendSMToList(de_ctx, s, DETECT_FLOW_PKTS_EITHER, (SigMatchCtx *)du32, + DETECT_SM_LIST_MATCH) == NULL) { + DetectFlowPktsEitherFree(de_ctx, du32); + return -1; + } + s->flags |= SIG_FLAG_REQUIRE_PACKET; + + return 0; +} + +static void PrefilterPacketFlowPktsEitherMatch( + DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx) +{ + const PrefilterPacketHeaderCtx *ctx = pectx; + if (!PrefilterPacketHeaderExtraMatch(ctx, p)) + return; + + DetectU32Data du32; + du32.mode = ctx->v1.u8[0]; + du32.arg1 = ctx->v1.u32[1]; + du32.arg2 = ctx->v1.u32[2]; + if (DetectFlowPktsEitherMatch(det_ctx, p, NULL, (const SigMatchCtx *)&du32)) { + PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt); + } +} + +static int PrefilterSetupFlowPktsEither(DetectEngineCtx *de_ctx, SigGroupHead *sgh) +{ + return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_FLOW_PKTS_EITHER, SIG_MASK_REQUIRE_FLOW, + PrefilterPacketU32Set, PrefilterPacketU32Compare, PrefilterPacketFlowPktsEitherMatch); +} + +static bool PrefilterFlowPktsEitherIsPrefilterable(const Signature *s) +{ + return PrefilterIsPrefilterableById(s, DETECT_FLOW_PKTS_EITHER); +} + +void DetectFlowPktsEitherRegister(void) +{ + sigmatch_table[DETECT_FLOW_PKTS_EITHER].name = "flow.pkts_either"; + sigmatch_table[DETECT_FLOW_PKTS_EITHER].desc = + "match flow number of packets in either direction"; + sigmatch_table[DETECT_FLOW_PKTS_EITHER].url = "/rules/flow-keywords.html#flow-pkts_either"; + sigmatch_table[DETECT_FLOW_PKTS_EITHER].Match = DetectFlowPktsEitherMatch; + sigmatch_table[DETECT_FLOW_PKTS_EITHER].Setup = DetectFlowPktsEitherSetup; + sigmatch_table[DETECT_FLOW_PKTS_EITHER].Free = DetectFlowPktsEitherFree; + sigmatch_table[DETECT_FLOW_PKTS_EITHER].SupportsPrefilter = + PrefilterFlowPktsEitherIsPrefilterable; + sigmatch_table[DETECT_FLOW_PKTS_EITHER].SetupPrefilter = PrefilterSetupFlowPktsEither; +} + static int DetectFlowPktsToClientMatch( DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) { @@ -171,6 +254,59 @@ void DetectFlowPktsToServerRegister(void) sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].SetupPrefilter = PrefilterSetupFlowPktsToServer; } +static int DetectFlowBytesEitherMatch( + DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) +{ + if (p->flow == NULL) { + return 0; + } + uint64_t nb = p->flow->tosrcbytecnt; + + const DetectU64Data *du64 = (const DetectU64Data *)ctx; + int ret1 = DetectU64Match(nb, du64); + if (ret1 == 1) + return 1; + + nb = p->flow->todstbytecnt; + int ret2 = DetectU64Match(nb, du64); + if (ret2 == 1) + return 1; + + return 0; +} + +static void DetectFlowBytesEitherFree(DetectEngineCtx *de_ctx, void *ptr) +{ + rs_detect_u64_free(ptr); +} + +static int DetectFlowBytesEitherSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) +{ + DetectU64Data *du64 = DetectU64Parse(rawstr); + if (du64 == NULL) + return -1; + + if (SigMatchAppendSMToList(de_ctx, s, DETECT_FLOW_BYTES_EITHER, (SigMatchCtx *)du64, + DETECT_SM_LIST_MATCH) == NULL) { + DetectFlowBytesEitherFree(de_ctx, du64); + return -1; + } + s->flags |= SIG_FLAG_REQUIRE_PACKET; + + return 0; +} + +void DetectFlowBytesEitherRegister(void) +{ + sigmatch_table[DETECT_FLOW_BYTES_EITHER].name = "flow.bytes_either"; + sigmatch_table[DETECT_FLOW_BYTES_EITHER].desc = + "match flow number of bytes in either direction"; + sigmatch_table[DETECT_FLOW_BYTES_EITHER].url = "/rules/flow-keywords.html#flow-bytes_either"; + sigmatch_table[DETECT_FLOW_BYTES_EITHER].Match = DetectFlowBytesEitherMatch; + sigmatch_table[DETECT_FLOW_BYTES_EITHER].Setup = DetectFlowBytesEitherSetup; + sigmatch_table[DETECT_FLOW_BYTES_EITHER].Free = DetectFlowBytesEitherFree; +} + static int DetectFlowBytesToClientMatch( DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) { diff --git a/src/detect-flow-pkts.h b/src/detect-flow-pkts.h index da1e0eb5a6aa..94bc373fe15b 100644 --- a/src/detect-flow-pkts.h +++ b/src/detect-flow-pkts.h @@ -20,7 +20,9 @@ void DetectFlowPktsToClientRegister(void); void DetectFlowPktsToServerRegister(void); +void DetectFlowPktsEitherRegister(void); void DetectFlowBytesToClientRegister(void); void DetectFlowBytesToServerRegister(void); +void DetectFlowBytesEitherRegister(void); #endif /* SURICATA_DETECT_FLOW_PKTS_H */ From f4bc8c76f2752ac8644b6dd66aa04781734286e8 Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Tue, 8 Oct 2024 12:43:27 +0530 Subject: [PATCH 2/2] doc: add entry for flow.bytes_either & flow.pkts_either Feature 5646 --- doc/userguide/rules/flow-keywords.rst | 44 +++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/doc/userguide/rules/flow-keywords.rst b/doc/userguide/rules/flow-keywords.rst index 00801352303e..92c9287036ed 100644 --- a/doc/userguide/rules/flow-keywords.rst +++ b/doc/userguide/rules/flow-keywords.rst @@ -340,6 +340,28 @@ Signature example:: alert ip any any -> any any (msg:"Flow has 20 packets"; flow.pkts_toclient:20; sid:1;) +flow.pkts_either +------------------ + +Flow number of packets in either direction (integer) +This keyword does not wait for the end of the flow, but will be checked at each packet. + +flow.pkts_either uses an :ref:`unsigned 32-bit integer `. + +Syntax:: + + flow.pkts_either: [op] + +The number of packets can be matched exactly, or compared using the _op_ setting:: + + flow.pkts_either:3 # exactly 3 + flow.pkts_either:<3 # smaller than 3 + flow.pkts_either:>=2 # greater than or equal to 2 + +Signature example:: + + alert ip any any -> any any (msg:"Flow has 20 packets in either dir"; flow.pkts_either:20; sid:1;) + flow.pkts_toserver ------------------ @@ -405,3 +427,25 @@ The number of packets can be matched exactly, or compared using the _op_ setting Signature example:: alert ip any any -> any any (msg:"Flow has less than 2000 bytes"; flow.bytes_toserver:<2000; sid:1;) + +flow.bytes_either +------------------- + +Flow number of bytes in either direction (integer) +This keyword does not wait for the end of the flow, but will be checked at each packet. + +flow.bytes_either uses an :ref:`unsigned 64-bit integer `. + +Syntax:: + + flow.bytes_either: [op] + +The number of packets can be matched exactly, or compared using the _op_ setting:: + + flow.bytes_either:3 # exactly 3 + flow.bytes_either:<3 # smaller than 3 + flow.bytes_either:>=2 # greater than or equal to 2 + +Signature example:: + + alert ip any any -> any any (msg:"Flow has greater than 3000 bytes in either dir"; flow.bytes_either:>3000; sid:1;)