]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
SNMP: add the "snmp.version" detection keyword
authorPierre Chifflier <chifflier@wzdftpd.net>
Mon, 10 Dec 2018 12:48:00 +0000 (13:48 +0100)
committerPierre Chifflier <chifflier@wzdftpd.net>
Thu, 6 Jun 2019 08:15:59 +0000 (10:15 +0200)
doc/userguide/rules/index.rst
doc/userguide/rules/snmp-keywords.rst [new file with mode: 0644]
rust/src/snmp/detect.rs [new file with mode: 0644]
rust/src/snmp/mod.rs
rust/src/snmp/snmp.rs
src/Makefile.am
src/detect-engine-register.c
src/detect-engine-register.h
src/detect-snmp-version.c [new file with mode: 0644]
src/detect-snmp-version.h [new file with mode: 0644]

index 391d410f5351f61c1b5db63b216c22473bca61b6..c81440ef94cda9e6de763b2244bcdfb957ee63c4 100644 (file)
@@ -22,6 +22,7 @@ Suricata Rules
    enip-keyword
    ftp-keywords
    kerberos-keywords
+   snmp-keywords
    app-layer
    xbits
    thresholding
diff --git a/doc/userguide/rules/snmp-keywords.rst b/doc/userguide/rules/snmp-keywords.rst
new file mode 100644 (file)
index 0000000..4b24658
--- /dev/null
@@ -0,0 +1,22 @@
+SNMP keywords
+=============
+
+snmp.version
+------------
+
+SNMP protocol version (integer). Expected values are 1, 2 (for version 2c) or 3.
+
+Syntax::
+
+ snmp.version:[op]<number>
+
+The version can be matched exactly, or compared using the _op_ setting::
+
+ snmp.version:3    # exactly 3
+ snmp.version:<3   # smaller than 3
+ snmp.version:>=2  # greater or equal than 2
+
+Signature example::
+
+ alert snmp any any -> any any (msg:"old SNMP version (<3)"; snmp.version:<3; sid:1; rev:1;)
+
diff --git a/rust/src/snmp/detect.rs b/rust/src/snmp/detect.rs
new file mode 100644 (file)
index 0000000..da36d58
--- /dev/null
@@ -0,0 +1,32 @@
+/* Copyright (C) 2017-2019 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.
+ */
+
+// written by Pierre Chifflier  <chifflier@wzdftpd.net>
+
+use libc;
+use snmp::snmp::SNMPTransaction;
+
+#[no_mangle]
+pub extern "C" fn rs_snmp_tx_get_version(tx: &mut SNMPTransaction,
+                                         version: *mut libc::uint32_t)
+{
+    debug_assert!(tx.version != 0, "SNMP version is 0");
+    unsafe {
+        *version = tx.version as libc::uint32_t;
+    }
+}
+
index c3ccbbcb392a7468eb06adf8da10dbfdaf0be68d..3bb90ab2f35dbf7b8703f1c1b70d7434f76d27b3 100644 (file)
@@ -21,3 +21,4 @@ extern crate snmp_parser;
 
 pub mod snmp;
 pub mod log;
