From a492d94826f4078d1b9299cea4f21ba3d87e3f1f Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Fri, 3 Dec 2021 08:14:34 +0100 Subject: [PATCH] detect/frames: implement 'frame' keyword Implement a special sticky buffer to select frames for inspection. This keyword takes an argument to specify the per protocol frame type: alert ... frame: Or it can specify both in the keyword: alert tcp ... frame:. The latter is useful in some cases like http, where "http" applies to both HTTP and HTTP/2. alert http ... frame:http1.request; alert http1 ... frame:request; Examples: tls.pdu smb.smb2.hdr smb.smb3.data Consider a rule like: alert tcp ... flow:to_server; content:"|ff|SMB"; content:"some smb 1 issue"; this will scan all toserver TCP traffic, where it will only be limited by a port, depending on how rules are grouped. With this work we'll be able to do: alert smb ... flow:to_server; frame:smb1.data; content:"some smb 1 issue"; This rule will only inspect the data portion of SMB1 frames. It will not affect any other protocol, and it won't need special patterns to "search" for the SMB1 frame in the raw stream. --- src/Makefile.am | 2 + src/detect-engine-register.c | 3 + src/detect-engine-register.h | 2 + src/detect-frame.c | 188 +++++++++++++++++++++++++++++++++++ src/detect-frame.h | 28 ++++++ 5 files changed, 223 insertions(+) create mode 100644 src/detect-frame.c create mode 100644 src/detect-frame.h diff --git a/src/Makefile.am b/src/Makefile.am index 3ac3f07b06..618821c5de 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -174,6 +174,7 @@ noinst_HEADERS = \ detect-flowvar.h \ detect-fragbits.h \ detect-fragoffset.h \ + detect-frame.h \ detect-ftpbounce.h \ detect-ftpdata.h \ detect-geoip.h \ @@ -760,6 +761,7 @@ libsuricata_c_a_SOURCES = \ detect-flowvar.c \ detect-fragbits.c \ detect-fragoffset.c \ + detect-frame.c \ detect-ftpbounce.c \ detect-ftpdata.c \ detect-geoip.c \ diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index d5ae96de74..2acf6f21e7 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -239,6 +239,7 @@ #include "app-layer-htp.h" #include "app-layer-smtp.h" #include "app-layer-template.h" +#include "detect-frame.h" #include "detect-tls.h" #include "detect-tls-cert-validity.h" #include "detect-tls-version.h" @@ -518,6 +519,8 @@ void SigTableSetup(void) DetectAppLayerEventRegister(); /* end of order dependent regs */ + DetectFrameRegister(); + DetectPcreRegister(); DetectDepthRegister(); DetectNocaseRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index ab77074d44..aea0e60f89 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -105,6 +105,8 @@ enum DetectKeywordId { DETECT_BSIZE, + DETECT_FRAME, + DETECT_AL_TLS_VERSION, DETECT_AL_TLS_SUBJECT, DETECT_AL_TLS_ISSUERDN, diff --git a/src/detect-frame.c b/src/detect-frame.c new file mode 100644 index 0000000000..e50a1d0009 --- /dev/null +++ b/src/detect-frame.c @@ -0,0 +1,188 @@ +/* Copyright (C) 2021 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 + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + */ + +#include "suricata-common.h" +#include "threads.h" +#include "debug.h" +#include "decode.h" +#include "detect.h" + +#include "app-layer-parser.h" + +#include "detect-parse.h" +#include "detect-engine.h" +#include "detect-engine-mpm.h" +#include "detect-engine-prefilter.h" +#include "detect-content.h" +#include "detect-engine-content-inspection.h" +#include "detect-frame.h" + +#include "flow.h" +#include "flow-util.h" +#include "flow-var.h" + +#include "conf.h" +#include "conf-yaml-loader.h" + +#include "util-debug.h" +#include "util-unittest.h" +#include "util-spm.h" +#include "util-print.h" + +static int DetectFrameSetup(DetectEngineCtx *, Signature *, const char *); + +/** + * \brief this function setup the sticky buffer used in the rule + * + * \param de_ctx Pointer to the Detection Engine Context + * \param s Pointer to the Signature to which the current keyword belongs + * \param str Should hold an empty string always + * + * \retval 0 On success + * \retval -1 On failure + */ +static int DetectFrameSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str) +{ + char value[256] = ""; + strlcpy(value, str, sizeof(value)); + + if (!(DetectProtoContainsProto(&s->proto, IPPROTO_TCP))) { + SCLogError(SC_ERR_INVALID_RULE_ARGUMENT, "'frame' keyword only supported for TCP"); + return -1; + } + + int raw_frame_type; + if (AppProtoIsValid(s->alproto)) { + raw_frame_type = AppLayerParserGetFrameIdByName(IPPROTO_TCP, s->alproto, str); + if (raw_frame_type < 0) { + char *dot = strchr(value, '.'); + if (dot != NULL) + *dot++ = '\0'; + const char *val = dot ? dot : value; + const char *proto = dot ? value : NULL; + if (proto != NULL) { + const AppProto keyword_alproto = StringToAppProto(proto); + if (!AppProtoEquals(s->alproto, keyword_alproto)) { + SCLogError(SC_ERR_INVALID_RULE_ARGUMENT, + "frame '%s' mismatch with rule protocol '%s'", str, + AppProtoToString(s->alproto)); + return -1; + } + if (DetectSignatureSetAppProto(s, keyword_alproto) < 0) + return -1; + } + raw_frame_type = AppLayerParserGetFrameIdByName(IPPROTO_TCP, s->alproto, val); + if (raw_frame_type < 0) { + SCLogError(SC_ERR_INVALID_RULE_ARGUMENT, "unknown frame '%s' for protocol '%s'", + val, proto); + return -1; + } + } + } else { + char *dot = strchr(value, '.'); + if (dot != NULL) + *dot++ = '\0'; + const char *val = dot ? dot : value; + const char *proto = dot ? value : NULL; + if (proto == NULL) { + return -1; + } + + AppProto alproto = StringToAppProto(proto); + if (alproto == ALPROTO_UNKNOWN || alproto == ALPROTO_FAILED) { + SCLogError(SC_ERR_INVALID_RULE_ARGUMENT, "unknown app proto '%s' for 'frame'", proto); + return -1; + } + + if (DetectSignatureSetAppProto(s, alproto) < 0) + return -1; + + raw_frame_type = AppLayerParserGetFrameIdByName(IPPROTO_TCP, s->alproto, val); + if (raw_frame_type < 0) { + SCLogError(SC_ERR_INVALID_RULE_ARGUMENT, "unknown frame '%s' for protocol '%s'", val, + proto); + return -1; + } + } + BUG_ON(raw_frame_type >= UINT8_MAX); + + uint8_t frame_type = (uint8_t)raw_frame_type; + /* TODO we can have TS and TC specific frames */ + const int buffer_id = DetectEngineBufferTypeRegisterWithFrameEngines( + de_ctx, str, SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT, s->alproto, frame_type); + if (buffer_id < 0) + return -1; + + if (DetectBufferSetActiveList(s, buffer_id) < 0) + return -1; + + return 0; +} + +#ifdef UNITTESTS + +static int DetectFrameTestBadRules(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + + const char *sigs[] = { + "alert tcp-pkt any any -> any any (frame:tls.pdu; content:\"a\"; sid:1;)", + "alert udp any any -> any any (frame:tls.pdu; content:\"a\"; sid:2;)", + "alert smb any any -> any any (frame:tls.pdu; content:\"a\"; sid:3;)", + "alert tcp any any -> any any (frame:tls; content:\"a\"; sid:4;)", + "alert tls any any -> any any (content:\"abc\"; frame:tls.pdu; content:\"a\"; sid:5;)", + "alert tls any any -> any any (tls.version:1.0; frame:tls.pdu; content:\"a\"; sid:6;)", + "alert tls any any -> any any (frame:smb1.pdu; content:\"a\"; sid:7;)", + NULL, + }; + + const char **sig = sigs; + while (*sig) { + SCLogDebug("sig %s", *sig); + Signature *s = DetectEngineAppendSig(de_ctx, *sig); + FAIL_IF_NOT_NULL(s); + sig++; + } + + DetectEngineCtxFree(de_ctx); + PASS; +} + +static void DetectFrameRegisterTests(void) +{ + UtRegisterTest("DetectFrameTestBadRules", DetectFrameTestBadRules); +} +#endif + +/** + * \brief Registration function for keyword: ja3_hash + */ +void DetectFrameRegister(void) +{ + sigmatch_table[DETECT_FRAME].name = "frame"; + sigmatch_table[DETECT_FRAME].desc = "sticky buffer for inspecting app-layer frames"; + sigmatch_table[DETECT_FRAME].Setup = DetectFrameSetup; + sigmatch_table[DETECT_FRAME].flags = SIGMATCH_INFO_STICKY_BUFFER; +#ifdef UNITTESTS + sigmatch_table[DETECT_FRAME].RegisterTests = DetectFrameRegisterTests; +#endif +} diff --git a/src/detect-frame.h b/src/detect-frame.h new file mode 100644 index 0000000000..ae094624c2 --- /dev/null +++ b/src/detect-frame.h @@ -0,0 +1,28 @@ +/* Copyright (C) 2021 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 + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + */ + +#ifndef __DETECT_FRAME_H__ +#define __DETECT_FRAME_H__ + +/* Prototypes */ +void DetectFrameRegister(void); + +#endif /* __DETECT_FRAME_H__ */ -- 2.47.2