]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect/frames: implement 'frame' keyword
authorVictor Julien <vjulien@oisf.net>
Fri, 3 Dec 2021 07:14:34 +0000 (08:14 +0100)
committerVictor Julien <vjulien@oisf.net>
Tue, 18 Jan 2022 11:21:52 +0000 (12:21 +0100)
Implement a special sticky buffer to select frames for inspection.

This keyword takes an argument to specify the per protocol frame type:

    alert <app proto name> ... frame:<specific frame name>

Or it can specify both in the keyword:

    alert tcp ... frame:<app proto name>.<specific frame name>

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
src/detect-engine-register.c
src/detect-engine-register.h
src/detect-frame.c [new file with mode: 0644]
src/detect-frame.h [new file with mode: 0644]

index 3ac3f07b06bb82289b660ebb603a9d7ba2d654e0..618821c5de53203e3874c671b57cc8b53ad4c3bc 100755 (executable)
@@ -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 \
index d5ae96de74c11d676da8d0ec08335c8d5fdabe06..2acf6f21e773bcd7db4a62a85a65d0591fcb6499 100644 (file)
 #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();
index ab77074d44c1cf4f56df0cbf5e56c36819ecd290..aea0e60f89f1d3c9bfd01834b3758a0c31d6397d 100644 (file)
@@ -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 (file)
index 0000000..e50a1d0
--- /dev/null
@@ -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 (file)
index 0000000..ae09462
--- /dev/null
@@ -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__ */