+pub mod detect;
index b190351742ba56e35518659b1c18404f26d96d77..28508bc6ddf67c314f5710dbe449099f0c71cdb8 100644 (file)
@@ -62,6 +62,9 @@ pub struct SNMPPduInfo {
 }
 
 pub struct SNMPTransaction {
+    /// PDU version
+    pub version: u32,
+
     /// PDU info, if present (and cleartext)
     pub info: Option<SNMPPduInfo>,
 
@@ -202,7 +205,7 @@ impl SNMPState {
 
     fn new_tx(&mut self) -> SNMPTransaction {
         self.tx_id += 1;
-        SNMPTransaction::new(self.tx_id)
+        SNMPTransaction::new(self.version, self.tx_id)
     }
 
     fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&SNMPTransaction> {
@@ -254,8 +257,9 @@ impl SNMPState {
 }
 
 impl SNMPTransaction {
-    pub fn new(id: u64) -> SNMPTransaction {
+    pub fn new(version: u32, id: u64) -> SNMPTransaction {
         SNMPTransaction {
+            version,
             info: None,
             community: None,
             usm: None,
index c80b71f229f44f74b189717ecab18b3208c4242d..fc0322f7ac980b245300c648686beba74a461311 100644 (file)
@@ -233,6 +233,7 @@ detect-rpc.c detect-rpc.h \
 detect-sameip.c detect-sameip.h \
 detect-seq.c detect-seq.h \
 detect-sid.c detect-sid.h \
+detect-snmp-version.c detect-snmp-version.h \
 detect-ssh-proto.c detect-ssh-proto.h \
 detect-ssh-proto-version.c detect-ssh-proto-version.h \
 detect-ssh-software.c detect-ssh-software.h \
index e104114e69b31e9938d926e83dfaebb17ebd9b5d..d64f94275c54f10e92aa214814f0e0f48a5ebafe 100644 (file)
 #include "detect-krb5-sname.h"
 #include "detect-target.h"
 #include "detect-template-rust-buffer.h"
+#include "detect-snmp-version.h"
 #include "detect-template-buffer.h"
 #include "detect-bypass.h"
 #include "detect-ftpdata.h"
@@ -526,6 +527,7 @@ void SigTableSetup(void)
     DetectKrb5SNameRegister();
     DetectTargetRegister();
     DetectTemplateRustBufferRegister();
+    DetectSNMPVersionRegister();
     DetectTemplateBufferRegister();
     DetectBypassRegister();
 
index bca55bfbe6a0db4c0b43ea85ebfea0d27cd4771f..44a698045b63615452026e24f6d296901155332e 100644 (file)
@@ -228,6 +228,7 @@ enum {
     DETECT_FTPDATA,
     DETECT_TARGET,
     DETECT_AL_TEMPLATE_RUST_BUFFER,
+    DETECT_AL_SNMP_VERSION,
     DETECT_AL_TEMPLATE_BUFFER,
 
     DETECT_BYPASS,
diff --git a/src/detect-snmp-version.c b/src/detect-snmp-version.c
new file mode 100644 (file)
index 0000000..6972ca8
--- /dev/null
@@ -0,0 +1,364 @@
+/* Copyright (C) 2015-2019 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
+ *
+ * \author Pierre Chifflier <chifflier@wzdftpd.net>
+ */
+
+#include "suricata-common.h"
+#include "conf.h"
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-content-inspection.h"
+#include "detect-snmp-version.h"
+#include "app-layer-parser.h"
+
+#ifndef HAVE_RUST
+
+void DetectSNMPVersionRegister(void)
+{
+}
+
+#else
+
+#include "rust-snmp-snmp-gen.h"
+#include "rust-snmp-detect-gen.h"
+
+/**
+ *   [snmp_version]:[<|>|<=|>=]<version>;
+ */
+#define PARSE_REGEX "^\\s*(<=|>=|<|>)?\\s*([0-9]+)\\s*$"
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+enum DetectSNMPVersionMode {
+    PROCEDURE_EQ = 1, /* equal */
+    PROCEDURE_LT, /* less than */
+    PROCEDURE_LE, /* less than */
+    PROCEDURE_GT, /* greater than */
+    PROCEDURE_GE, /* greater than */
+};
+
+typedef struct DetectSNMPVersionData_ {
+    uint32_t version;
+    enum DetectSNMPVersionMode mode;
+} DetectSNMPVersionData;
+
+static DetectSNMPVersionData *DetectSNMPVersionParse (const char *);
+static int DetectSNMPVersionSetup (DetectEngineCtx *, Signature *s, const char *str);
+static void DetectSNMPVersionFree(void *);
+static void DetectSNMPVersionRegisterTests(void);
+static int g_snmp_version_buffer_id = 0;
+
+static int DetectEngineInspectSNMPRequestGeneric(ThreadVars *tv,
+        DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+        const Signature *s, const SigMatchData *smd,
+        Flow *f, uint8_t flags, void *alstate,
+        void *txv, uint64_t tx_id);
+
+static int DetectSNMPVersionMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *,
+                                   uint8_t, void *, void *, const Signature *,
+                                   const SigMatchCtx *);
+
+/**
+ * \brief Registration function for snmp_procedure keyword.
+ */
+void DetectSNMPVersionRegister (void)
+{
+    sigmatch_table[DETECT_AL_SNMP_VERSION].name = "snmp_version";
+    sigmatch_table[DETECT_AL_SNMP_VERSION].desc = "match SNMP version";
+    sigmatch_table[DETECT_AL_SNMP_VERSION].url = DOC_URL DOC_VERSION "/rules/snmp-keywords.html#snmp_version";
+    sigmatch_table[DETECT_AL_SNMP_VERSION].Match = NULL;
+    sigmatch_table[DETECT_AL_SNMP_VERSION].AppLayerTxMatch = DetectSNMPVersionMatch;
+    sigmatch_table[DETECT_AL_SNMP_VERSION].Setup = DetectSNMPVersionSetup;
+    sigmatch_table[DETECT_AL_SNMP_VERSION].Free = DetectSNMPVersionFree;
+    sigmatch_table[DETECT_AL_SNMP_VERSION].RegisterTests = DetectSNMPVersionRegisterTests;
+
+
+    DetectSetupParseRegexes(PARSE_REGEX, &parse_regex, &parse_regex_study);
+
+    DetectAppLayerInspectEngineRegister("snmp_version",
+            ALPROTO_SNMP, SIG_FLAG_TOSERVER, 0,
+            DetectEngineInspectSNMPRequestGeneric);
+
+    DetectAppLayerInspectEngineRegister("snmp_version",
+            ALPROTO_SNMP, SIG_FLAG_TOCLIENT, 0,
+            DetectEngineInspectSNMPRequestGeneric);
+
+    g_snmp_version_buffer_id = DetectBufferTypeGetByName("snmp_version");
+
+    SCLogDebug("g_snmp_version_buffer_id %d", g_snmp_version_buffer_id);
+}
+
+static int DetectEngineInspectSNMPRequestGeneric(ThreadVars *tv,
+        DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+        const Signature *s, const SigMatchData *smd,
+        Flow *f, uint8_t flags, void *alstate,
+        void *txv, uint64_t tx_id)
+{
+    return DetectEngineInspectGenericList(tv, de_ctx, det_ctx, s, smd,
+                                          f, flags, alstate, txv, tx_id);
+}
+
+static inline int
+VersionMatch(const uint32_t version,
+        enum DetectSNMPVersionMode mode, uint32_t ref_version)
+{
+    switch (mode) {
+        case PROCEDURE_EQ:
+            if (version == ref_version)
+                SCReturnInt(1);
+            break;
+        case PROCEDURE_LT:
+            if (version < ref_version)
+                SCReturnInt(1);
+            break;
+        case PROCEDURE_LE:
+            if (version <= ref_version)
+                SCReturnInt(1);
+            break;
+        case PROCEDURE_GT:
+            if (version > ref_version)
+                SCReturnInt(1);
+            break;
+        case PROCEDURE_GE:
+            if (version >= ref_version)
+                SCReturnInt(1);
+            break;
+    }
+    SCReturnInt(0);
+}
+
+/**
+ * \internal
+ * \brief Function to match version of a TX
+ *
+ * \param t       Pointer to thread vars.
+ * \param det_ctx Pointer to the pattern matcher thread.
+ * \param f       Pointer to the current flow.
+ * \param flags   Flags.
+ * \param state   App layer state.
+ * \param s       Pointer to the Signature.
+ * \param m       Pointer to the sigmatch that we will cast into
+ *                DetectSNMPVersionData.
+ *
+ * \retval 0 no match.
+ * \retval 1 match.
+ */
+static int DetectSNMPVersionMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+                                   Flow *f, uint8_t flags, void *state,
+                                   void *txv, const Signature *s,
+                                   const SigMatchCtx *ctx)
+{
+    SCEnter();
+
+    const DetectSNMPVersionData *dd = (const DetectSNMPVersionData *)ctx;
+    uint32_t version;
+    rs_snmp_tx_get_version(txv, &version);
+    SCLogDebug("version %u mode %u ref_version %d",
+            version, dd->mode, dd->version);
+    if (VersionMatch(version, dd->mode, dd->version))
+        SCReturnInt(1);
+    SCReturnInt(0);
+}
+
+/**
+ * \internal
+ * \brief Function to parse options passed via snmp_version keywords.
+ *
+ * \param rawstr Pointer to the user provided options.
+ *
+ * \retval dd pointer to DetectSNMPVersionData on success.
+ * \retval NULL on failure.
+ */
+static DetectSNMPVersionData *DetectSNMPVersionParse (const char *rawstr)
+{
+    DetectSNMPVersionData *dd = NULL;
+#define MAX_SUBSTRINGS 30
+    int ret = 0, res = 0;
+    int ov[MAX_SUBSTRINGS];
+    char mode[2] = "";
+    char value1[20] = "";
+    char *endptr = NULL;
+
+    ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr), 0,
+                    0, ov, MAX_SUBSTRINGS);
+    if (ret < 3 || ret > 5) {
+        SCLogError(SC_ERR_PCRE_MATCH, "Parse error %s", rawstr);
+        goto error;
+    }
+
+    res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 1, mode,
+                              sizeof(mode));
+    if (res < 0) {
+        SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+        goto error;
+    }
+
+    res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 2, value1,
+                              sizeof(value1));
+    if (res < 0) {
+        SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+        goto error;
+    }
+
+    dd = SCCalloc(1, sizeof(DetectSNMPVersionData));
+    if (unlikely(dd == NULL))
+        goto error;
+
+    if (strlen(mode) == 1) {
+        if (mode[0] == '<')
+            dd->mode = PROCEDURE_LT;
+        else if (mode[0] == '>')
+            dd->mode = PROCEDURE_GT;
+    } else if (strlen(mode) == 2) {
+        if (strcmp(mode, "<=") == 0)
+            dd->mode = PROCEDURE_LE;
+        if (strcmp(mode, ">=") == 0)
+            dd->mode = PROCEDURE_GE;
+    }
+
+    if (dd->mode == 0) {
+        dd->mode = PROCEDURE_EQ;
+    }
+
+    /* set the first value */
+    dd->version = strtoul(value1, &endptr, 10);
+    if (endptr == NULL || *endptr != '\0') {
+        SCLogError(SC_ERR_INVALID_SIGNATURE, "invalid character as arg "
+                   "to snmp_version keyword");
+        goto error;
+    }
+
+    return dd;
+
+error:
+    if (dd)
+        SCFree(dd);
+    return NULL;
+}
+
+
+
+/**
+ * \brief Function to add the parsed snmp version field into the current signature.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ * \param s      Pointer to the Current Signature.
+ * \param rawstr Pointer to the user provided flags options.
+ * \param type   Defines if this is notBefore or notAfter.
+ *
+ * \retval 0 on Success.
+ * \retval -1 on Failure.
+ */
+static int DetectSNMPVersionSetup (DetectEngineCtx *de_ctx, Signature *s,
+                                   const char *rawstr)
+{
+    DetectSNMPVersionData *dd = NULL;
+    SigMatch *sm = NULL;
+
+    if (DetectSignatureSetAppProto(s, ALPROTO_SNMP) != 0)
+        return -1;
+
+    dd = DetectSNMPVersionParse(rawstr);
+    if (dd == NULL) {
+        SCLogError(SC_ERR_INVALID_ARGUMENT,"Parsing \'%s\' failed", rawstr);
+        goto error;
+    }
+
+    /* okay so far so good, lets get this into a SigMatch
+     * and put it in the Signature. */
+    sm = SigMatchAlloc();
+    if (sm == NULL)
+        goto error;
+
+    sm->type = DETECT_AL_SNMP_VERSION;
+    sm->ctx = (void *)dd;
+
+    SCLogDebug("snmp_version %d", dd->version);
+    SigMatchAppendSMToList(s, sm, g_snmp_version_buffer_id);
+    return 0;
+
+error:
+    DetectSNMPVersionFree(dd);
+    return -1;
+}
+
+/**
+ * \internal
+ * \brief Function to free memory associated with DetectSNMPVersionData.
+ *
+ * \param de_ptr Pointer to DetectSNMPVersionData.
+ */
+static void DetectSNMPVersionFree(void *ptr)
+{
+    SCFree(ptr);
+}
+
+
+#ifdef UNITTESTS
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+/**
+ * \test This is a test for a valid value 2.
+ *
+ * \retval 1 on success.
+ * \retval 0 on failure.
+ */
+static int SNMPValidityTestParse01 (void)
+{
+    DetectSNMPVersionData *dd = NULL;
+    dd = DetectSNMPVersionParse("2");
+    FAIL_IF_NULL(dd);
+    FAIL_IF_NOT(dd->version == 2 && dd->mode == PROCEDURE_EQ);
+    DetectSNMPVersionFree(dd);
+    PASS;
+}
+
+/**
+ * \test This is a test for a valid value >2.
+ *
+ * \retval 1 on success.
+ * \retval 0 on failure.
+ */
+static int SNMPValidityTestParse02 (void)
+{
+    DetectSNMPVersionData *dd = NULL;
+    dd = DetectSNMPVersionParse(">2");
+    FAIL_IF_NULL(dd);
+    FAIL_IF_NOT(dd->version == 2 && dd->mode == PROCEDURE_GT);
+    DetectSNMPVersionFree(dd);
+    PASS;
+}
+
+
+#endif
+
+static void DetectSNMPVersionRegisterTests(void)
+{
+#ifdef UNITTESTS
+    UtRegisterTest("SNMPValidityTestParse01", SNMPValidityTestParse01);
+    UtRegisterTest("SNMPValidityTestParse02", SNMPValidityTestParse02);
+#endif /* UNITTESTS */
+}
+
+#endif
diff --git a/src/detect-snmp-version.h b/src/detect-snmp-version.h
new file mode 100644 (file)
index 0000000..281ae51
--- /dev/null
@@ -0,0 +1,31 @@
+/* Copyright (C) 2015-2019 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
+ *
+ * \author Pierre Chifflier <chifflier@wzdftpd.net>
+ */
+
+#ifndef __DETECT_SNMP_VERSION_H__
+#define __DETECT_SNMP_VERSION_H__
+
+#include "app-layer-snmp.h"
+
+void DetectSNMPVersionRegister(void);
+
+#endif /* __DETECT_SNMP_VERSION_H__ */